Clients

Ursula speaks plain HTTP and Server-Sent Events. There is no required client library. Any HTTP client in any language works.

The examples elsewhere in these docs use curl because it's universal. The same routes, headers, and query parameters apply to every other client.

Minimal examples

# create a bucket and stream
curl -X PUT http://127.0.0.1:4437/demo
curl -X PUT http://127.0.0.1:4437/demo/hello

# append
curl -X POST http://127.0.0.1:4437/demo/hello \
  -H 'Content-Type: application/octet-stream' \
  --data-binary 'hello world'

# catch-up read
curl 'http://127.0.0.1:4437/demo/hello?offset=-1'

# live tail
curl 'http://127.0.0.1:4437/demo/hello?offset=-1&live=sse'

Notes for client implementers

  • After every read and append, the server returns Stream-Next-Offset. Track it. Use it as the offset query parameter on the next read. Don't construct offsets manually. They're opaque.
  • For binary streams over SSE, data events carry raw base64 text and the response includes Stream-Sse-Data-Encoding: base64. Decode the data event first, then interpret the bytes using Stream-Data-Content-Type. See Binary SSE.
  • For exactly-once writes, send Producer-Id, Producer-Epoch, Producer-Seq headers and retry on network errors. The server deduplicates. See Exactly-once writes.
  • For conditional writes, use Stream-Seq with a monotonically increasing token. (If-Match is part of the Durable Streams Protocol but is not yet implemented in Ursula.) See Conditional writes.