Publish snapshot
Publishes a new checkpoint blob at the given offset. The snapshot replaces any previously published snapshot, but does not delete stream history. Retention advances only through the separate /retention endpoint.
bucketpathstringrequiredBucket ID.
streampathstringrequiredStream ID.
offsetpathstringrequiredStream offset this snapshot represents: the 20-character zero-padded decimal token.
For an application/json stream the offset must be a message boundary: a Stream-Next-Offset that Ursula returned, or any offset just after a message's LF.
Content-TypeheaderstringContent type of the snapshot blob, stored separately from the stream's own content type. Defaults to application/octet-stream.
bodybodybinaryrequiredThe snapshot blob bytes. Bodies of up to 32 MiB are stored inline in the stream's replicated state. On a node with a cold backend, a body of at least runtime.external_payload_min_size (1 MiB by default) is streamed into the cold tier instead, and bodies of up to 1 GiB are accepted. Larger bodies return 413.
Stream-IncarnationheaderstringOptional precondition: the stream's incarnation token, as a previous response's Stream-Incarnation header returned it. The request applies only if the stream is still that incarnation, checked atomically with the write; otherwise it answers 412 and changes nothing. Without the header, the request behaves exactly as described here. A malformed value answers 400. See Stream-Incarnation.
Response
| Status | Meaning |
|---|---|
204 | Snapshot published successfully. |
400 | Invalid offset or content type, or, on a JSON stream, an offset inside a message. |
404 | Stream not found or expired. |
409 | Stale publish (a newer snapshot exists) or offset out of range. |
410 | Snapshot offset is older than the retained stream history. |
412 | Stream-Incarnation did not match: the stream was deleted and recreated. Nothing was published; the response's Stream-Incarnation is the current token. |
413 | Snapshot body exceeds the maximum allowed size (32 MiB inline, 1 GiB in the cold tier). |
502 | The cold backend failed while storing the body. |
503 | JSON stream only: Ursula could not read the byte before the offset to check the boundary. Nothing was published; retry. |
Response headers include Stream-Next-Offset, Stream-Snapshot-Offset, Stream-Snapshot-Digest, and Stream-Incarnation. Repeating the same offset, content type, and body is idempotent and returns the same digest. Reusing an offset with different content returns 409.
curl -X PUT 'http://127.0.0.1:4437/demo/hello/snapshot/00000000000000000042' \
-H 'Content-Type: application/json' \
--data-binary '{"state": "aggregated snapshot data"}'Advance retention
PUT /{bucket}/{stream}/retention/{offset} explicitly discards history before a published checkpoint. The boundary must be monotonic, no newer than the visible checkpoint, and, on a JSON stream, a message boundary (400 otherwise, 503 when it cannot be checked). Retention is exact: the response's Stream-Retained-Offset is the requested offset.
After a successful 204, reads before the retained offset return 410 Gone. This separate step lets clients publish and verify a checkpoint before making history unreachable.
Snapshots are immutable at a given offset. A byte-identical retry is idempotent; a different body at the same offset conflicts. Publishing does not advance retention.