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.

bucketpathstringrequired

Bucket ID.

streampathstringrequired

Stream ID.

offsetpathstringrequired

Stream 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-Typeheaderstring

Content type of the snapshot blob, stored separately from the stream's own content type. Defaults to application/octet-stream.

bodybodybinaryrequired

The 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-Incarnationheaderstring

Optional 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

StatusMeaning
204Snapshot published successfully.
400Invalid offset or content type, or, on a JSON stream, an offset inside a message.
404Stream not found or expired.
409Stale publish (a newer snapshot exists) or offset out of range.
410Snapshot offset is older than the retained stream history.
412Stream-Incarnation did not match: the stream was deleted and recreated. Nothing was published; the response's Stream-Incarnation is the current token.
413Snapshot body exceeds the maximum allowed size (32 MiB inline, 1 GiB in the cold tier).
502The cold backend failed while storing the body.
503JSON 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.