Sync and blob fetch
StableGenerated from capsule-server/openapi.json, the OpenAPI 3.2 document
capsule-server emits and mise run openapi-check-kynos keeps current. To change a
description on this page, change the annotation on the handler or model it comes from
and regenerate — this file is build output. The auth model, error contract, and
conventions common to every endpoint are on the REST API overview.
GET /v1/blob/{hash}
Section titled “GET /v1/blob/{hash}”Fetch a ciphertext blob by its content address, ranged.
Opaque octets: the server holds no key and this route never learns what it is serving. An
account fetches the blobs of its own assets and of the albums it is currently a member of; a
former member is told 403, and everyone else is told what an unknown address is told —
see [crate::serve] for the boundary and its reasons.
The one answer that is account-scoped is the transient 409: it reports the caller’s own
in-flight upload and nobody else’s.
A federated peer fetches here with a capability instead of a session token, through the same resolution and the same authority.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
If-Match | header | string | The entity tag the client’s copy was taken from, per RFC 9110 section 13.1.1. |
If-Modified-Since | header | string | The date the client’s copy carries, per RFC 9110 section 13.1.3. |
If-None-Match | header | string | The entity tag the client already holds, per RFC 9110 section 13.1.2. |
If-Range | header | string | The entity tag the client’s partial copy came from, per RFC 9110 section 13.1.5. The Range is honoured only if it matches this representation under the strong comparison; otherwise the whole representation is sent. |
If-Unmodified-Since | header | string | The date the client’s copy carries, refused if the representation changed since, per RFC 9110 section 13.1.4. |
Range | header | string | The part of the representation to transfer, per RFC 9110 section 14.2. A field this operation cannot apply is ignored and the whole representation is sent. Example: bytes=0-1023. |
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Request-Signature | header | string | null | t=<Unix seconds>, n=<nonce>, s=<base64url Ed25519 signature>, made by the peer a presented federation capability names, over the request’s method, path and capability (design/federation.md, “Request Signatures”). Required with a capability; ignored with a session token. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
hash | path | string | Required. The blob’s ciphertext content address, lowercase hex. |
| Status | Body | Description |
|---|---|---|
200 | string application/octet-stream | the whole representation. Headers: Accept-Ranges, ETag, Last-Modified, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
206 | string application/octet-stream | the part the request asked for. Headers: Accept-Ranges, Content-Range, ETag, Last-Modified, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
304 | — | the client’s copy is current. Headers: ETag, Last-Modified, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Access revoked; Capability revoked; Server blocked; Scope insufficient. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
404 | CodedProblem application/problem+json | Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Upload in progress. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
410 | CodedProblem application/problem+json | Gone. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
412 | — | the representation is not the one the client’s copy came from. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
416 | CodedProblem application/problem+json | Range Not Satisfiable. Headers: Content-Range, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Rate budget exceeded. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
GET /v1/sync
Section titled “GET /v1/sync”Returns the changes in the caller’s library after cursor.
Read-only and idempotent: two calls with the same cursor return the same page, because the cursor names a position rather than consuming one. That is what makes a lost response harmless and a retry free.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Request-Signature | header | string | null | t=<Unix seconds>, n=<nonce>, s=<base64url Ed25519 signature>, made by the peer a presented federation capability names, over the request’s method, path and capability (design/federation.md, “Request Signatures”). Required with a capability; ignored with a session token. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
album_id | query | string | null | One album’s page rather than the caller’s own feed. For the album’s owner or any account on its current roster. Positions are the owner’s sequence numbers filtered to the album, and the cursor is bound to (caller, album), so it cannot be presented on the caller’s own feed or on another album. Absent: the caller’s own library, as before. |
cursor | query | string | null | The opaque cursor a previous page returned. Absent means “from the beginning”. |
page_size | query | integer | null | How many entries to return. Clamped into the range this server serves. u32 and not usize: Kynos refuses to describe a platform-width integer, and it is right to — a schema whose bounds depend on the server’s pointer size is a schema no client can rely on. |
| Status | Body | Description |
|---|---|---|
200 | SyncPageResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Invalid sync cursor; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden; Album access denied; Capability revoked; Audience mismatch; Server blocked. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
409 | CodedProblem application/problem+json | Stale sync cursor. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
429 | CodedProblem application/problem+json | Rate budget exceeded. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
POST /v1/sync/reannounce
Section titled “POST /v1/sync/reannounce”Restore index rows from the signed manifests a client holds, after a rebuild lost them.
Idempotent per manifest: a row restored once is already_indexed the second time, and
nothing is written again.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
X-Capsule-Crypto-Suite | header | integer | The crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400. |
X-Capsule-Protocol | header | string | Required. The YYYY-MM-DD protocol version this request is written against. Outside the server’s [X-Capsule-Protocol-Min, X-Capsule-Protocol-Max] window the request is refused with 426. |
X-Capsule-Sidecar-Schema | header | integer | The sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400. |
Request body (required, application/json): ReannounceRequest
| Status | Body | Description |
|---|---|---|
200 | ReannounceResponse application/json | OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
400 | CodedProblem application/problem+json | Bad Request; Invalid request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
403 | CodedProblem application/problem+json | Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
413 | CodedProblem application/problem+json | Payload Too Large; the request body exceeds the configured limit. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
415 | CodedProblem application/problem+json | Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
422 | CodedProblem application/problem+json | Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
425 | CodedProblem application/problem+json | Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
426 | CodedProblem application/problem+json | Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
500 | CodedProblem application/problem+json | Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id. |
Schemas
Section titled “Schemas”The models these endpoints carry. A field whose type names another model links to it when this page documents that model, which it does when some path from an operation reaches it within 4 references. A model only ever reached deeper than that is named without being expanded.
CodedProblem
Section titled “CodedProblem”An RFC 9457 problem detail.
| Field | Type | Description |
|---|---|---|
type | string | Required. |
title | string | — |
status | integer | Required. |
detail | string | — |
instance | string | — |
code | string | Required. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders. |
request_id | string | The UUIDv7 this server minted for the request, as the X-Request-Id header carries it. Quote it when reporting the failure: it names the request’s log lines. |
ReannounceOutcome
Section titled “ReannounceOutcome”What re-announcing one manifest did.
One of: restored, already_indexed, missing_blobs, refused.
ReannounceRequest
Section titled “ReannounceRequest”The POST /v1/sync/reannounce body.
| Field | Type | Description |
|---|---|---|
manifests | string[] | Required. Each asset’s chain-head provenance record, base64 of the exact bytes the client holds: the same bytes the feed’s manifest_cbor carries and an upload’s provenance blob is. |
ReannounceResponse
Section titled “ReannounceResponse”The POST /v1/sync/reannounce response.
| Field | Type | Description |
|---|---|---|
results | ReannounceResult[] | Required. One answer per manifest, in request order and never shortened. |
ReannounceResult
Section titled “ReannounceResult”One manifest’s answer.
| Field | Type | Description |
|---|---|---|
asset_id | string | null | The asset the manifest names. Absent only when it did not decode far enough to name one. |
outcome | ReannounceOutcome | Required. What happened. |
sync_seq | integer | null | The position a restored row was published at. |
missing | string[] | The content addresses the store does not hold, for missing_blobs. Empty otherwise. |
code | string | null | The stable error.sync.reannounce_* code, for refused. |
SyncBlobRef
Section titled “SyncBlobRef”One blob an asset holds.
| Field | Type | Description |
|---|---|---|
role | WireBlobRole | Required. The blob’s role in the bundle. |
hash | string | Required. Its ciphertext content address, lowercase hex. |
size | integer | Required. Its size in bytes, so a client can budget a fetch before issuing one. |
SyncEntry
Section titled “SyncEntry”One change in a library.
| Field | Type | Description |
|---|---|---|
asset_id | string | Required. The asset that changed. |
album_id | string | Required. The album it belongs to. The client keeps its anti-rewind high-water mark per album. |
protocol_version | string | Required. The album’s pinned protocol date. A client refuses an entry above its own maximum rather than applying it partially. |
sync_seq | integer | Required. The entry’s position. Strictly increasing within a page, and therefore within any album the page touches. |
change | WireChangeKind | Required. What this is to the client that asked. |
manifest_cbor | string | null | The signed manifest, base64 of the provenance blob’s exact bytes. On a tombstone, the signed delete record, whose retention_until is the asset’s retention window; absent once a purge has dropped it. Absent too — with a loud server-side log — when the index names a provenance blob the store cannot produce. |
metadata_blob | string | null | The encrypted metadata blob’s content address. On a tombstone in the owner’s own feed, the asset’s live metadata blob, which a restore needs; absent once a purge has dropped it. Absent on a tombstone in any album page, an account’s or a peer’s. |
blobs | SyncBlobRef[] | Required. The asset’s original and derivative blobs. On a tombstone in the owner’s own feed, only its original, which a restore needs; empty once a purge has dropped it. Empty on a tombstone in any album page. |
derivative_manifests | string[] | The asset’s signed derivative manifests, each base64 of its derivative_manifest blob’s exact bytes, in address order (#556). What a receiver opens a thumbnail or preview with: the manifest names the derivative’s ciphertext_hash, nonce_prefix and AMK epoch, and the blob list carries none of them. Served verbatim for the reason [Self::manifest_cbor] is. Empty on a tombstone; a manifest the store cannot produce is left out with a loud server-side log. |
original_held | boolean | Required. Whether the original has landed. false is the derived awaiting-original state. On a tombstone in the owner’s own feed, whether the original a restore needs is held; false once a purge has dropped it. Always false on a tombstone in any album page. |
purged | boolean | Whether the asset has been purged: its retention window closed and the server dropped its references, so no trash-restore can bring it back (#885). Only ever true on a tombstone. This, not an absent manifest_cbor, is a client’s evidence of a purge: the manifest is also absent when the store cannot produce it. A server that predates the field sends no key, which reads as false. |
changed_at | string | Required. When the change happened, RFC 3339. |
SyncPageResponse
Section titled “SyncPageResponse”A page of the feed.
| Field | Type | Description |
|---|---|---|
entries | SyncEntry[] | Required. The changes, in sync_seq order. |
next_cursor | string | Required. The cursor that resumes after the last entry. Always present, including on an empty page, where it re-mints the position the client arrived with. A client therefore never has to decide whether to keep its old cursor. |
has_more | boolean | Required. Whether the server holds changes beyond this page. Answered from the owner’s high-water mark rather than by fetching one more entry, so a caught-up client is told so without paying for a page it will not receive. |
WireBlobRole
Section titled “WireBlobRole”A blob’s role in its asset bundle, as the wire spells it.
A wire type of its own rather than a serde derive on [BlobRole]: the state ports’
records deliberately derive no serde traits, so that a record cannot be smuggled through a
store built for another. The mapping is one match in one direction.
One of: original, derivative, metadata, provenance, backup, derivative_manifest.
WireChangeKind
Section titled “WireChangeKind”What an entry is, relative to the client that asked for it.
One of: created, updated, deleted.