Skip to content

Sync and blob fetch

Stable

Generated 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.

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.

ParameterInTypeDescription
If-MatchheaderstringThe entity tag the client’s copy was taken from, per RFC 9110 section 13.1.1.
If-Modified-SinceheaderstringThe date the client’s copy carries, per RFC 9110 section 13.1.3.
If-None-MatchheaderstringThe entity tag the client already holds, per RFC 9110 section 13.1.2.
If-RangeheaderstringThe 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-SinceheaderstringThe date the client’s copy carries, refused if the representation changed since, per RFC 9110 section 13.1.4.
RangeheaderstringThe 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-SuiteheaderintegerThe crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400.
X-Capsule-ProtocolheaderstringRequired. 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-Signatureheaderstring | nullt=<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-SchemaheaderintegerThe sidecar schema version declared at sidecar_schema field 0. Sent on metadata updates; a schema newer than this server indexes is refused with 400.
hashpathstringRequired. The blob’s ciphertext content address, lowercase hex.
StatusBodyDescription
200string
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.
206string
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.
400CodedProblem
application/problem+json
Bad Request; Malformed handshake. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
403CodedProblem
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.
404CodedProblem
application/problem+json
Not found. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
409CodedProblem
application/problem+json
Upload in progress. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
410CodedProblem
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.
413CodedProblem
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.
416CodedProblem
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.
429CodedProblem
application/problem+json
Rate budget exceeded. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
500CodedProblem
application/problem+json
Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.

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.

ParameterInTypeDescription
X-Capsule-Crypto-SuiteheaderintegerThe crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400.
X-Capsule-ProtocolheaderstringRequired. 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-Signatureheaderstring | nullt=<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-SchemaheaderintegerThe 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_idquerystring | nullOne 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.
cursorquerystring | nullThe opaque cursor a previous page returned. Absent means “from the beginning”.
page_sizequeryinteger | nullHow 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.
StatusBodyDescription
200SyncPageResponse
application/json
OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
400CodedProblem
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.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
403CodedProblem
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.
409CodedProblem
application/problem+json
Stale sync cursor. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
413CodedProblem
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.
429CodedProblem
application/problem+json
Rate budget exceeded. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
500CodedProblem
application/problem+json
Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.

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.

ParameterInTypeDescription
X-Capsule-Crypto-SuiteheaderintegerThe crypto suite id from the primitives inventory. Sent on writes; a suite this server does not implement is refused with 400.
X-Capsule-ProtocolheaderstringRequired. 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-SchemaheaderintegerThe 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

StatusBodyDescription
200ReannounceResponse
application/json
OK. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
400CodedProblem
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.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate, X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
403CodedProblem
application/problem+json
Forbidden. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
413CodedProblem
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.
415CodedProblem
application/problem+json
Unsupported Media Type. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
422CodedProblem
application/problem+json
Unprocessable Entity. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
425CodedProblem
application/problem+json
Too early. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
426CodedProblem
application/problem+json
Protocol version unsupported. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.
500CodedProblem
application/problem+json
Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.

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.

An RFC 9457 problem detail.

FieldTypeDescription
typestringRequired.
titlestring—
statusintegerRequired.
detailstring—
instancestring—
codestringRequired. The stable error.* catalog code. The client localizes this; detail stays English. Present on every problem this server renders.
request_idstringThe 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.

What re-announcing one manifest did.

One of: restored, already_indexed, missing_blobs, refused.

The POST /v1/sync/reannounce body.

FieldTypeDescription
manifestsstring[]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.

The POST /v1/sync/reannounce response.

FieldTypeDescription
resultsReannounceResult[]Required. One answer per manifest, in request order and never shortened.

One manifest’s answer.

FieldTypeDescription
asset_idstring | nullThe asset the manifest names. Absent only when it did not decode far enough to name one.
outcomeReannounceOutcomeRequired. What happened.
sync_seqinteger | nullThe position a restored row was published at.
missingstring[]The content addresses the store does not hold, for missing_blobs. Empty otherwise.
codestring | nullThe stable error.sync.reannounce_* code, for refused.

One blob an asset holds.

FieldTypeDescription
roleWireBlobRoleRequired. The blob’s role in the bundle.
hashstringRequired. Its ciphertext content address, lowercase hex.
sizeintegerRequired. Its size in bytes, so a client can budget a fetch before issuing one.

One change in a library.

FieldTypeDescription
asset_idstringRequired. The asset that changed.
album_idstringRequired. The album it belongs to. The client keeps its anti-rewind high-water mark per album.
protocol_versionstringRequired. The album’s pinned protocol date. A client refuses an entry above its own maximum rather than applying it partially.
sync_seqintegerRequired. The entry’s position. Strictly increasing within a page, and therefore within any album the page touches.
changeWireChangeKindRequired. What this is to the client that asked.
manifest_cborstring | nullThe 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_blobstring | nullThe 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.
blobsSyncBlobRef[]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_manifestsstring[]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_heldbooleanRequired. 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.
purgedbooleanWhether 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_atstringRequired. When the change happened, RFC 3339.

A page of the feed.

FieldTypeDescription
entriesSyncEntry[]Required. The changes, in sync_seq order.
next_cursorstringRequired. 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_morebooleanRequired. 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.

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.

What an entry is, relative to the client that asked for it.

One of: created, updated, deleted.