Skip to content

Upload

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.

Open an upload session for one blob of an asset bundle.

Runs the refuse-by-default envelope battery — invariants 1–8 and the top-level↔envelope consistency family — before anything is written, then stages the session’s file and records the session. A request whose (owner, hash, album) tuple already has an active session gets that session back rather than a second one.

Authentication: required — bearer.

ParameterInTypeDescription
X-Capsule-Protocolheaderstring | nullThe protocol date the client speaks. Read as a string rather than a typed value so that a malformed one is this surface’s coded 400 rather than the framework’s uncoded one.

Request body (required, application/json): CreateUploadRequest

StatusBodyDescription
200CreateUploadResponse
application/json
The active session for these bytes, to resume. Headers: Location, X-Capsule-Offset, X-Capsule-Suggested-Chunk-Size.
201CreateUploadResponse
application/json
Upload session created. Headers: Location, X-Capsule-Offset, X-Capsule-Suggested-Chunk-Size.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
409DuplicateBlobProblem
application/problem+json
Album quiescing.
413CodedProblem
application/problem+json
File too large.
415CodedProblem
application/problem+json
Unsupported Media Type.
422CodedProblem
application/problem+json
Unprocessable Entity.
426ProtocolRangeProblem
application/problem+json
Protocol version unsupported.
500CodedProblem
application/problem+json
Internal server error.

Every upload the caller can resume.

Oldest first, which is the order the store promises and the order a client wants: the oldest in-flight session is the one closest to eviction.

Authentication: required — bearer.

ParameterInTypeDescription
statusquerystring | nullReturn only sessions in this state. One of pending, uploading, waiting_for_processing, completed, failed_processing — the same tokens the X-Capsule-Upload-Status header carries, so a client filters on the value it was already given rather than on a second vocabulary.
StatusBodyDescription
200SessionsResponse
application/json
OK.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
413the request body exceeds the configured limit.
500CodedProblem
application/problem+json
Internal server error.

Cancel a session: its record, its accepted chunks and its staged bytes, together.

Refused while finalization is running — it is not interruptible — and refused once the session is terminal, because there is nothing left to cancel and the receipt is what a client should read instead.

Authentication: required — bearer.

ParameterInTypeDescription
X-Capsule-Protocolheaderstring | nullThe protocol date the client speaks. Read as a string rather than a typed value so that a malformed one is this surface’s coded 400 rather than the framework’s uncoded one.
idpathstringRequired. The session’s identifier, as POST /v1/upload returned it.
StatusBodyDescription
204the request succeeded and there is no content to send.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
404CodedProblem
application/problem+json
Upload session not found.
409CodedProblem
application/problem+json
Session not active.
413the request body exceeds the configured limit.
426ProtocolRangeProblem
application/problem+json
Protocol version unsupported.
500CodedProblem
application/problem+json
Internal server error.

Report a session’s progress and state.

The resumption primitive: a client that lost a connection, an acknowledgement, or a process asks here and learns the authoritative offset, the declared length and the session’s state. The answer carries no body — HTTP forbids one on HEAD, which is why the protocol puts all three on headers.

Authentication: required — bearer.

ParameterInTypeDescription
X-Capsule-Protocolheaderstring | nullThe protocol date the client speaks. Read as a string rather than a typed value so that a malformed one is this surface’s coded 400 rather than the framework’s uncoded one.
idpathstringRequired. The session’s identifier, as POST /v1/upload returned it.
StatusBodyDescription
200Progress and state on X-Capsule-* headers, no body. Headers: Cache-Control, X-Capsule-Content-Length, X-Capsule-Offset, X-Capsule-Upload-Status.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
404CodedProblem
application/problem+json
Upload session not found.
413the request body exceeds the configured limit.
426ProtocolRangeProblem
application/problem+json
Protocol version unsupported.
500CodedProblem
application/problem+json
Internal server error.

Append a chunk, and finalize when it completes the declared size.

Every rule the chunk contract fixes is checked before a byte is written, and the checksum is verified against the received bytes first, so a chunk corrupted in transit persists nothing.

Authentication: required — bearer.

ParameterInTypeDescription
X-Capsule-Checksumheaderstring | nullThe chunk’s SHA-256, bare lowercase hex. Required: the idempotency tuple is undefined without it.
X-Capsule-Offsetheaderstring | nullWhere in the blob this chunk starts.
X-Capsule-Protocolheaderstring | nullThe protocol date the client speaks.
idpathstringRequired. The session’s identifier, as POST /v1/upload returned it.

Request body (required, application/octet-stream): string

StatusBodyDescription
204the request succeeded and there is no content to send. Headers: X-Capsule-Offset.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
404CodedProblem
application/problem+json
Upload session not found.
409OffsetMismatchProblem
application/problem+json
Offset mismatch.
413CodedProblem
application/problem+json
Chunk too large.
415CodedProblem
application/problem+json
Unsupported media type.
426ProtocolRangeProblem
application/problem+json
Protocol version unsupported.
500CodedProblem
application/problem+json
Internal server error.

Fetch the custody receipt for a finalized upload.

Authentication: required — bearer.

ParameterInTypeDescription
idpathstringRequired. The session’s identifier, as POST /v1/upload returned it.
StatusBodyDescription
200string
application/cbor
OK.
400CodedProblem
application/problem+json
Bad Request.
401CodedProblem
application/problem+json
Unauthorized. Headers: WWW-Authenticate.
403CodedProblem
application/problem+json
Forbidden.
404CodedProblem
application/problem+json
Not found.
409CodedProblem
application/problem+json
Receipt not available.
413the request body exceeds the configured limit.
500CodedProblem
application/problem+json
Internal server error.

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.

The body of POST /v1/upload.

Strict (deny_unknown_fields): an unknown field is a client bug and is refused rather than ignored. Plaintext metadata — a filename, a capture date, dimensions — is deliberately absent: it rides the encrypted metadata blob and never the wire request.

FieldTypeDescription
sizeintegerRequired. The ciphertext length in bytes. Immutable for the session’s life.
hashstringRequired. The ciphertext content hash, lowercase hex; the digest length is the suite’s.
content_typestringRequired. The media type, from the closed enum this protocol version fixes.
crypto_suite_idintegerRequired. The crypto suite the blob was sealed under.
protocol_versionstringRequired. The protocol date (YYYY-MM-DD) this session is pinned to.
blob_roleWireBlobRoleRequired. The blob’s role in its bundle.
manifest_envelopeManifestEnvelopeRequired. The unencrypted manifest fields the server validates.
album_idstring | nullThe album the asset is filed into. Optional on the wire because the contract reserves the shape for owner-scoped kinds and for the album-upgrade ceremony; required by this server, which has no way to check invariant 6 without one and refuses rather than skipping it.
owner_idstring | nullThe owner the asset is filed under, when it is not the uploader. Refused when it is anyone but the uploader: an on-behalf upload needs a verified relationship, and the port that would answer for one does not exist here.
intent_idstring | nullThe album-upgrade intent this write belongs to, when it belongs to one. Carried onto the session verbatim and read by nobody in this port; the ceremony that gives it meaning is S-C24.

What a client needs to start sending bytes.

FieldTypeDescription
idstringRequired. The session’s identifier.
upload_urlstringRequired. Where to send chunks.
suggested_chunk_sizeintegerRequired. A starting chunk size. A suggestion only — the client owns adaptation.

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.
existing_assetstringThe asset already holding these exact bytes in the same album. Structured so a client merges rather than re-parsing a sentence (slice S-C22).

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.

FieldTypeDescription
crypto_suite_idintegerRequired. The crypto suite the blob was sealed under. Must equal the top-level declaration.
protocol_versionstringRequired. The protocol date the manifest was written under (YYYY-MM-DD).
album_idstring | nullThe album the asset belongs to. Must equal the top-level declaration.
file_idstringRequired. The asset this blob belongs to — the same id across the bundle’s members.
amk_versionintegerRequired. The album-key epoch the manifest was written under.
ciphertext_hashstringRequired. 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_sizeintegerRequired. The plaintext length the manifest commits to.
chunk_sizeintegerRequired. The STREAM plaintext chunk size.
key_modestringRequired. derived or wrapped.
metadata_blob_hashstring | nullThe content hash of the bundle’s metadata blob, when the manifest commits to one.
original_blob_hashstring | nullThe 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_userstringRequired. The account that created the asset.
created_by_devicestringRequired. The device that created it, as a UUID — invariant 7’s subject.
client_versionstringRequired. The client build that wrote the manifest.
timestampstringRequired. The manifest’s self-asserted RFC3339 timestamp — invariants 7 and 8’s subject.
actionstringRequired. The lifecycle action. create or replace on this surface — the two that move blob bytes — and see [GateReject::ActionNotAllowed] for the rest.
prior_provenance_hashstring | nullThe provenance chain position this write continues from.
retention_untilstring | nullThe retention floor the manifest carries, when it carries one.

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.
offsetintegerThe offset the server is actually at, so a client resumes from it instead of asking again.

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.
protocol_minstringThe oldest protocol date this server still speaks (YYYY-MM-DD).
protocol_maxstringThe newest protocol date this server speaks (YYYY-MM-DD).

One in-flight upload, as the resumption listing serves it.

Deliberately not the whole [UploadSessionRecord]. The manifest envelope, the expected hash and the crypto pin are finalization’s inputs and are already the client’s own — echoing them to every listing would put a signed document in a response nobody reads it from.

FieldTypeDescription
idstringRequired. The session id, which is what PATCH /v1/upload/{id} resumes against.
asset_idstringRequired. The asset the bundle belongs to, so a client can group a bundle’s sessions.
album_idstring | nullThe album it is filed into, when it named one.
blob_rolestringRequired. This blob’s role in its bundle: original, derivative, metadata, provenance or backup.
statusstringRequired. Where the session is in its state machine.
received_bytesintegerRequired. Bytes durably accepted so far — the offset a resume continues from.
total_sizeintegerRequired. The declared total.
created_atstringRequired. When the session was created, RFC 3339.
last_progress_atstringRequired. When the last chunk was accepted, RFC 3339, or the creation time if none. What the survival floor is measured from, so a client can tell which of its stalled sessions is closest to being evicted.

The listing.

FieldTypeDescription
sessionsSessionSummary[]Required. Every session the caller can resume, oldest first, ties broken by upload id.

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.