Documentation
¶
Overview ¶
Package ext is the frozen public SDK boundary between the AList core (the "host") and its extensions — storage drivers today, and (reserved) the other extension Kinds tomorrow.
STABILITY: v0.x — UNSTABLE. Per docs/v4-plugin-sdk-9.0-resolution.md §0.3 the contract is intentionally NOT frozen until v4 GA: it must first be exercised by the Step 10 install-wizard and the gRPC host (internal/extension/grpc) consumers and validated against representative driver parity. Until then, breaking changes may land on minor version bumps. Do not treat any type here as stable.
BOUNDARY RULE: this module MUST NOT import anything from the core's internal/ tree. It is a standalone Go module (its own go.mod; the root module pulls it in through a replace directive) precisely so that out-of-tree extensions can depend on the SDK without dragging in the whole core. Keep the dependency set to the Go standard library.
Index ¶
- Constants
- Variables
- func RefKey(ref ObjRef) string
- type APIRoute
- type AccessProtocol
- type Action
- type AddArgs
- type ArchiveDecompress
- type ArchiveGetter
- type ArchiveMeta
- type ArchiveReader
- type AssistForm
- type AssistRef
- type Auth
- type AuthKind
- type AuthMethodInfo
- type AuthNChallenge
- type AuthNMethod
- type AuthNRequest
- type AuthNResponse
- type AuthNResult
- type AuthProvider
- type AuthURLRequest
- type BackgroundService
- type BundleSpec
- type ByteSource
- type Cache
- type Capability
- type ConfigSection
- type Contributions
- type Copier
- type DashboardCard
- type DependsOn
- type DriverConfig
- type ErrCode
- type Error
- type Event
- type EventHook
- type EventType
- type ExchangeRequest
- type Extension
- type FS
- type FeatureBundle
- type FileColumn
- type FileDecorator
- type GetRooter
- type Getter
- type HTTPClient
- type HTTPRequest
- type HTTPResponse
- type Host
- type I18nBundle
- type IndexProgress
- type Info
- type Item
- type JSONSchema
- type KV
- type Kind
- type Link
- type LinkArgs
- type ListArgs
- type Logger
- type LoginMethod
- type Manifest
- type MenuAction
- type Mkdirer
- type Mover
- type Notice
- type NoticeLevel
- type Notification
- type Obj
- type ObjRef
- type OfflineDownloadTool
- type OfflineDownloadToolConfig
- type Other
- type Permissions
- type PreviewerReg
- type Principal
- type ProtocolInfo
- type Provenance
- type ProvisionHint
- type PublicPage
- type PutURLer
- type Putter
- type Quota
- type Quotaer
- type Range
- type RangeOpener
- type RapidPutter
- type RemoteDownloader
- type RemoteTask
- type RemoteTaskStatus
- type Remover
- type Renamer
- type RouteDecl
- type ScheduledJob
- type SearchIndexer
- type SearchIndexerConfig
- type SearchNode
- type SearchReq
- type ServiceStatus
- type SettingsPage
- type SignScope
- type Signer
- type StatusProvider
- type StorageDriver
- type Store
- type Table
- type TableRow
- type Theme
- type ThemeFont
- type Token
- type ToolStatus
- type Transport
- type UIPanel
- type UploadInfo
- type UploadStream
- type Wizard
- type WizardOption
- type WizardStep
Constants ¶
const ( ServiceStateRunning = "running" ServiceStateStopped = "stopped" ServiceStateError = "error" )
Service lifecycle state constants for ServiceStatus.State.
const ( WizardInfo = "info" WizardPermissionConsent = "permission-consent" WizardForm = "form" WizardOAuth = "oauth" WizardChoice = "choice" WizardTest = "test" WizardRisk = "risk" WizardDone = "done" )
WizardStep kinds. The host's engine understands each kind; oauth is a seam (embedded auth broker, Step 2b) and risk is reserved for Step 11's risk interstitial — it renders whatever the manifest declares but carries no enforcement yet.
Variables ¶
var ErrNeedAppPassword = NewError(ErrCodePermissionDenied, "account requires an app password for protocol login")
ErrNeedAppPassword is returned by Auth.Login for an account with a second factor enabled: a protocol client cannot enter a TOTP code, so it must use an app password instead of the raw account password (design §2.1).
var ErrPermissionDenied = NewError(ErrCodePermissionDenied, "permission denied")
ErrPermissionDenied is the sentinel a Host capability returns when the plugin's Grant does not authorize the call (design §6.3): an ungranted capability, a network host outside Grant.Network, or an FS/Sign target the plugin's principal cannot reach. It carries ErrCodePermissionDenied so it maps back to errs.PermissionDenied across the boundary.
Functions ¶
func RefKey ¶
RefKey is the canonical serialization of an ObjRef used as the map key in a FileDecorator result and in the fs/fields backfill response. It is the shared contract both the host and a decorator key by. A borrowed-fs ref (empty StorageID, virtual path in Anchor) keys by its path directly; a storage-anchored ref prefixes the storage id so the two ref worlds never collide.
Types ¶
type APIRoute ¶
type APIRoute interface {
Extension
// Routes declares the sub-routes this plugin serves. The host validates and
// mounts each; an empty/invalid prefix or one that escapes the plugin
// namespace is rejected.
Routes() []RouteDecl
// Handle serves one proxied request. req.URL carries the sub-path BELOW the
// plugin's mount (the host strips the /api/v4/ext/{id} prefix). The response
// is written back to the client verbatim. Handle must not assume it can reach
// the host beyond its granted capabilities.
Handle(ctx context.Context, req *HTTPRequest) (*HTTPResponse, error)
}
APIRoute lets a plugin mount HTTP sub-routes the host proxies to it: a webhook receiver, a share-management API, a public share landing page (design §2.5 FeatureBundle = APIRoute + PublicPage + …). The plugin declares its routes and one Handle entry point; the host owns the actual mounting, the reserved-prefix isolation, and the authentication boundary.
Two boundaries the host enforces around this interface:
- Namespacing / reserved words: a plugin's routes mount under the plugin's own namespace (/api/v4/ext/{plugin-id}/…), so they can never collide with a core route. A route that instead asks for a top-level short prefix (a share short-link) is checked against the host's reserved-prefix table (CheckReservedPrefix) and refused on conflict — at install/registration.
- Authentication: a RouteDecl with Public=false mounts behind the auth middleware (only logged-in callers reach Handle). Public=true mounts under the unauthenticated public group AND is gated on the "public_routes" grant; its Handle runs with a delegated-only Host (design §2.5), so a public route can borrow the fs only through an owner-delegated principal, never the plugin's own grant.
type AccessProtocol ¶
type AccessProtocol interface {
Extension
// ProtocolInfo describes how the host binds this protocol: its scheme, its
// transport, the default port, and — for TransportHTTP — where the host mounts
// the handler.
ProtocolInfo() ProtocolInfo
// ServeHTTP handles ONE inbound HTTP request for a TransportHTTP protocol
// (WebDAV/S3/MCP). The host proxies the client's request here after mounting
// the protocol at ProtocolInfo.MountPattern; the plugin authenticates it via
// host.Auth() and performs every fs operation via host.FS(). The request and
// response are the serializable HTTPRequest/HTTPResponse (the frozen wire
// contract, same as APIRoute) so the same handler works in-process or over
// gRPC. Only meaningful for TransportHTTP; a TransportConn plugin returns
// ErrCodeNotImplement.
ServeHTTP(ctx context.Context, req *HTTPRequest) (*HTTPResponse, error)
// HandleConn services ONE accepted raw connection for a TransportConn protocol
// (FTP/SFTP). The host owns the listener (port/TLS from settings) and hands the
// plugin a live net.Conn; the plugin drives the wire protocol and, per command,
// authenticates via host.Auth() and acts via host.FS(). HandleConn must return
// when the connection closes or ctx is cancelled (host shutdown).
//
// NATIVE ONLY: a raw net.Conn is not serializable, so a TransportConn plugin
// runs in-process (or as a trusted official binary), never behind the gRPC
// bridge. The trust requirement is the point — a plugin holding a bare socket
// is outside the capability sandbox for that connection's bytes, so only signed
// official / built-in code provides one (design §5, §2.1 decision 4). Only
// meaningful for TransportConn; a TransportHTTP plugin returns
// ErrCodeNotImplement.
HandleConn(ctx context.Context, conn net.Conn) error
}
AccessProtocol exposes the AList filesystem OVER an inbound network protocol (design v4-plugin-extensions-design.md §2.1). It is the common floor beneath WebDAV / S3 / MCP (served over HTTP) and FTP / SFTP (raw byte streams). It is orthogonal to StorageDriver: a StorageDriver connects OUT to some cloud, an AccessProtocol serves the AList tree IN to a client.
The one iron security rule (design §0, §2.1): a protocol plugin NEVER makes an authorization decision. It only turns an inbound credential into a host-issued Principal via host.Auth().Login / VerifyToken / VerifyAppPassword, then does every filesystem operation through host.FS() with that Principal — and host.FS runs the core Authorize on every single call. So the v3 defects this fixes (WebDAV's per-operation re-check, S3 server auth) are satisfied structurally: the decision lives in the host, not in the plugin. A protocol plugin that tried to bypass Auth would only ever hold the empty Principal, which is its own narrow bound grant — it cannot mint a session or impersonate a user.
Because it owns sockets and speaks wire protocols it is native/official tier only (design §5): it is never sandboxed to WASM, and the raw-socket transport (FTP/SFTP) does not cross the gRPC boundary at all (see Transport).
type Action ¶
type Action interface {
Extension
// Invoke runs the action named actionID for principal, with opaque args from
// the client. principal is the host-issued identity handle of the acting user
// (design §3) — the empty Principal means the plugin's own bound identity.
// The returned value is serialized back to the client.
Invoke(ctx context.Context, principal Principal, actionID string, args map[string]any) (any, error)
}
Action is a plugin operation the user triggers from the UI: a file/dir context-menu action or a global action (design §2, contributions MenuAction). The plugin declares the actions it offers as Contributions.MenuActions (id + label + target); this interface is how the host invokes the one the user picked.
The host executes an action only AFTER authorizing the caller (the /api/v4/extensions/{id}/actions/{aid} endpoint is admin-gated; per-action permission scopes are a later refinement). The action does its filesystem work through the Grant-gated Host.FS keyed off principal, so an action can never reach outside what the plugin was granted, and the core Authorize still runs on every borrowed operation.
type AddArgs ¶
type AddArgs struct {
URL string `json:"url"`
TempDir string `json:"temp_dir"`
UID string `json:"uid"`
}
AddArgs parameterizes one AddURL call: the source URL, the host-provided temp directory the daemon should download into, and the id of the user the download runs on behalf of.
type ArchiveDecompress ¶
type ArchiveGetter ¶
type ArchiveMeta ¶
type ArchiveMeta struct {
Comment string `json:"comment,omitempty"`
Encrypted bool `json:"encrypted"`
}
ArchiveMeta is the serializable meta-info of an archive (9.0 §6). The tree structure, when present, is carried out-of-band by the host.
type ArchiveReader ¶
type ArchiveReader interface {
GetArchiveMeta(ctx context.Context, obj ObjRef, password string) (ArchiveMeta, error)
ListArchive(ctx context.Context, obj ObjRef, innerPath, password string) ([]Obj, error)
Extract(ctx context.Context, obj ObjRef, innerPath, password string) (*Link, error)
}
ArchiveReader is a driver's native archive handling (115/云盘). Distinct from the ArchiveTool Kind (9.0 §6). Extract returns a Link (§1 contract).
type AssistForm ¶
type AssistForm struct {
ID string `json:"id"`
Title map[string]string `json:"title"`
Schema *JSONSchema `json:"schema"`
TestProbe string `json:"test_probe,omitempty"`
}
AssistForm is an in-place completion sub-form referenced by a field's x-assist (SDK §4.1). Submitting it makes the frontend POST /api/v4/extensions/{id}/assist/{form-id}; the returned value refills the originating field.
type AssistRef ¶
type AssistRef struct {
Form string `json:"form"`
Permission string `json:"permission,omitempty"`
Label map[string]string `json:"label,omitempty"`
}
AssistRef points a field at an AssistForm for in-place completion (SDK §4.1).
type Auth ¶
type Auth interface {
// Login resolves a username/password to a Principal. The host applies
// login rate-limiting here. For a 2FA-enabled account it returns
// ErrNeedAppPassword, forcing protocol clients onto an app password rather
// than letting a protocol login bypass the second factor.
Login(ctx context.Context, username, password string) (Principal, error)
// VerifyToken resolves a bearer/session token to a Principal.
VerifyToken(ctx context.Context, token string) (Principal, error)
// VerifyAppPassword resolves a per-client app password to a Principal.
VerifyAppPassword(ctx context.Context, username, appPassword string) (Principal, error)
}
Auth lets a protocol plugin turn an inbound credential into a Principal (the host runs rate-limiting / brute-force protection at this choke point) and verify tokens. Token ISSUANCE always stays in the core (design §7 decision 3): a plugin only proves "this credential is valid", the host decides what to mint.
type AuthKind ¶
type AuthKind uint8
AuthKind classifies where in the login flow a method participates.
const ( // AuthPrimary replaces the password as the initial authenticator (LDAP, SSO // acting as the sole factor). The plugin is the trusted authenticator; per // design §5/§6.4 a primary method is native + signed + audited only, and its // consent screen is marked "can assert login identity (account-takeover // capability)". AuthPrimary AuthKind = iota // AuthSecondFactor is a step-up factor run AFTER a password succeeds (TOTP, // WebAuthn). Its Begin receives the already-identified Principal; it proves // possession, it does not resolve identity. AuthSecondFactor // AuthFederated is an external IdP (OIDC) that may auto-provision a new user // — but only behind the host's default-off policy gate, and never as an // admin (design §2.2, §6.1). AuthFederated )
type AuthMethodInfo ¶
type AuthMethodInfo struct {
// ID identifies the method within its extension. A single extension may
// expose more than one method (e.g. an SSO plugin offering two IdPs), so the
// host addresses a method by (extensionID, ID).
ID string `json:"id"`
Kind AuthKind `json:"kind"`
// Display is the i18n label the login button / step-up prompt renders.
Display map[string]string `json:"display,omitempty"`
}
AuthMethodInfo is the method's self-description.
type AuthNChallenge ¶
type AuthNChallenge struct {
// ChallengeID correlates this Begin with the eventual Finish. The plugin may
// set it to carry its own handshake state; if left empty the host mints one.
ChallengeID string `json:"challenge_id,omitempty"`
// RedirectURL, when set, is where the host sends the browser (federated /
// OIDC authorization URL).
RedirectURL string `json:"redirect_url,omitempty"`
// Data is an opaque, method-specific payload (WebAuthn creation options, a
// TOTP provisioning URI) the client-side component consumes, carried as JSON
// bytes.
Data []byte `json:"data,omitempty"`
}
AuthNChallenge is what the host relays back to the client to continue (or complete) the handshake.
type AuthNMethod ¶
type AuthNMethod interface {
Extension
// MethodInfo reports how the host should classify and present this method.
// It is the authoritative source for AuthKind: the login-flow orchestrator
// consults it to route (primary/federated on the login page; second_factor
// only after a password succeeds) and to refuse using a second_factor method
// as a standalone login.
MethodInfo() AuthMethodInfo
// Begin starts a challenge: a WebAuthn options blob, an OIDC redirect URL, an
// LDAP bind prompt, a TOTP setup URI. principal is empty for a fresh login
// (primary/federated) and non-empty for step-up (a second factor on an
// already-identified user); it is an opaque host-issued handle the plugin
// cannot forge or introspect (design §3).
Begin(ctx context.Context, principal Principal, req AuthNRequest) (AuthNChallenge, error)
// Finish verifies the client's response and returns the resolved identity.
// Returning Verified=false (with a nil error) is the normal "credentials
// rejected" outcome; a non-nil error is reserved for operational failures
// (IdP unreachable, malformed response). Finish must never be treated by the
// host as authorization — only as an identity assertion the host then
// independently gates.
Finish(ctx context.Context, req AuthNResponse) (AuthNResult, error)
}
AuthNMethod adds a way to authenticate a user beyond the core's built-in username/password path (design §2.2). It is the extension point behind SSO / OIDC, LDAP, TOTP and WebAuthn.
The security contract is the whole point of this interface: the plugin only PROVES an identity — it returns "this identity was verified, here is the username/provision hint" — and the HOST alone decides whether to mint a session token, for whom, and with what privileges (design §0, §2.2). A plugin can never sign a token itself, and the host never unconditionally trusts a returned username: federated auto-provisioning is gated by an explicit, default-off policy, and no AuthNMethod path may mint an admin session.
AuthNMethod is deliberately DISTINCT from AuthProvider (see manifest.go's Kind comments): AuthProvider brokers a storage vendor's OAuth token exchange (it produces a storage *credential*); AuthNMethod authenticates an AList *user* (it produces, at most, a host-issued session). They share no code path.
type AuthNRequest ¶
type AuthNRequest struct {
// RedirectURI is the host's own callback URL (points back at AList), for
// federated flows whose IdP redirects the browser back. Mirrors
// AuthURLRequest.RedirectURI.
RedirectURI string `json:"redirect_uri,omitempty"`
// State is an opaque, host-generated anti-CSRF token the plugin round-trips
// unmodified through the external handshake (RFC 6749 §10.12); the plugin
// never needs to interpret it.
State string `json:"state,omitempty"`
// Params carries method-specific client inputs the user supplied on the
// login page (e.g. a typed LDAP username) as an opaque string map. Bytes and
// credentials that should not be logged belong here, not in dedicated fields.
Params map[string]string `json:"params,omitempty"`
}
AuthNRequest carries the host-supplied inputs to Begin.
type AuthNResponse ¶
type AuthNResponse struct {
// ChallengeID echoes the challenge this response answers.
ChallengeID string `json:"challenge_id,omitempty"`
// Params carries the client's response fields (OIDC code+state, a TOTP code,
// an LDAP password, a WebAuthn assertion) as an opaque map.
Params map[string]string `json:"params,omitempty"`
// Data is an opaque, method-specific response payload as JSON bytes.
Data []byte `json:"data,omitempty"`
}
AuthNResponse carries the client's reply to a challenge back into Finish.
type AuthNResult ¶
type AuthNResult struct {
// Username is the resolved AList user the plugin asserts. For a primary
// method the host requires this user to already exist; for a federated
// method a non-existent user triggers the (default-off) auto-provision path.
Username string `json:"username,omitempty"`
// Verified is the plugin's assertion that the credential checked out. A
// false value (with nil error) is a clean rejection; the host mints nothing.
Verified bool `json:"verified"`
// AutoProvision, when non-nil on a federated result, carries attributes for
// creating/updating the user — honored ONLY when the host's auto-provision
// policy is enabled, and never to grant admin.
AutoProvision *ProvisionHint `json:"auto_provision,omitempty"`
}
AuthNResult is the identity assertion Finish returns. It is an assertion, not an authorization: the host independently gates on it (existing/disabled user, admin cap, federated auto-provision policy) before any token is minted.
type AuthProvider ¶
type AuthProvider interface {
Extension
// AuthURL builds the vendor's authorization-page URL that the admin's
// browser should be sent to. req.State is an opaque, host-generated
// anti-CSRF token; the provider's only obligation is to make the vendor
// echo it back unmodified as a query parameter on redirect (RFC 6749 §10.12)
// — it never needs to interpret it.
AuthURL(ctx context.Context, req AuthURLRequest) (string, error)
// Exchange trades an authorization code for a token by calling the vendor's
// token endpoint directly from inside the plugin process (using whatever
// client_id/secret/etc the plugin was configured with). It never proxies
// through a third-party broker — the exchange is local to this call.
Exchange(ctx context.Context, req ExchangeRequest) (Token, error)
// Refresh trades a refresh token for a fresh access token. Same locality
// guarantee as Exchange: the vendor's token endpoint is called directly.
Refresh(ctx context.Context, refreshToken string) (Token, error)
}
AuthProvider is a Kind=AuthProvider extension (SDK doc §1, WS-8): it turns an OAuth authorization-code dance into a durable token, with the code<->token exchange happening entirely INSIDE the plugin process — the plugin holds the vendor's client_id/secret and talks to the vendor's token endpoint itself. No external broker sits between the admin's AList instance and the storage vendor; that locality is WS-8's core requirement.
AuthProvider is deliberately NOT embedded in StorageDriver: a plugin that only brokers OAuth for a network drive (or for the install wizard of a different plugin) declares Kind=AuthProvider on its own manifest and Extension value; a plugin that is both a storage driver and its own OAuth broker declares both Kinds and implements both interfaces on the same value.
type AuthURLRequest ¶
type AuthURLRequest struct {
// State is the anti-CSRF token the provider must round-trip unmodified.
State string `json:"state"`
// RedirectURI is the host's own OAuth callback URL (points back at AList,
// not at the provider or any broker). Exchange's RedirectURI must match it
// exactly — most vendors require that.
RedirectURI string `json:"redirect_uri"`
// Scopes are the OAuth scopes requested, as declared by the install
// wizard's oauth step (ext.WizardStep.Scopes).
Scopes []string `json:"scopes,omitempty"`
}
AuthURLRequest carries the inputs AuthURL needs to build the vendor's authorization-page URL.
type BackgroundService ¶
type BackgroundService interface {
Extension
// Run performs the long-running work. It MUST block until ctx is cancelled
// (host shutdown or a Stop) and return promptly when it fires. Returning an
// error (or panicking) is isolated by the host supervisor, which logs it and
// restarts the service after a backoff — one service crashing never affects
// the others or the host.
Run(ctx context.Context) error
// Status reports the service's current state for the frontend status card. It
// must be cheap and non-blocking (it is polled): read cached counters, do not
// perform a live probe. State is "running" | "stopped" | "error"; Detail and
// Metrics are free-form (e.g. an frp tunnel's proxy/traffic counts).
Status(ctx context.Context) (ServiceStatus, error)
}
BackgroundService is a long-running side task the host starts after boot and supervises for the process lifetime (design §2.4): an frp tunnel, a sync engine. Unlike ScheduledJob (which the host ticks on a cadence), a BackgroundService runs continuously — Run blocks until its context is cancelled, and the host restarts it with backoff if it returns early or panics. Its Status() feeds the frontend status card (a StatusProvider contribution polls it via the host).
Everything the service does inside Run — outbound network, fs access — is still borrowed through the injected Host and stays Grant-gated + Authorize- enforced by the F0 layer. A background service with no user context that touches storage does so under the fs:system scope (design §6.1). frp is the canonical case: a native-tier service granted the "network" capability.
type BundleSpec ¶
type BundleSpec struct {
// NeedsStore requests the plugin's namespaced Store (design §2.5: share/labels
// persist their tables). The actual access is still gated by the "store"
// grant at the host boundary.
NeedsStore bool `json:"needs_store,omitempty"`
// NeedsSign requests the Signer (design §2.5: share signs ID-anchored,
// expiring download links; the plugin never sees the key). Gated by the "sign"
// grant.
NeedsSign bool `json:"needs_sign,omitempty"`
}
BundleSpec declares the host resources a FeatureBundle needs provisioned.
type ByteSource ¶
type ByteSource string
ByteSource is the wire-serializable classification of where a Link's bytes come from (9.0 §1). v3 had three forms (URL / RangeReadCloser / MFile), two of them json:"-". They collapse to two: URL, and Stream — where Stream absorbs both RangeReadCloser and MFile (an MFile is just "can read a range from an open file", a special case of Stream).
const ( ByteSourceURL ByteSource = "url" // host pulls the bytes from URL+Header itself ByteSourceStream ByteSource = "stream" // bytes come out of the driver: host calls back RangeOpener.OpenRange )
type Cache ¶
type Cache interface {
Get(key string) (value []byte, ok bool)
Set(key string, value []byte, ttl time.Duration)
Delete(key string)
}
Cache is a per-plugin in-memory cache, namespaced by plugin id.
type Capability ¶
type Capability string
Capability names an optional interface a StorageDriver may implement. It is orthogonal to Kind (9.0 §4). The host validates that a driver's declared Capabilities are a subset of what it actually implements, and — with gRPC (9.2) — dispatches from the declaration because a single proxy struct cannot express "has Move but not Copy" through Go type assertions.
const ( CapMkdir Capability = "Mkdir" CapMove Capability = "Move" CapRename Capability = "Rename" CapCopy Capability = "Copy" CapRemove Capability = "Remove" CapPut Capability = "Put" CapPutURL Capability = "PutURL" // CapPutRapid marks driver.PutRapid (SP-H 秒传: hash-based instant upload, // no bytes transferred on hit). CapPutRapid Capability = "PutRapid" CapGetter Capability = "Getter" CapGetRooter Capability = "GetRooter" CapOther Capability = "Other" CapRangeOpener Capability = "RangeOpener" CapArchiveReader Capability = "ArchiveReader" CapArchiveGetter Capability = "ArchiveGetter" CapArchiveDecompress Capability = "ArchiveDecompress" CapQuota Capability = "Quota" CapRemoteDownload Capability = "RemoteDownload" )
type ConfigSection ¶
type ConfigSection struct {
Parent string `json:"parent"`
Title map[string]string `json:"title"`
SettingKeys []string `json:"setting_keys"`
Order int `json:"order,omitempty"`
}
ConfigSection is a titled group of settings rendered on the Parent feature's config page (design C). An extension whose config extends a base feature declares one with Parent=<feature id>; a base feature declares its own "general" section with Parent=<self id>. Membership in the merged document equals "enabled" — disabling the owning extension removes its section, so the feature page shows only the sections of currently-enabled contributors.
type Contributions ¶
type Contributions struct {
ConfigForm *JSONSchema `json:"config_form,omitempty"`
MenuActions []MenuAction `json:"menu_actions,omitempty"`
Previewers []PreviewerReg `json:"previewers,omitempty"`
UIPanels []UIPanel `json:"ui_panels,omitempty"`
DashboardCards []DashboardCard `json:"dashboard_cards,omitempty"`
SettingsPages []SettingsPage `json:"settings_pages,omitempty"`
Themes []Theme `json:"themes,omitempty"`
I18n []I18nBundle `json:"i18n,omitempty"`
AssistForms []AssistForm `json:"assist_forms,omitempty"`
// StatusProviders render a runtime status card the host polls (design §4):
// AccessProtocol / BackgroundService plugins (running state, port, metrics,
// start/stop toggle).
StatusProviders []StatusProvider `json:"status_providers,omitempty"`
// LoginMethods render login affordances for an AuthNMethod (design §4):
// primary/federated login-page buttons, or a second-factor prompt component.
LoginMethods []LoginMethod `json:"login_methods,omitempty"`
// PublicPages render an extension's unauthenticated landing page (design §4):
// a share bundle's public page (iframe, guest context).
PublicPages []PublicPage `json:"public_pages,omitempty"`
// FileColumns add a badge/column to the file listing (design §4): the labels
// badge. Its data source is the extension's FileDecorator field group.
FileColumns []FileColumn `json:"file_columns,omitempty"`
// ConfigSections render titled setting groups on a feature's config page
// (design C). A base feature declares its own "general" section with
// Parent=self; an extension whose config extends a base feature declares one
// with Parent=<base feature id>. The frontend's FeatureConfigPage(id) collects
// every section (across enabled extensions) whose Parent==id.
ConfigSections []ConfigSection `json:"config_sections,omitempty"`
}
Contributions is the declarative front-end surface an extension adds to the host UI (SDK doc §4). The host aggregates the Contributions of every enabled extension into one versioned document (GET /api/v4/extensions/contributions); the frontend renders them natively. Everything here is data only — no behavior crosses this boundary.
type DashboardCard ¶
type DashboardCard struct {
ID string `json:"id"`
Title map[string]string `json:"title"`
Route string `json:"route,omitempty"`
}
DashboardCard is a card shown on the admin dashboard.
type DependsOn ¶
DependsOn drives conditional show/hide/required of a field on another field's value (SDK §4.1).
type DriverConfig ¶
type DriverConfig struct {
Name string `json:"name"`
LocalSort bool `json:"local_sort"`
OnlyLocal bool `json:"only_local"`
OnlyProxy bool `json:"only_proxy"`
NoCache bool `json:"no_cache"`
NoUpload bool `json:"no_upload"`
DefaultRoot string `json:"default_root"`
CheckStatus bool `json:"check_status"`
Alert string `json:"alert,omitempty"`
NoOverwriteUpload bool `json:"no_overwrite_upload"`
ProxyRangeOption bool `json:"proxy_range_option"`
}
DriverConfig is a storage driver's static configuration, returned by StorageDriver.DriverConfig. It mirrors the fields v3 carried on internal/driver.Config that affect host behavior.
9.1 note: built-in drivers still expose their config through the legacy internal/driver.Config reflection path; DriverConfig is the frozen SDK shape future native (out-of-tree) drivers will use. Seam.
type ErrCode ¶
type ErrCode string
ErrCode is the cross-boundary error code (9.0 §5). v3 drove control flow with sentinel errors (errs.NotImplement/NotSupport trigger op-layer fallbacks). Once gRPC flattens an error to a status string, errors.Is stops working — so the code travels explicitly and the host boundary maps it back to the matching sentinel, keeping op-layer control flow (errors.Is(err, errs.NotImplement)) intact.
const ( ErrCodeNotImplement ErrCode = "NOT_IMPLEMENT" ErrCodeNotSupport ErrCode = "NOT_SUPPORT" ErrCodeObjectNotFound ErrCode = "OBJECT_NOT_FOUND" ErrCodeWrongArchivePassword ErrCode = "WRONG_ARCHIVE_PASSWORD" ErrCodePermissionDenied ErrCode = "PERMISSION_DENIED" ErrCodeStorageNotInit ErrCode = "STORAGE_NOT_INIT" ErrCodeStorageNotFound ErrCode = "STORAGE_NOT_FOUND" )
type Error ¶
Error is an error carrying an ErrCode across the boundary. The host boundary layer maps Code back to the corresponding internal sentinel error.
type Event ¶
type Event struct {
// Type is the event verb (fs.move, fs.remove, ...).
Type EventType `json:"type"`
// UnixMilli is the host wall-clock time the event fired.
UnixMilli int64 `json:"unix_milli"`
// Path is the primary virtual path the operation acted on.
Path string `json:"path"`
// DstPath is the destination for two-path operations (move/rename); empty
// otherwise.
DstPath string `json:"dst_path,omitempty"`
// Actor is the username of the acting principal, when the host knows it
// (empty for background/system-initiated operations).
Actor string `json:"actor,omitempty"`
}
Event is the serializable payload delivered to EventHook.OnEvent. It carries only the operation's shape: never object bytes (a hook that needs content re-reads it through the Grant-gated Host.FS) and never credentials.
type EventHook ¶
type EventHook interface {
Extension
// OnEvent is called once per host event the plugin is subscribed to. Its
// error/panic is isolated by the host. It should return promptly; heavy work
// belongs on a queue the plugin drains, not inline in the host's dispatch.
OnEvent(ctx context.Context, e Event) error
}
EventHook observes host events — primarily filesystem mutations (move, rename, remove, mkdir, upload) — after they succeed (design §2.5: FeatureBundle composes EventHook; F1 "事件/定时"). It is a NOTIFICATION sink, not a veto: the host fires OnEvent AFTER an operation has already committed, so a hook cannot block or undo a user action. This keeps the fs mutation path free of plugin latency/failure in its critical section — a misbehaving hook can only fail itself.
Every OnEvent runs in failure isolation (the host recovers panics and swallows errors, logging them): one hook erroring never affects the originating operation nor the other hooks. A hook that needs to borrow host capabilities (read the moved file, notify an external service) does so through the injected Host, which remains Grant-gated and Authorize-enforced (design §0, §6.3) — the Event payload itself only carries the operation's shape (verb + virtual paths + actor), never object bytes or credentials.
type EventType ¶
type EventType string
EventType names a host event. The fs.* family fires after the matching filesystem mutation commits.
const ( // EventFSMkdir fires after a directory is created (Path = the new dir). EventFSMkdir EventType = "fs.mkdir" // EventFSMove fires after an object is moved (Path = source, DstPath = // destination directory). EventFSMove EventType = "fs.move" // EventFSRename fires after an object is renamed (Path = source, DstPath = // the new name). EventFSRename EventType = "fs.rename" // EventFSRemove fires after an object is removed (Path = the removed object). EventFSRemove EventType = "fs.remove" // EventFSPut fires after an object is uploaded (Path = destination // directory). EventFSPut EventType = "fs.put" )
type ExchangeRequest ¶
ExchangeRequest carries the inputs Exchange needs to trade an authorization code for a token.
type Extension ¶
type Extension interface {
Manifest() Manifest
// Init is called once with the Host callback set the core injects.
Init(ctx context.Context, host Host) error
// Shutdown is called once when the extension is torn down.
Shutdown(ctx context.Context) error
}
Extension is the base interface of every extension. Init/Shutdown are process-level and run once (9.0 §3); per-instance lifecycle for storage drivers is added in StorageDriver.
type FS ¶
type FS interface {
// Resolve maps a virtual path to an ObjRef. It is authorized (read) so a
// plugin cannot probe existence outside its grant. This is the per-request
// hot path for protocol plugins that live in the path world (WebDAV/S3/FTP).
Resolve(ctx context.Context, p Principal, path string) (ObjRef, error)
List(ctx context.Context, p Principal, dir ObjRef, a ListArgs) ([]Obj, error)
Get(ctx context.Context, p Principal, ref ObjRef) (Obj, error)
Link(ctx context.Context, p Principal, ref ObjRef, a LinkArgs) (*Link, error)
Mkdir(ctx context.Context, p Principal, parent ObjRef, name string) (Obj, error)
Move(ctx context.Context, p Principal, src, dstDir ObjRef) error
Rename(ctx context.Context, p Principal, src ObjRef, name string) error
Copy(ctx context.Context, p Principal, src, dstDir ObjRef) error
Remove(ctx context.Context, p Principal, ref ObjRef) error
Put(ctx context.Context, p Principal, dstDir ObjRef, up UploadStream) (Obj, error)
}
FS is the host filesystem borrowed by a plugin. EVERY method runs the core Authorize against the acting principal before touching storage (design §0, §3): borrowing FS never bypasses authorization. The plugin only ever holds opaque ObjRefs (pillar A) — never a mutable virtual-path key it could fabricate to reach outside its grant. Even a fabricated ref is backstopped by the per-call Authorize.
type FeatureBundle ¶
type FeatureBundle interface {
Extension
// Bundle declares the host resources this composite needs. The composed
// extension-point implementations are discovered by interface assertion on the
// bundle value itself (the host checks whether it also implements APIRoute /
// EventHook / ScheduledJob), so this spec carries only the resource requests.
Bundle() BundleSpec
}
FeatureBundle is a COMPOSITE feature (design §2.5): share and labels are not a single extension point but a bundle of several — an APIRoute to manage them, a public landing page (an APIRoute with Public=true), an EventHook to react to fs changes, a ScheduledJob for expiry cleanup — plus host Store/Sign to persist and to sign ID-anchored links. Rather than invent a new mega-interface, a FeatureBundle is a value that ALSO implements the existing extension-point interfaces it contributes; the host EXPANDS it onto the F1 wiring points (RegisterAPIRouteInstance / RegisterEventHookInstance / …) by asserting which interfaces it satisfies, reusing that plumbing rather than duplicating it.
So a share bundle is one Go value implementing Extension + FeatureBundle + APIRoute (+ EventHook + ScheduledJob as needed); Bundle() only declares the host RESOURCES it needs (Store/Sign), which the host provisions and gates through the store / sign grants.
type FileColumn ¶
type FileColumn struct {
ID string `json:"id"`
Header map[string]string `json:"header"`
FieldGroup string `json:"field_group"` // e.g. "_labels"; data source = FileDecorator
}
FileColumn adds a badge/column to the file listing (design §4). Its data is NOT inlined in the base listing; it is registered as an fs/list extension field group (FieldGroup, "_"-prefixed), fetched on demand from the extension's FileDecorator only when the column is actually shown — so a column nobody opens costs nothing, and a slow decorator only slows its own column (design §2.6).
type FileDecorator ¶
type FileDecorator interface {
Extension
// Groups names the field groups this decorator provides (each "_"-prefixed,
// e.g. "_labels"), matching a FileColumn contribution's FieldGroup. The host
// routes a requested group to the decorator(s) that declare it.
Groups() []string
// Decorate returns the field values for one group over a batch of refs. The
// result maps a ref's serialized key (RefKey) to the arbitrary field value the
// column renders (a badge list, a boolean, …). A ref absent from the map simply
// has no value for this group. Decorate must honor ctx: the host cancels it at
// the per-group budget and marks the group pending.
Decorate(ctx context.Context, group string, refs []ObjRef) (map[string]any, error)
}
FileDecorator supplies EXTENSION FIELD GROUPS for the file listing (design §2.6): the data behind a FileColumn contribution — label badges, share markers. It is the backend of the "field groups, fetched on demand" protocol: the base listing (name/size/time/type) always returns, but a decorator's group is computed only when a client actually asks for that column (fs/list?fields=_labels), and each group runs under a per-group time budget so a slow decorator is marked pending and backfilled rather than blocking the listing.
A decorator must be pure-read and fast (typically a single indexed query against its own Store table): the host calls it in parallel across decorators under a ~200ms per-group budget. The host only ever passes refs the requesting user may already see (the caller filters the listing first), so a decorator cannot be used to probe objects outside the user's view.
type HTTPClient ¶
type HTTPClient interface {
Do(ctx context.Context, req *HTTPRequest) (*HTTPResponse, error)
}
HTTPClient is the host-gated outbound HTTP capability. Do rejects any request whose target host is not on the effective allowlist (Grant.Network) before the request leaves the process.
type HTTPRequest ¶
type HTTPRequest struct {
Method string `json:"method"`
URL string `json:"url"`
Header http.Header `json:"header,omitempty"`
Body []byte `json:"body,omitempty"`
}
HTTPRequest is the serializable outbound request a plugin asks the host to make (design §3, §6.3). The host enforces the network allowlist (global outbound policy ∩ the plugin's Grant.Network) before dialing; a host not on the allowlist yields ErrPermissionDenied.
type HTTPResponse ¶
type HTTPResponse struct {
Status int `json:"status"`
Header http.Header `json:"header,omitempty"`
Body []byte `json:"body,omitempty"`
}
HTTPResponse is the serializable response for a HTTPClient.Do call.
type Host ¶
type Host interface {
Logger() Logger
FS() FS
Auth() Auth
Store() Store
Sign() Signer
HTTP() HTTPClient
Cache() Cache
// LoadConfig returns the plugin's persisted config_form value as JSON, or
// nil if none has been set.
LoadConfig(ctx context.Context) ([]byte, error)
// SaveConfig persists the plugin's own config_form value as JSON (cfg must
// be valid JSON; an empty cfg clears it). Mirrors LoadConfig in the write
// direction. A Host built without a config saver wired (e.g. no admin-
// editable config_form) returns an ErrCodeNotImplement error rather than
// silently discarding the write.
SaveConfig(ctx context.Context, cfg []byte) error
// Capabilities returns the names of the Host APIs (host_api vocabulary,
// e.g. "host.fs"/"store"/"sign") this plugin was granted — introspection
// over its own Grant, so a plugin can adapt its behavior to what it was
// actually approved for instead of guessing from its own manifest request.
Capabilities(ctx context.Context) ([]string, error)
}
Host is the set of capabilities the core exposes back to an extension (SDK doc §3, design v4-plugin-extensions-design.md §3). Every capability is gated at the host boundary by the plugin's runtime Grant (§6.3): a plugin that was not granted a capability sees ErrPermissionDenied from the corresponding accessor / its methods.
The capability sub-interfaces (FS/Auth/Store/Sign/HTTP/Cache) and their DTOs live in host.go. F0 wires FS/Store/HTTP end-to-end over the reverse gRPC bridge; Auth/Sign/Cache have host-side implementations (the reverse-bridge leg for those follows in a later wave). An in-process (native) plugin receives a Host that calls the core directly; a subprocess (gRPC) plugin receives a Host whose calls travel back over the plugin→host bridge.
type I18nBundle ¶
I18nBundle points at a locale message bundle the extension ships.
type IndexProgress ¶
type IndexProgress struct {
ObjCount uint64 `json:"obj_count"`
IsDone bool `json:"is_done"`
LastDoneTime *time.Time `json:"last_done_time"`
Error string `json:"error"`
}
IndexProgress is the build-progress snapshot the host persists (as the index_progress setting) and the frontend polls: how many objects have been indexed, whether the build finished, when it last completed, and the last error if any.
type Info ¶
type Info struct {
Common []Item `json:"common"`
Additional []Item `json:"additional"`
Config DriverConfig `json:"config"`
}
Info is the host-facing description of a driver: the config form plus its static config.
type Item ¶
type Item struct {
Name string `json:"name"`
Type string `json:"type"`
Default string `json:"default,omitempty"`
Options string `json:"options,omitempty"`
Required bool `json:"required,omitempty"`
Help string `json:"help,omitempty"`
}
Item is one field of a driver's declarative config form.
type JSONSchema ¶
type JSONSchema struct {
Type string `json:"type,omitempty"` // object|string|number|integer|boolean
Title map[string]string `json:"title,omitempty"`
Properties map[string]*JSONSchema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
Enum []string `json:"enum,omitempty"`
Default string `json:"default,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
// DependsOn / Assist are the SDK §4.1 extension keywords. The host carries
// them verbatim to the frontend (page-zero assist); it does not act on them.
DependsOn *DependsOn `json:"x-depends-on,omitempty"`
Assist *AssistRef `json:"x-assist,omitempty"`
}
JSONSchema is the declarative form/field description shared by config forms, wizard form steps, and assist sub-forms (SDK doc §4). It is a deliberately small subset of JSON Schema: enough for the host's wizard engine to validate answers and for the frontend to render a form. Unknown JSON keys are ignored on decode, so a manifest may carry richer schema the frontend understands without the host needing to model it.
type KV ¶
type KV interface {
Get(ctx context.Context, key string) (value []byte, ok bool, err error)
Set(ctx context.Context, key string, value []byte) error
Delete(ctx context.Context, key string) error
// List returns the keys under prefix (all keys when prefix is "").
List(ctx context.Context, prefix string) ([]string, error)
}
KV is a namespaced key/value store. All keys are implicitly scoped to the calling plugin's id on the host side — the plugin cannot address another plugin's namespace.
type Kind ¶
type Kind string
Kind is an extension point. An extension declares one or more Kinds it implements (SDK doc §1). StorageDriver is the only Kind with a live implementation in 9.1; the rest are reserved so their identifiers are frozen alongside the rest of the contract.
const ( KindStorageDriver Kind = "StorageDriver" // KindAuthProvider brokers a storage vendor's OAuth authorization-code // exchange (ext.AuthProvider): it turns a code into a storage *credential*, // with the client_id/secret exchange happening inside the plugin process. KindAuthProvider Kind = "AuthProvider" // KindAuthNMethod adds a user login method (ext.AuthNMethod): SSO/OIDC, LDAP, // TOTP, WebAuthn. It authenticates an AList *user* and, on success, lets the // host mint a session — the plugin never signs a token. This is a different // axis from KindAuthProvider (storage credential vs user session); the two // share no interface or code path. Login methods are native/official tier // only (design §5): closed-source third parties can never provide one. KindAuthNMethod Kind = "AuthNMethod" KindOfflineDownloadTool Kind = "OfflineDownloadTool" KindArchiveTool Kind = "ArchiveTool" KindSearchIndexer Kind = "SearchIndexer" KindThumbnailer Kind = "Thumbnailer" KindFileTransformer Kind = "FileTransformer" KindMetadataProvider Kind = "MetadataProvider" KindEventHook Kind = "EventHook" KindScheduledJob Kind = "ScheduledJob" KindNotification Kind = "NotificationChannel" KindAction Kind = "Action" KindPreviewer Kind = "Previewer" KindUIPanel Kind = "UIPanel" KindAPIRoute Kind = "APIRoute" // KindAccessProtocol exposes the AList filesystem over an inbound network // protocol (ext.AccessProtocol): WebDAV/S3/MCP (TransportHTTP) or FTP/SFTP // (TransportConn). Orthogonal to StorageDriver — inbound vs outbound. Runs // native/official only (design §5): it owns sockets / speaks wire protocols, // so it never runs in a WASM sandbox. KindAccessProtocol Kind = "AccessProtocol" // KindBackgroundService is a long-running side task started after boot // (ext.BackgroundService): frp tunnel, a sync engine. The host supervises it // (restart/backoff, panic isolation) and polls Status() for the frontend // status card. KindBackgroundService Kind = "BackgroundService" // KindFeatureBundle is a composite feature (ext.FeatureBundle): share/labels. // It contributes several existing extension points at once (APIRoute + // PublicPage + EventHook + ScheduledJob) plus host Store/Sign, which the host // expands onto the F1 wiring points rather than inventing a new one. KindFeatureBundle Kind = "FeatureBundle" // KindFileDecorator supplies extension field groups for the file listing // (ext.FileDecorator): label badges, share markers. The host calls it in // parallel under a per-group time budget; a slow group is marked pending and // backfilled, never blocking the base listing (design §2.6). KindFileDecorator Kind = "FileDecorator" // KindFeature is a plain, host-registered core feature turned into a // manageable built-in extension (Project A-2): offline download, file search, // sharing, SSO login. Unlike FeatureBundle it contributes no interface and is // not expanded onto any wiring point — it is a pure registry entry that gives // an existing, natively-wired core feature a manageable/gate-able/aggregatable // identity. The host registers it enabled at boot (RegisterBuiltinExtension); // an admin may turn it off, and a RequireExtensionEnabled gate on the feature's // entry-point routes turns that off into a real 403 boundary. KindFeature Kind = "Feature" )
type Link ¶
type Link struct {
ByteSource ByteSource `json:"byte_source"`
URL string `json:"url,omitempty"` // ByteSource==url
Header http.Header `json:"header,omitempty"` // ByteSource==url
StreamID string `json:"stream_id,omitempty"` // ByteSource==stream: echoed back on OpenRange to locate the session
RangeSupport bool `json:"range_support"`
Expiration int64 `json:"expiration,omitempty"` // seconds; 0 = do not cache (v3 nil)
IPCacheKey bool `json:"ip_cache_key,omitempty"` // cache key is scoped by client IP (v3 op/fs.go semantics)
Concurrency int `json:"concurrency,omitempty"`
PartSize int `json:"part_size,omitempty"`
}
Link describes how the host obtains a file's bytes (9.0 §1). Unlike v3's model.Link it carries no live Go handles, so it survives serialization.
type LinkArgs ¶
type LinkArgs struct {
IP string `json:"ip"`
Header http.Header `json:"header,omitempty"`
Type string `json:"type,omitempty"`
Redirect bool `json:"redirect"`
}
LinkArgs is the serializable redefinition of v3's LinkArgs (which carried a *http.Request). The host extracts the whitelisted subset a driver needs instead of passing the whole request across the boundary (9.0 §1).
type ListArgs ¶
type ListArgs struct {
ReqPath string `json:"req_path,omitempty"`
Refresh bool `json:"refresh,omitempty"`
}
ListArgs are the arguments to StorageDriver.List.
type Logger ¶
type Logger interface {
Debugf(format string, args ...any)
Infof(format string, args ...any)
Warnf(format string, args ...any)
Errorf(format string, args ...any)
}
Logger is the host's logger, namespaced to the extension.
type LoginMethod ¶
type LoginMethod struct {
ID string `json:"id"`
Kind string `json:"kind"` // primary | second_factor | federated
Label map[string]string `json:"label"`
Icon string `json:"icon,omitempty"`
UIRef string `json:"ui_ref,omitempty"`
}
LoginMethod renders a login affordance for an AuthNMethod (design §4). Kind selects placement: "primary"/"federated" methods appear as login-page buttons that drive the host login flow; a "second_factor" method renders a prompt component after the password step. UIRef optionally points at a custom component bundle (iframe); otherwise the frontend uses a generic renderer.
type Manifest ¶
type Manifest struct {
ID string `json:"id"` // globally unique, reverse-domain: com.aliyun.drive_open
Name map[string]string `json:"name"` // i18n display name
Version string `json:"version"` // semver
CoreAPI string `json:"core_api"` // semver constraint on the host core API (0.1.0 until GA), e.g. "^0.1.0"
Platforms []string `json:"platforms,omitempty"`
Kinds []Kind `json:"kinds"`
Capabilities []Capability `json:"capabilities,omitempty"` // optional interfaces implemented; validated at registration
Provenance Provenance `json:"provenance"`
Permissions Permissions `json:"permissions"`
// Wizard and Contributions are the Step 10 declarative front-end surface:
// the install flow the host drives and the UI contributions it aggregates.
// Both are optional — a plain storage driver leaves them zero.
Wizard Wizard `json:"wizard,omitempty"`
Contributions Contributions `json:"contributions,omitempty"`
// Info is an optional declarative storage-config form for StorageDriver
// plugins. The host synthesizes the generic common fields itself; a plugin
// only declares its driver-specific Additional items (and DriverConfig).
// Without it, the "add storage" form for the plugin driver is empty.
Info *Info `json:"info,omitempty"`
// DisableWarning is the declarative replacement for a hardcoded front-end
// confirm list (formerly CONFIRM_ON_DISABLE_IDS): an optional localized
// (LangMap, en/zh) message shown to the admin BEFORE disabling this
// extension, stating the real consequence of turning it off — e.g. share:
// "关闭后所有公开分享链接将立即失效" / "Disabling this immediately invalidates all
// public share links". A plugin or built-in with genuine disable
// consequences declares the warning copy here; empty/absent means the
// extension disables without a special confirm.
DisableWarning map[string]string `json:"disable_warning,omitempty"`
// Signature and Checksum are reserved and currently unused: 9.3 package
// verification reads the sidecar checksums.txt + signature files instead.
Signature string `json:"signature,omitempty"`
Checksum string `json:"checksum,omitempty"`
}
Manifest is the contract root of an extension, distributed with it as plugin.json (SDK doc §1).
type MenuAction ¶
type MenuAction struct {
ID string `json:"id"`
Label map[string]string `json:"label"`
Icon string `json:"icon,omitempty"`
Target string `json:"target"` // file | dir | selection | global
}
MenuAction is a context-menu / toolbar action. Triggering it makes the frontend POST /api/v4/extensions/{id}/actions/{action-id}; the host routes to the extension's Action handler (SDK §4).
type Notice ¶
type Notice struct {
// Level is the notice's severity.
Level NoticeLevel `json:"level"`
// Title is a short human-readable summary.
Title string `json:"title"`
// Body is an optional longer human-readable description.
Body string `json:"body,omitempty"`
// Source is the host subsystem that raised the notice, e.g. "task",
// "storage" (empty when the raiser did not identify itself).
Source string `json:"source,omitempty"`
// UnixMilli is the host wall-clock time the notice was raised (0 when
// unset).
UnixMilli int64 `json:"unix_milli,omitempty"`
}
Notice is the serializable payload delivered to Notification.Notify. It carries only the notice's shape: never object bytes (a channel that needs more re-reads it through the Grant-gated Host) and never credentials.
type NoticeLevel ¶
type NoticeLevel string
NoticeLevel is the severity of a Notice.
const ( NoticeInfo NoticeLevel = "info" NoticeWarn NoticeLevel = "warn" NoticeError NoticeLevel = "error" )
type Notification ¶
type Notification interface {
Extension
// Notify delivers one notice to this channel. Its error/panic is isolated
// by the host. It should return promptly; heavy delivery (an outbound HTTP
// call, a retry queue) belongs on the plugin's own queue, not inline in the
// host's dispatch.
Notify(ctx context.Context, n Notice) error
}
Notification is a sink the host fans notices out to (design: NotificationChannel kind) — a plugin forwarding host notices to an external channel (email, webhook, Telegram, ...). Like EventHook it is a NOTIFICATION sink, not a veto: the host has already decided a notice is worth raising by the time Notify is called, so a channel cannot block or alter it — only forward or drop it (by failing) on its own.
Every Notify runs in failure isolation (the host recovers panics and swallows errors, logging them): one channel erroring never affects the host or the other channels. A channel that needs to borrow host capabilities (render a richer message, look up a display name) does so through the injected Host, which remains Grant-gated and Authorize-enforced (design §0, §6.3) — the Notice payload itself only carries the notice's shape, never object bytes or credentials.
type Obj ¶
type Obj struct {
Name string `json:"name"`
Size int64 `json:"size"`
Modified int64 `json:"modified"` // unix milliseconds (v3 ModTime)
Created int64 `json:"created"` // unix milliseconds (v3 CreateTime)
IsDir bool `json:"is_dir"`
ID string `json:"id"` // driver-internal id (v3 GetID)
Path string `json:"path"` // driver-internal path (v3 GetPath)
// Hashes MUST be exact whole-file digests of the named algorithm,
// lowercase hex — never a chunk/partial hash, and never truncated or
// padded. The host may use these for rapid upload matching and content
// dedup (content-addressing on the value); a wrong value silently
// produces wrong content for every consumer that trusts it. Keys are
// registered hash names ("md5", "sha1", "sha256"); an unrecognized key
// or a value whose length doesn't match that algorithm's digest width
// is dropped when crossing the boundary.
Hashes map[string]string `json:"hashes,omitempty"`
Thumb string `json:"thumb,omitempty"` // v3 Thumb optional interface
URL string `json:"url,omitempty"` // v3 URL optional interface
}
Obj is the flat, serializable DTO for a filesystem object crossing the boundary (9.0 §7). It mirrors v3's model.Obj 8-method interface plus the optional Thumb/URL, with wire-friendly scalar fields.
The "…" placeholder in the pre-freeze SDK draft is nailed down here.
type ObjRef ¶
type ObjRef struct {
StorageID string `json:"storage_id"`
Anchor string `json:"anchor"` // pillar A: ID-anchored resource key
}
ObjRef is the SDK-side projection of the architecture's ResourceRef (pillar A). It identifies a resource by storage plus an opaque anchor key, replacing v3's path-threading across the boundary (9.0 §7).
type OfflineDownloadTool ¶
type OfflineDownloadTool interface {
Extension
// Config reports the tool's identity (Name, matched against the
// offline_download_tool setting to resolve the active tool).
Config() OfflineDownloadToolConfig
// AddURL hands the daemon a URL to fetch into args.TempDir and returns the
// daemon-side gid identifying the resulting download.
AddURL(ctx context.Context, args AddArgs) (gid string, err error)
// Remove cancels/removes the download identified by gid from the daemon.
Remove(ctx context.Context, gid string) error
// Status reports the current progress/completion of the download gid.
Status(ctx context.Context, gid string) (ToolStatus, error)
}
OfflineDownloadTool is the extension point behind offline download's daemon backends (design: 子项目 B). The v4 core ships a direct-download path (SimpleHttp: op.Put straight into the target storage) as the always-available base — SimpleHttp is NOT a tool, it is the offline_download base's built-in direct path. A OfflineDownloadTool adds an external download daemon that the base hands a URL to and then polls: aria2, qBittorrent and transmission each implement this interface as an independent built-in extension.
Like ext.SearchIndexer and ext.AuthNMethod, the contract is defined here so an out-of-process (gRPC) third-party backend is a stable future seam; this wave wires only in-process TRUSTED instances the host itself authors (RegisterTrustedOfflineDownloadToolInstance on the host side). The v3 coupling to a concrete *DownloadTask is deliberately INVERTED into value DTOs (AddArgs / ToolStatus) so a tool never reaches back into the task/db layer — it only takes a URL to add and reports status by gid.
type OfflineDownloadToolConfig ¶
type OfflineDownloadToolConfig struct {
Name string `json:"name"`
}
OfflineDownloadToolConfig is a tool's self-description. Name is the stable id the offline_download_tool setting selects on (e.g. "aria2", "qBittorrent", "Transmission").
type Other ¶
type Other interface {
Other(ctx context.Context, obj ObjRef, method string, data json.RawMessage) (json.RawMessage, error)
}
Other carries an opaque, driver-specific request/response (v3 Other's interface{} payload becomes json.RawMessage across the boundary, 9.0 §6).
type Permissions ¶
type Permissions struct {
Network []string `json:"network,omitempty"` // allowed host globs
StorageTokens []string `json:"storage_tokens,omitempty"` // "self" or provider ids
Filesystem string `json:"filesystem,omitempty"` // none | temp | full
HostAPIs []string `json:"host_apis,omitempty"` // requested Host methods
}
Permissions is a declarative permission grant, shown to the admin at install time for informed consent (SDK doc §1). StorageTokens is enforced when scoped tokens are issued (IssueScopedToken); the rest is informational — every plugin runs as a full-permission process (plugin parity).
type PreviewerReg ¶
type PreviewerReg struct {
ID string `json:"id"`
Accept []string `json:"accept"`
Route string `json:"route"`
}
PreviewerReg registers a custom previewer: accepted mime/extension globs and the plugin-UI route that renders them (served under /ui/).
type Principal ¶
type Principal string
Principal is an opaque, host-issued identity handle. Across the plugin boundary it is only a random string: a plugin cannot construct one, nor read a role/permission out of it. The host holds the handle→identity mapping. It appears here so a second_factor method's Begin can be scoped to the already-identified user without exposing that user's real identity to the plugin (design §3).
type ProtocolInfo ¶
type ProtocolInfo struct {
// Scheme is the protocol name: "webdav" | "s3" | "mcp" | "ftp" | "sftp".
Scheme string `json:"scheme"`
// Transport selects how the host feeds the plugin traffic.
Transport Transport `json:"transport"`
// DefaultPort is the port the host listens on for a TransportConn protocol
// when the admin has not overridden it in settings. Ignored for TransportHTTP
// (which shares the host's HTTP listener under MountPattern).
DefaultPort int `json:"default_port,omitempty"`
// MountPattern is where the host mounts a TransportHTTP handler, e.g. "/dav"
// or "/s3". It is checked against the reserved-prefix table at
// install/registration. Ignored for TransportConn.
MountPattern string `json:"mount_pattern,omitempty"`
}
ProtocolInfo describes an AccessProtocol's binding to the host.
type Provenance ¶
type Provenance struct {
Source string `json:"source"` // official | verified-community | third-party
License string `json:"license"` // SPDX id or "proprietary"
SourceURL string `json:"source_url"` // source repo; empty => treated as closed source
Pricing string `json:"pricing"` // free | paid
}
Provenance decides the trust tier and execution mode (SDK doc §1).
type ProvisionHint ¶
type ProvisionHint struct {
DisplayName string `json:"display_name,omitempty"`
Email string `json:"email,omitempty"`
Attributes map[string]string `json:"attributes,omitempty"`
}
ProvisionHint is the attribute set a federated method suggests for a newly-provisioned user. The host decides which fields it honors; none of them can request a role or elevate privileges — the host assigns the configured minimal role and refuses to build an admin.
type PublicPage ¶
type PublicPage struct {
ID string `json:"id"`
Route string `json:"route"`
UIRef string `json:"ui_ref,omitempty"`
}
PublicPage renders an extension's unauthenticated landing page (design §4): a share bundle's public page. Route is mounted under the Manifest-requested top-level short prefix (e.g. public_prefix="/s" → "/s/:share_id"); an extension that requests no prefix falls back to the default namespace (/api/v4/ext-public/{id}/…). UIRef points at the iframe bundle, which runs in a guest context (the Bridge exposes only the public method subset).
type PutURLer ¶
type PutURLer interface {
PutURL(ctx context.Context, dstDir ObjRef, name, url string) (Obj, error)
}
PutURLer uploads by handing a URL to the storage (v3 PutURL; offline SimpleHttp path).
type Putter ¶
Putter uploads a file. The bytes are pulled from the host-held UploadStream (9.0 §2); progress/rate-limit/cancel are host concerns.
type RangeOpener ¶
type RangeOpener interface {
OpenRange(ctx context.Context, ref ObjRef, streamID string, rng Range) (io.ReadCloser, error)
}
RangeOpener is the byte source for ByteSourceStream links. The host's stream layer adapts it to its internal range-reader. Under gRPC (9.2) this becomes a server-streaming RPC; in-process native drivers may take a zero-copy fast path (the SDK's in-process adapter recognizes it) that never hits the wire.
type RapidPutter ¶
type RapidPutter interface {
TryRapidUpload(ctx context.Context, dstDir ObjRef, name string, size int64, hashes map[string]string) (Obj, bool, error)
}
RapidPutter tries a hash-based instant ("rapid") upload: if the storage already has content matching the given full-file hashes, it links a new file without transferring bytes (SP-H). Semantics mirror the host's driver.PutRapid contract:
- hit: (obj, true, nil)
- miss: (Obj{}, false, nil) — caller falls back to streaming
- hash flavor unavailable / not supported: error with code NOT_SUPPORT
Implementations MUST NOT read or spool bytes to compute a hash. hashes keys are utils hash names ("md5", "sha1", "sha256").
type RemoteDownloader ¶
type RemoteDownloader interface {
SupportedSchemes() []string
AddRemoteDownload(ctx context.Context, dstDir ObjRef, url string, opts map[string]any) (RemoteTask, error)
RemoteDownloadStatus(ctx context.Context, storageID, taskID string) (RemoteTaskStatus, error)
RemoteDownloadCancel(ctx context.Context, storageID, taskID string) error
}
RemoteDownloader submits a remote resource to the network drive's own cloud download queue — bytes never pass through AList (9.0 §6b).
type RemoteTask ¶
type RemoteTaskStatus ¶
type RouteDecl ¶
type RouteDecl struct {
// Prefix is the sub-path under the plugin namespace this route owns, e.g.
// "/webhook" or "/s". It must be a rooted, non-traversing path segment set
// ("/a/b" is fine, "../x" is not).
Prefix string `json:"prefix"`
// Public mounts the route under the unauthenticated public group. A public
// route requires the "public_routes" grant and runs with a delegated-only
// Host. Default false: mounted behind auth.
Public bool `json:"public,omitempty"`
}
RouteDecl is one sub-route a plugin serves.
type ScheduledJob ¶
type ScheduledJob interface {
Extension
// Schedule returns the job's cadence as an interval spec the host parses
// (see ParseSchedule): "@every <dur>" (e.g. "@every 30s", "@every 1h"),
// or the shorthands "@hourly"/"@daily"/"@weekly". An unparseable or
// non-positive schedule disables the job (the host logs and skips it) rather
// than defaulting to a surprise cadence.
Schedule() string
// Run performs one execution. Returning an error is logged by the host; it
// does not stop future ticks. Run should honor ctx cancellation (host
// shutdown) and return promptly when it fires.
Run(ctx context.Context) error
}
ScheduledJob is a time-driven background task a plugin contributes: share expiry cleanup, index compaction, a periodic sync (design §2.5 notes share's expiry cleanup is a ScheduledJob — time-driven, not event-driven). The host supervises scheduling; the plugin only declares its cadence and does the work.
Run executes under the host's supervision: each invocation is panic-isolated and non-overlapping (the host skips a tick if the previous Run of the same job is still going), so a slow or crashing job degrades only itself. Any host capability the job borrows inside Run stays Grant-gated + Authorize-enforced by the F0 host layer; a background job with no user context uses the fs:system scope (design §6.1) if it needs storage access.
type SearchIndexer ¶
type SearchIndexer interface {
Extension
// Config reports the backend's identity (Name, matched against the
// `search_index` setting to resolve the active backend) and whether it
// supports incremental auto-update.
Config() SearchIndexerConfig
// Search returns the nodes matching req plus the total hit count. The
// returned nodes carry full virtual paths (path.Join(Parent, Name)); the
// base re-authorizes each one per requesting user — Search itself performs
// no access control.
Search(ctx context.Context, req SearchReq) ([]SearchNode, int64, error)
// Index adds or updates a single node.
Index(ctx context.Context, node SearchNode) error
// BatchIndex adds or updates many nodes at once (the build path fans out
// through this to amortize per-write cost).
BatchIndex(ctx context.Context, nodes []SearchNode) error
// Get returns the direct children previously indexed under parent.
Get(ctx context.Context, parent string) ([]SearchNode, error)
// Del removes every node at or below prefix (a subtree delete).
Del(ctx context.Context, prefix string) error
// Clear drops the entire index.
Clear(ctx context.Context) error
}
SearchIndexer is the extension point behind file search's real-index backends (design: 子项目 A). The v4 core ships an index-less, on-the-fly BFS search as the always-available fallback; a SearchIndexer adds a persistent index that the `search` base can build once (as admin, over the full path space) and then query cheaply — database (SQL LIKE), bleve (embedded full-text) and meilisearch (external service) each implement this interface as an independent built-in extension.
Like ext.AuthNMethod, the contract is defined here so an out-of-process (gRPC) third-party backend is a stable future seam; this wave wires only in-process TRUSTED instances the host itself authors (RegisterTrustedSearchIndexerInstance on the host side). The host alone owns the security invariant — the index is built as admin and stores full paths, and the `search` base re-authorizes every hit against the requesting user before returning it — so an indexer never makes an access decision; it only stores and matches nodes.
type SearchIndexerConfig ¶
SearchIndexerConfig is a backend's self-description. Name is the stable id the `search_index` setting selects on; AutoUpdate advertises whether the backend can apply incremental updates (consulted by the later auto-update wave).
type SearchNode ¶
type SearchNode struct {
Parent string `json:"parent"`
Name string `json:"name"`
IsDir bool `json:"is_dir"`
Size int64 `json:"size"`
}
SearchNode is one indexed filesystem object. It mirrors v3's model.SearchNode: there is deliberately NO path field — the virtual path is derived as path.Join(Parent, Name) — so the index stores each object once, keyed by its parent directory and name.
type SearchReq ¶
type SearchReq struct {
Parent string `json:"parent"`
Keywords string `json:"keywords"`
Scope int `json:"scope"` // 0 all, 1 dir, 2 file
Page int `json:"page"`
PerPage int `json:"per_page"`
}
SearchReq parameterizes one index query. Scope selects object kind (0 all, 1 dir only, 2 file only); Page/PerPage paginate the surviving hits.
type ServiceStatus ¶
type ServiceStatus struct {
// State is the coarse lifecycle state: "running" | "stopped" | "error".
State string `json:"state"`
// Detail is an optional human-readable line (last error, current phase).
Detail string `json:"detail,omitempty"`
// Metrics is optional free-form structured telemetry for the status card
// (e.g. {"proxies":3,"traffic_bytes":10485760}).
Metrics map[string]any `json:"metrics,omitempty"`
}
ServiceStatus is the serializable status a BackgroundService reports.
type SettingsPage ¶
type SettingsPage struct {
ID string `json:"id"`
Title map[string]string `json:"title"`
Route string `json:"route,omitempty"`
}
SettingsPage is a settings section the extension contributes.
type SignScope ¶
type SignScope string
SignScope is the permitted use of a signed link.
const ( // SignScopeDownload authorizes fetching the referenced object's bytes. SignScopeDownload SignScope = "download" )
type Signer ¶
type Signer interface {
SignLink(ctx context.Context, p Principal, ref ObjRef, scope SignScope, ttl time.Duration) (token string, err error)
VerifyLink(ctx context.Context, token string) (ref ObjRef, scope SignScope, err error)
}
Signer issues signed, ID-anchored, expiring links reusing the core key set — the plugin never sees the key (design §3). A link is anchored to an ObjRef the plugin has access to; SignLink authorizes the ref before signing so a plugin cannot mint a link to an object outside its grant (invariant 4).
type StatusProvider ¶
type StatusProvider struct {
ID string `json:"id"`
Title map[string]string `json:"title"`
// PollPath is the host status endpoint the frontend polls, e.g.
// "/api/v4/extensions/{id}/status/{ID}" → the host resolves the extension's
// Status().
PollPath string `json:"poll_path"`
}
StatusProvider is a runtime status card the frontend renders on the system/services page (design §4). The host polls PollPath and hands the card the extension's reported status (an AccessProtocol's or BackgroundService's Status()): running state, port, metrics, a start/stop toggle.
type StorageDriver ¶
type StorageDriver interface {
Extension
DriverConfig() DriverConfig
// NewInstance delivers a mounted storage's Addition JSON to the driver and
// builds its session. One mounted storage = one instance.
NewInstance(ctx context.Context, storageID string, config json.RawMessage) error
// DropInstance tears an instance down.
DropInstance(ctx context.Context, storageID string) error
// List lists a directory. dir.StorageID routes to the instance.
List(ctx context.Context, dir ObjRef, args ListArgs) ([]Obj, error)
// Link resolves how to fetch a file's bytes (see Link / ByteSource).
Link(ctx context.Context, file ObjRef, args LinkArgs) (*Link, error)
}
StorageDriver is a storage backend extension (9.0 §3). On top of the process-level Extension lifecycle it adds instance-level lifecycle, because a single driver process multiplexes many mounted storages: ops route to an instance by ObjRef.StorageID.
type Store ¶
type Store interface {
KV() KV
// Secrets returns a KV namespace whose values are encrypted at rest
// (AES-256-GCM) on the host side — for a plugin holding OAuth tokens or API
// keys it should not persist in the plaintext KV. It shares KV's shape and
// per-plugin namespacing/grant gate, but its storage extent is disjoint from
// KV()'s: a key set through Secrets() is never visible through KV() (and
// vice versa), even when both hold the same key string.
Secrets() KV
// Table returns a named, schemaless document table: JSON rows addressed by
// a host-assigned primary key. Unlike Secrets (a fully disjoint storage
// extent), Table is a structured VIEW over the same backend as KV() —
// isolation between tables (and from plain KV keys) is by key-prefix
// design (a reserved "table:" prefix), not a separate storage extent. name
// must not be empty and must not contain ':' (rejected — see the hostcap
// implementation's escaping scheme).
Table(name string) Table
}
Store is a plugin's private, host-managed persistence, namespaced by plugin id so plugin A can never read plugin B's data and no plugin can touch a core table (design §3). F0 exposes the KV half; a manifest-declared, host- migrated RELATIONAL schema (SQL-backed tables) remains a later extension — Table() here is the honest, schemaless intermediate: named tables of JSON-document rows addressed by a host-assigned primary key, layered over the same store backend as KV().
type Table ¶
type Table interface {
// Insert stores row as a new document and returns the host-assigned id
// (a random, unique token — never derived from a counter or wall clock).
Insert(ctx context.Context, row map[string]any) (id string, err error)
// Get returns the row at id, or ok=false if no such row exists in this
// table.
Get(ctx context.Context, id string) (row map[string]any, ok bool, err error)
// Update replaces the row at id. It is error-if-missing (not an upsert):
// updating a nonexistent id returns an error rather than silently
// creating it, so a caller's Update always means "I believe this row
// already exists" — Insert is the only way to mint a new id.
Update(ctx context.Context, id string, row map[string]any) error
// Delete removes the row at id. Deleting a nonexistent id is a no-op
// (mirrors KV.Delete's semantics).
Delete(ctx context.Context, id string) error
// List returns every row in this table (id + row), scanning the table's
// key-prefix range. Order is unspecified.
List(ctx context.Context) ([]TableRow, error)
}
Table is a per-plugin, schemaless document table: named rows of arbitrary JSON data addressed by a primary key the host assigns on Insert. This is the F0 realization of "structured storage beyond raw KV" — no SQL engine, no manifest-declared schema (that richer relational form is a later extension); just PK-addressed JSON rows with list/scan, namespaced by plugin id and table name on the host side.
type TableRow ¶
TableRow is one row returned by Table.List: its host-assigned primary key paired with the JSON document stored at it.
type Theme ¶
type Theme struct {
ID string `json:"id"`
Name map[string]string `json:"name"`
Author string `json:"author,omitempty"`
Tokens string `json:"tokens,omitempty"` // relative path to the token JSON (spec §5.3)
Css string `json:"css,omitempty"` // optional relative path to the restricted CSS (§5.4)
Fonts []ThemeFont `json:"fonts,omitempty"` // optional same-origin bundled fonts (§5.5.3)
}
Theme is a UI theme the extension contributes (theming spec §5.2). Beyond the picker metadata (id/name/author) it points at the theme's token file and its optional restricted-css file + bundled fonts — all RELATIVE paths into the plugin's own asset tree, served same-origin under /plugin-assets/<plugin-id>/. The frontend fetches these, runs them through the shared sanitizer/validator gate, and registers the theme (no code or behavior crosses the boundary — a theme is declarative data + a gated, scoped stylesheet). A declaration with only id/name is metadata the frontend cannot load (nothing to fetch).
type ThemeFont ¶
type ThemeFont struct {
Family string `json:"family"`
Src string `json:"src"`
Weight string `json:"weight,omitempty"`
Style string `json:"style,omitempty"`
}
ThemeFont is a bundled-font reference in a theme contribution: a family NAME + a RELATIVE path into the plugin's assets. The host rewrites the path to a same-origin /plugin-assets/<id>/… url and emits the @font-face — the plugin never writes a remote src. woff2 only (validated by the frontend's magic-byte sniff), backed by CSP font-src 'self'.
type Token ¶
type Token struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int64 `json:"expires_in,omitempty"` // seconds; 0 = unknown/non-expiring
TokenType string `json:"token_type,omitempty"`
Raw map[string]any `json:"raw,omitempty"`
}
Token is the provider-agnostic result of Exchange/Refresh: an opaque JSON blob (Raw) plus the few fields the host understands well enough to act on — surface it as a storage credential, know whether/when to refresh it. A provider may put vendor-specific fields (e.g. an account id) only in Raw; the host round-trips Raw verbatim as part of the resulting storage's Addition config.
type ToolStatus ¶
type ToolStatus struct {
TotalBytes int64 `json:"total_bytes"`
Progress float64 `json:"progress"`
NewGID string `json:"new_gid"`
Completed bool `json:"completed"`
Status string `json:"status"`
Err error `json:"-"`
}
ToolStatus is a value snapshot of one download's state as reported by the daemon. NewGID follows a daemon that re-keys a download (e.g. aria2 promoting a metadata download to the real file); Completed signals the base to transfer the finished files into the target storage; Err carries a daemon-side failure.
type Transport ¶
type Transport uint8
Transport is how a protocol plugin receives inbound traffic.
const ( // TransportHTTP: the host proxies inbound HTTP requests to ServeHTTP. Works // in-process or cross-process (gRPC). Used by WebDAV, S3, MCP. TransportHTTP Transport = iota // TransportConn: the host owns a listener and hands each accepted net.Conn to // HandleConn. Raw byte streams (FTP/SFTP); native/in-process only in v4 (a raw // conn does not cross the gRPC boundary). TransportConn )
type UIPanel ¶
type UIPanel struct {
ID string `json:"id"`
Title map[string]string `json:"title"`
Route string `json:"route"`
Placement string `json:"placement,omitempty"` // sidebar | main | modal
}
UIPanel is a custom rich-interaction panel rendered in a sandboxed iframe from the plugin bundle (SDK §5).
type UploadInfo ¶
type UploadInfo struct {
Name string `json:"name"`
Size int64 `json:"size"`
Mimetype string `json:"mimetype,omitempty"`
Hashes map[string]string `json:"hashes,omitempty"` // host may precompute; empty => driver hashes itself via ReadAt
Exist *Obj `json:"exist,omitempty"` // carries v3 GetExist/SetExist overwrite semantics
}
UploadInfo is the metadata about an upload (9.0 §2).
type UploadStream ¶
type UploadStream interface {
Info() UploadInfo
// ReadAt is a replayable random read served by the host from a temp/seekable
// source. It follows io.ReaderAt semantics.
ReadAt(p []byte, off int64) (n int, err error)
}
UploadStream is the host-held, replayable upload source handed to a driver's Putter (9.0 §2, "pull model"). The host lands the inbound stream in a temp/seekable source; the driver reads it by range via ReadAt. This satisfies two-pass reads (hash the whole file, then upload in parts), resumable upload, and header peeking of non-seekable streams.
Responsibilities the host keeps (so 90 drivers need not each reimplement them, 9.0 §2): rate limiting is applied host-side inside ReadAt; cancellation flows through ctx on the driver's request methods; progress is reported via a Host callback rather than a per-call progress func.
type Wizard ¶
type Wizard struct {
Steps []WizardStep `json:"steps,omitempty"`
}
Wizard is an extension's declarative install flow (SDK doc §4). The host runs it as a step state machine (internal/extension/wizard.go); the frontend renders each step natively.
type WizardOption ¶
type WizardOption struct {
ID string `json:"id"`
Label map[string]string `json:"label"`
NextStep string `json:"next_step,omitempty"`
}
WizardOption is one branch of a choice step. NextStep, when set, names the step id to jump to after the option is chosen (else the flow advances to the next step in order).
type WizardStep ¶
type WizardStep struct {
ID string `json:"id"`
Type string `json:"type"`
Title map[string]string `json:"title,omitempty"`
Body map[string]string `json:"body,omitempty"` // info / risk / consent copy
Schema *JSONSchema `json:"schema,omitempty"` // form: field definitions
Options []WizardOption `json:"options,omitempty"` // choice: branches
ProbeID string `json:"probe_id,omitempty"` // test: probe selector
// Provider is the AuthProvider extension id an oauth step drives (WS-8).
// Required when Type == WizardOAuth; the host resolves it to a live
// ext.AuthProvider and calls AuthURL/Exchange against it — locally, never
// through an external broker.
Provider string `json:"provider,omitempty"`
// Scopes are the OAuth scopes requested from Provider. oauth only.
Scopes []string `json:"scopes,omitempty"`
}
WizardStep is one node of the install flow.
Source Files
¶
- access_protocol.go
- action.go
- api_route.go
- auth_provider.go
- authn_method.go
- background_service.go
- config.go
- contributions.go
- doc.go
- driver.go
- errcode.go
- event_hook.go
- extension.go
- feature_bundle.go
- file_decorator.go
- host.go
- link.go
- manifest.go
- notification.go
- obj.go
- offline_download_tool.go
- scheduled_job.go
- schema.go
- search_indexer.go
- upload.go
- wizard.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package grpcplugin is the plugin-side half of the Step 9.2 gRPC executor.
|
Package grpcplugin is the plugin-side half of the Step 9.2 gRPC executor. |