Storage verification
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/assets/{asset_id}/receipts
Section titled “GET /v1/assets/{asset_id}/receipts”Fetch every custody receipt covering one asset.
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. |
asset_id | path | string | Required. The asset id. |
| Status | Body | Description |
|---|---|---|
200 | AssetReceiptsResponse 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; 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. |
404 | CodedProblem application/problem+json | Not found. 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. |
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/storage/verify
Section titled “POST /v1/storage/verify”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.
| 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): StorageVerifyRequest
| Status | Body | Description |
|---|---|---|
200 | StorageVerifyResponse 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. |
429 | StorageVerifyRateLimitedProblem 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. |
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.
AssetReceipt
Section titled “AssetReceipt”One custody receipt, decoded, beside the bytes that were signed.
| Field | Type | Description |
|---|---|---|
receipt_seq | integer | Required. Strictly monotonic per server. The chain position this receipt cannot be moved from. |
server_id | string | Required. This server’s canonical origin — what binds the receipt to one server. |
server_key_id | string | Required. The attestation key fingerprint that signed, hex. Survives rotation, which is why the key is named rather than assumed. |
prior_receipt_hash | string | null | SHA-256 of the previous receipt in the server’s log, hex. Absent for the first receipt this server ever issued. |
upload_id | string | Required. The upload session that produced custody. |
blob_role | string | Required. original, derivative, metadata or provenance. |
ciphertext_hash | string | Required. The server-recomputed ciphertext content address, hex. |
size | integer | Required. Ciphertext size in bytes. |
envelope_hash | string | null | SHA-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_at | string | Required. The server’s trusted clock at the finalization commit, RFC 3339. |
receipt_cbor | string | Required. 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. |
AssetReceiptsResponse
Section titled “AssetReceiptsResponse”The chain.
| Field | Type | Description |
|---|---|---|
asset_id | string | Required. The asset the chain belongs to, echoed so a client batching requests can tell the answers apart. |
receipts | AssetReceipt[] | Required. Every receipt covering the asset, in receipt_seq order. |
AssetVerifyRequest
Section titled “AssetVerifyRequest”One asset to verify, with the exact copies the client is relying on.
| Field | Type | Description |
|---|---|---|
asset_id | string | Required. The asset. |
blob_hashes | string[] | 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. |
BlobVerdictResponse
Section titled “BlobVerdictResponse”One declared blob’s verdict.
| Field | Type | Description |
|---|---|---|
hash | string | Required. The address, as the client declared it. |
role | string | Required. The role the asset holds it under — unknown for a hash the asset does not hold. |
stored | boolean | Required. The bytes are present at that address. |
indexed | boolean | Required. A live asset of the caller’s references the address. |
retrievable | boolean | Required. Nothing is withholding it. |
deep | string | null | What 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. |
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. |
StorageVerdictResponse
Section titled “StorageVerdictResponse”One asset’s verdict.
| Field | Type | Description |
|---|---|---|
asset_id | string | Required. The asset the client asked about. |
durable | boolean | Required. 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. |
blobs | BlobVerdictResponse[] | Required. One entry per declared hash, in declaration order and never shortened. |
checked_at | string | Required. The server’s own clock at verification, RFC 3339. Never the client’s. |
StorageVerifyRateLimitedProblem
Section titled “StorageVerifyRateLimitedProblem”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. |
retry_after | integer | When 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. |
StorageVerifyRequest
Section titled “StorageVerifyRequest”The POST /v1/storage/verify body.
| Field | Type | Description |
|---|---|---|
assets | AssetVerifyRequest[] | Required. The assets to verify. |
deep | boolean | Also 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_nonce | string | null | Ask 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. |
StorageVerifyResponse
Section titled “StorageVerifyResponse”The POST /v1/storage/verify response.
| Field | Type | Description |
|---|---|---|
verdicts | StorageVerdictResponse[] | Required. One verdict per requested asset, in request order. |
attestation | string | null | The 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. |