Albums and lifecycle writes
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.
POST /v1/albums
Section titled “POST /v1/albums”Bind an album id to the authenticated caller.
Idempotent: the same id from a second device, or after a recovery, is a success that writes nothing.
Authentication: required — bearer.
Request body (required, application/json): ProvisionAlbumRequest
| Status | Body | Description |
|---|---|---|
200 | ProvisionAlbumResponse application/json | The album id was already provisioned to this account; nothing was written. |
201 | ProvisionAlbumResponse application/json | The album was created and bound to the caller. |
400 | CodedProblem application/problem+json | Bad Request. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate. |
403 | CodedProblem application/problem+json | Forbidden. |
413 | — | the request body exceeds the configured limit. |
415 | CodedProblem application/problem+json | Unsupported Media Type. |
422 | CodedProblem application/problem+json | Unprocessable Entity. |
500 | CodedProblem application/problem+json | Internal server error. |
POST /v1/albums/{album_id}/ops
Section titled “POST /v1/albums/{album_id}/ops”Apply one signed lifecycle manifest to an album’s asset.
The whole battery runs before anything is written, and a rejection writes nothing — including the blobs the bundle carries, which are stored only after the manifest has passed every check the server can make without a key.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
album_id | path | string | Required. The album’s identifier. |
Request body (required, application/json): OpRequest
| Status | Body | Description |
|---|---|---|
200 | OpResponse application/json | OK. |
400 | CodedProblem application/problem+json | Bad Request. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate. |
403 | CodedProblem application/problem+json | Forbidden. |
409 | StaleRevivalProblem application/problem+json | Stale revival. |
413 | — | the request body exceeds the configured limit. |
415 | CodedProblem application/problem+json | Unsupported Media Type. |
422 | CodedProblem application/problem+json | Unprocessable Entity. |
426 | ProtocolRangeProblem application/problem+json | Upgrade required. |
500 | CodedProblem application/problem+json | Internal server error. |
GET /v1/albums/{album_id}/upgrade
Section titled “GET /v1/albums/{album_id}/upgrade”Read the ceremony’s phase and the drain count.
The one call a proposer polls between steps 2 and 4. in_flight reaching zero is the signal
that the tombstone may be committed.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
album_id | path | string | Required. The album’s id. |
| Status | Body | Description |
|---|---|---|
200 | UpgradePhaseResponse application/json | OK. |
400 | CodedProblem application/problem+json | Bad Request. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate. |
403 | CodedProblem application/problem+json | Forbidden. |
404 | CodedProblem application/problem+json | Not found. |
413 | — | the request body exceeds the configured limit. |
500 | CodedProblem application/problem+json | Internal server error. |
POST /v1/albums/{album_id}/upgrade
Section titled “POST /v1/albums/{album_id}/upgrade”Put an album into upgrade quiescence.
Idempotent under its own intent_id: versioning.md is explicit that the same UpgradeIntent
never produces two forks, and a proposer that lost an acknowledgement re-POSTs the same bytes.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
album_id | path | string | Required. The album’s id. |
Request body (required, application/cbor): string
| Status | Body | Description |
|---|---|---|
200 | UpgradePhaseResponse application/json | OK. |
400 | CodedProblem application/problem+json | Bad Request. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate. |
403 | CodedProblem application/problem+json | Forbidden. |
404 | CodedProblem application/problem+json | Not found. |
409 | CodedProblem application/problem+json | Upgrade in flight. |
413 | — | the request body exceeds the configured limit. |
415 | CodedProblem application/problem+json | Unsupported media type. |
500 | CodedProblem application/problem+json | Internal server error. |
DELETE /v1/albums/{album_id}/upgrade
Section titled “DELETE /v1/albums/{album_id}/upgrade”Abort a ceremony, returning the album to normal operation.
Named by intent_id in the path’s own query so that aborting is a statement about which
upgrade — a caller that does not hold the live id gets a 409 rather than the power to
cancel somebody else’s ceremony.
Authentication: required — bearer.
| Parameter | In | Type | Description |
|---|---|---|---|
album_id | path | string | Required. The album’s id. |
intent_id | query | string | Required. The ceremony to abort. |
| Status | Body | Description |
|---|---|---|
200 | UpgradePhaseResponse application/json | OK. |
400 | CodedProblem application/problem+json | Bad Request. |
401 | CodedProblem application/problem+json | Unauthorized. Headers: WWW-Authenticate. |
403 | CodedProblem application/problem+json | Forbidden. |
404 | CodedProblem application/problem+json | Not found. |
409 | CodedProblem application/problem+json | Upgrade in flight. |
413 | — | the request body exceeds the configured limit. |
500 | CodedProblem application/problem+json | Internal server error. |
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. |
ManifestEnvelope
Section titled “ManifestEnvelope”The server-visible mirror of the signed manifest’s envelope fields, as declared at
POST /v1/upload.
Strict (deny_unknown_fields) like the rest of the transport JSON. The Postel asymmetry
the design draws — tolerant inside documents that outlive us, strict on the wire we own —
puts unknown-key tolerance in the signed CBOR interiors, never in this JSON projection.
| Field | Type | Description |
|---|---|---|
crypto_suite_id | integer | Required. The crypto suite the blob was sealed under. Must equal the top-level declaration. |
protocol_version | string | Required. The protocol date the manifest was written under (YYYY-MM-DD). |
album_id | string | null | The album the asset belongs to. Must equal the top-level declaration. |
file_id | string | Required. The asset this blob belongs to — the same id across the bundle’s members. |
amk_version | integer | Required. The album-key epoch the manifest was written under. |
ciphertext_hash | string | Required. The ciphertext content hash, lowercase hex. Must equal the top-level hash. This names the blob this session is uploading, not the manifest’s own ciphertext_hash. For the original the two coincide; for a metadata or provenance session they do not, and the projection reuses the manifest’s field name for a per-blob declaration. Invisible for a create, because the bundle is assembled in a pending row nobody can see and no member has to name another. It is not invisible for a replace, which is why [Self::original_blob_hash] exists (S-C43). |
plaintext_size | integer | Required. The plaintext length the manifest commits to. |
chunk_size | integer | Required. The STREAM plaintext chunk size. |
key_mode | string | Required. derived or wrapped. |
metadata_blob_hash | string | null | The content hash of the bundle’s metadata blob, when the manifest commits to one. |
original_blob_hash | string | null | The content hash of the bundle’s original blob, when the manifest commits to one (S-C43). The manifest’s own ciphertext_hash, under a name that cannot be confused with [Self::ciphertext_hash]‘s per-session meaning. Optional on the wire and required on a replace: a replace re-points roles that already have bytes, so it has to be applied as one act, and the only member of the bundle that can carry the whole change is the manifest — which therefore has to be able to name the original it commits to. A create may omit it. Its bundle is assembled incrementally in a row nobody can see, so no member needs to name another and requiring it would be a wire change for no gain. |
created_by_user | string | Required. The account that created the asset. |
created_by_device | string | Required. The device that created it, as a UUID — invariant 7’s subject. |
client_version | string | Required. The client build that wrote the manifest. |
timestamp | string | Required. The manifest’s self-asserted RFC3339 timestamp — invariants 7 and 8’s subject. |
action | string | Required. The lifecycle action. create or replace on this surface — the two that move blob bytes — and see [GateReject::ActionNotAllowed] for the rest. |
prior_provenance_hash | string | null | The provenance chain position this write continues from. |
retention_until | string | null | The retention floor the manifest carries, when it carries one. |
OpRequest
Section titled “OpRequest”The signed manifest bundle a lifecycle write carries.
| Field | Type | Description |
|---|---|---|
manifest_envelope | ManifestEnvelope | Required. The server-visible projection of the signed manifest’s fields, exactly as POST /v1/upload carries it. Its album_id must equal the path segment and its action must be one this surface accepts. |
manifest_cbor | string | Required. The signed manifest itself, base64 of the canonical CBOR. Stored verbatim as the asset’s new provenance blob, so the feed serves the exact bytes the client signed (S-C30) for a lifecycle write as it already does for an upload. The server does not parse it: base64 is a transport encoding, and decode(encode(b)) == b. |
metadata_blob | string | null | The encrypted metadata blob, base64, present exactly when the action carries one. Its content hash must equal the manifest’s committed metadata_blob_hash (invariant 25). The server holds no key and never reads it. |
OpResponse
Section titled “OpResponse”What a lifecycle write did.
| Field | Type | Description |
|---|---|---|
asset_id | string | Required. The asset the op chained onto. |
sync_seq | integer | Required. The feed position it occupies. On a replay, the position the first application took. |
action | string | Required. The action that was applied. |
replayed | boolean | Required. Whether this response is a replay of an already-applied manifest. Advisory, and deliberately not something a correct client needs: the other three fields are identical either way, which is what “byte-identical prior response” means. |
ProtocolRangeProblem
Section titled “ProtocolRangeProblem”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. |
protocol_min | string | The oldest protocol date this server still speaks (YYYY-MM-DD). |
protocol_max | string | The newest protocol date this server speaks (YYYY-MM-DD). |
ProvisionAlbumRequest
Section titled “ProvisionAlbumRequest”The provisioning request.
| Field | Type | Description |
|---|---|---|
album_id | string | Required. The client-derived album id, as a canonical lowercase hyphenated UUID. |
ProvisionAlbumResponse
Section titled “ProvisionAlbumResponse”What provisioning did.
| Field | Type | Description |
|---|---|---|
album_id | string | Required. The album, echoed. |
protocol_version | string | Required. The protocol date the album is pinned to — the server’s, fixed at creation. |
created | boolean | Required. Whether this call created the album. Advisory; both answers mean the same thing. |
StaleRevivalProblem
Section titled “StaleRevivalProblem”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. |
chain_head | string | null | The manifest hash the asset’s chain is actually at. Absent when the conflict is not a chain conflict, which is why it is nullable. |
UpgradePhaseResponse
Section titled “UpgradePhaseResponse”The ceremony this album is in, as a client polls it.
| Field | Type | Description |
|---|---|---|
album_id | string | Required. The album, echoed. |
intent_id | string | null | The ceremony in flight, or absent when the album is in normal operation. Absent also covers expired: the deadline passing aborts the upgrade, so there is nothing left to be in. |
to_protocol_version | string | null | The protocol version the fork will be pinned to, when a ceremony is in flight. |
expires_at | string | null | When the window closes, RFC 3339, on the server’s clock. |
in_flight | integer | Required. How many upload sessions are still in flight against this album. The drain signal of versioning.md step 3: the proposer waits for zero. A count rather than a listing, because the proposer needs to know whether to wait and has no business seeing other members’ upload identifiers to find out. |