Skip to content

Storage verification

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 every custody receipt covering one asset.

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.
asset_idpathstringRequired. The asset id.
StatusBodyDescription
200AssetReceiptsResponse
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; 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.
404CodedProblem
application/problem+json
Not found. 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.
500CodedProblem
application/problem+json
Internal server error. Headers: X-Capsule-Min-Client-Build, X-Capsule-Protocol-Max, X-Capsule-Protocol-Min, X-Request-Id.

Confirm that the server holds the copies a client is about to stop holding.

A pure read: it writes no blob, no index row and no verdict. Soundness against a racing collection comes from the standing GC grace window rather than from a per-request lease, which is why nothing here takes one.

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): StorageVerifyRequest

StatusBodyDescription
200StorageVerifyResponse
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.
429StorageVerifyRateLimitedProblem
application/problem+json
Too many requests. Headers: Retry-After, 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.

One custody receipt, decoded, beside the bytes that were signed.

FieldTypeDescription
receipt_seqintegerRequired. Strictly monotonic per server. The chain position this receipt cannot be moved from.
server_idstringRequired. This server’s canonical origin — what binds the receipt to one server.
server_key_idstringRequired. The attestation key fingerprint that signed, hex. Survives rotation, which is why the key is named rather than assumed.
prior_receipt_hashstring | nullSHA-256 of the previous receipt in the server’s log, hex. Absent for the first receipt this server ever issued.
upload_idstringRequired. The upload session that produced custody.
blob_rolestringRequired. original, derivative, metadata or provenance.
ciphertext_hashstringRequired. The server-recomputed ciphertext content address, hex.
sizeintegerRequired. Ciphertext size in bytes.
envelope_hashstring | nullSHA-256 of the asset’s signed manifest, hex — present on the provenance receipt and absent on every other, because the manifest commits to the rest.
received_atstringRequired. The server’s trusted clock at the finalization commit, RFC 3339.
receipt_cborstringRequired. The full signed receipt as canonical CBOR, base64. This is the receipt. Verify the hybrid signature over these bytes under the key server_key_id names, from /.well-known/capsule/attestation-keys; everything above is a reading of them.

The chain.

FieldTypeDescription
asset_idstringRequired. The asset the chain belongs to, echoed so a client batching requests can tell the answers apart.
receiptsAssetReceipt[]Required. Every receipt covering the asset, in receipt_seq order.

One asset to verify, with the exact copies the client is relying on.

FieldTypeDescription
asset_idstringRequired. The asset.
blob_hashesstring[]Required. Every content address the client would be trusting the server with. The verdict is a conjunction over exactly these, so a client asks about what it is about to delete.

One declared blob’s verdict.

FieldTypeDescription
hashstringRequired. The address, as the client declared it.
rolestringRequired. The role the asset holds it under — unknown for a hash the asset does not hold.
storedbooleanRequired. The bytes are present at that address.
indexedbooleanRequired. A live asset of the caller’s references the address.
retrievablebooleanRequired. Nothing is withholding it.
deepstring | nullWhat a deep scan found: intact, corrupt, or rate_limited. Absent when no deep scan ran, and the absence is load-bearing: it is the difference between “we did not look at the bytes” and “we looked and they were fine”, and a client deciding whether to release its only copy has to be able to tell those apart.

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.

One asset’s verdict.

FieldTypeDescription
asset_idstringRequired. The asset the client asked about.
durablebooleanRequired. Every declared blob is stored ∧ indexed ∧ retrievable. This is the field that gates a deletion, so it is false whenever the server cannot say otherwise.
blobsBlobVerdictResponse[]Required. One entry per declared hash, in declaration order and never shortened.
checked_atstringRequired. The server’s own clock at verification, RFC 3339. Never the client’s.

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.
retry_afterintegerWhen the caller may retry, as Unix seconds. From the limiter’s own window when a budget is spent, and an upper bound of one window when the limiter is at capacity.

The POST /v1/storage/verify body.

FieldTypeDescription
assetsAssetVerifyRequest[]Required. The assets to verify.
deepbooleanAlso re-read and re-hash the bytes. Absent or false is the structural check: ask the index and the store whether the bytes are there. true additionally re-hashes them, which is the only way to catch silent corruption — stored is a question about the filesystem, and a corrupt blob is still stored. Rate-limited per account, because a deep scan reads and hashes every declared blob and an unbounded one is an I/O-amplification attack costing the caller one small JSON body. Past the budget the structural verdict still comes back and each blob’s deep reads rate_limited: throwing away a good structural answer because the optional half was throttled would make the limiter cost more than it saves.
attestation_noncestring | nullAsk for the signed verdict, bound to this nonce: 32 bytes the client drew fresh for this request, as 64 lowercase hex characters. When present the response carries attestation, the same verdicts signed under the server attestation key with this nonce inside the signature, so the client can keep the answer as evidence and a replayed answer to an earlier request is refused. Rate-limited per account from the same budget as deep. Unlike deep, a throttled signed request is refused whole with 429: a client that asked for evidence and silently got an unsigned answer would hold nothing it could later show anyone.

The POST /v1/storage/verify response.

FieldTypeDescription
verdictsStorageVerdictResponse[]Required. One verdict per requested asset, in request order.
attestationstring | nullThe signed verdict, present exactly when the request carried attestation_nonce: base64 of the canonical-CBOR StorageAttestation (capsule_core::crypto::storage_attestation). The signed bytes are the evidence; verdicts is a reading of the same answer for a client that does not need to keep it. A client verifies the signature under the pinned attestation key, checks the nonce is the one it sent and that the verdicts answer the assets and hashes it asked about, then keeps the bytes as they arrived.