API 参考
https://side.example.com/v1https://blocks.side.example.comControl-plane REST API of sidespace-server (prefix /v1) and the data-plane
API of sidespace-blockgw. This file is the single source of truth for the
client/server contract: change it first, then the Go handlers
(services/server) and the Rust client (crates/sidespace-apiclient).
Authorization: Bearer <token> on every /v1 call except /v1/auth/* and
/v1/openapi.yaml. The token is an app's access token (from /v1/auth/*, full access)
or a personal access token ssp_… (see /v1/tokens). A personal access token carries
scopes and may be limited to some spaces; a call it does not cover answers 403 naming
the scope it needs. Scopes: files:read, files:write (includes read), sidecars:write
(every namespace) or sidecars:write:<ns> (one; a trailing * covers a family, e.g.
sidecars:write:x-acme.*), renditions:write, events:read, shares:manage,
members:manage, webhooks:manage, runs:write, pipelines:manage, plugins:manage,
comments:write, admin (everything). Tokens never reach /v1/devices, /v1/tokens
or /v1/agents.agent, made by a person
(/v1/agents). It joins spaces by its address like anyone, acts only through tokens,
and every change it makes carries actor_kind: agent in the journal./llms.txt. AI agents can also connect to the remote MCP
server at /mcp (streamable HTTP, JSON answers) with the same bearer token: tools
list_spaces, search, list_folder, describe_file, read_text, write_text, get_sidecar,
put_sidecar, list_versions, restore_version, recent_changes, list_actions, run_action,
get_run, each checked against the token's scopes and spaces.side.transcribe
(transcript sidecar), side.tags (ai.tags sidecar), side.convert (a new file next to
it, with a derived sidecar). GET /v1/spaces/{sid}/actions lists them and whether this
server is set up for them. A run is one action on one version; results are cached by
content, so the same bytes with the same params are done once (cached: true).
Pipelines run an action on every new file matching a path glob and MIME prefix. Plugins
(/v1/spaces/{sid}/plugins) add third-party actions: HTTPS services that get signed jobs
(PluginJob) and answer with a PluginResult, at once or later through
/v1/plugin-runs/{rid}/… with the job's callback token (ADR 0015). Changes
made by runs carry actor_kind: plugin and actor_via: <action>, and never start
pipelines again.{"error": {"code": "...", "message": "...", "details": {...}, "request_id": "req_…"}}
with a matching HTTP status. A space the caller is not a member of answers 404, never 403.
Every answer carries X-Request-Id and Sidespace-Version headers.If-Match: "ver_…" with the version you read (commit, delete, restore,
rename) or If-None-Match: * to write only where nothing is yet; a write that would
overwrite someone else's save answers 412 precondition_failed instead. The S3 gateway
takes If-Match: "<etag>", If-None-Match: * and x-sidespace-if-version: ver_… on
PutObject, CopyObject, CompleteMultipartUpload and DeleteObject.as_of (a time, "-2h" or a journal seq) reads paths, nodes and folders as they
were; POST …/nodes/{nid}/rewind puts a folder back. Over S3 every bucket is versioned:
ListObjectVersions, GetObject/HeadObject versionId, copy ?versionId= to restore,
trash shows as delete markers (del.<node id>; deleting one brings the file back), and
x-sidespace-as-of lists or reads the bucket as it was. PUT bucket makes a space (with
x-sidespace-fork-source: <bucket>[/<folder>], a fork); DELETE bucket deletes a space
that never held anything.?cursor=&limit=; responses carry next_cursor (null on the last page)./v1/spaces/{sid}/webhooks). Each event is one signed POST (WebhookEvent), in journal
order; failures retry with backoff (10 s doubling to 1 h; 24 failures in a row turn the
subscription off). Side-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")>;
dedupe on Side-Event-Id.usr_, dev_, spc_, nod_, ver_, shr_, ntf_, tok_, whk_, run_, pip_, plg_).