Quick Start
For now, Ursula builds from Rust source. Pre-built release binaries are on the way.
Start a local node from the workspace root. The default is in-memory (no on-disk state, good for kicking the tires):
cargo run --bin ursula
It binds 127.0.0.1:4437, picks a core count from your CPU, and uses the in-memory engine. For local disk-backed persistence instead, point --disk-path at an empty directory:
cargo run --bin ursula -- --storage-backend disk --disk-path ./data
Then drive the HTTP API with curl in another terminal.
Acknowledge the bucket
Bucket creation in the current build is an idempotent acknowledgement - Ursula returns 201 whether or not the name has been seen before. It's still worth issuing because clients and docs assume the call:
curl -X PUT http://127.0.0.1:4437/demo
Create a stream
curl -X PUT http://127.0.0.1:4437/demo/hello
Append data
curl -X POST http://127.0.0.1:4437/demo/hello \
-H 'Content-Type: application/octet-stream' \
--data-binary 'first message'
curl -X POST http://127.0.0.1:4437/demo/hello \
-H 'Content-Type: application/octet-stream' \
--data-binary 'second message'
Each successful append returns 204 No Content with a Stream-Next-Offset header. Add Producer-Id / Producer-Epoch / Producer-Seq if you need exactly-once retries.
Read everything from the beginning
curl -i 'http://127.0.0.1:4437/demo/hello?offset=-1'
The body contains the appended bytes. Response headers include Stream-Next-Offset, Stream-Up-To-Date, and an ETag.
Subscribe for live updates
Open a second terminal:
curl -N 'http://127.0.0.1:4437/demo/hello?offset=-1&live=sse'
-N keeps curl from line-buffering the SSE stream. Append more data from the first terminal and you'll see it arrive immediately as event: data lines. Binary streams are delivered as a JSON envelope with a base64-encoded payload (Stream-Sse-Data-Encoding: base64). See binary SSE for details.
Inspect runtime state
For a multi-node cluster the canonical day-2 tool is ursulactl — it speaks to every node, summarises leadership, and is what you'll use for rolling restarts. For a single local node you can either point it at a one-line manifest:
cat > /tmp/local.json <<'JSON'
{"nodes": [{"id": 1, "http_url": "http://127.0.0.1:4437", "host": "127.0.0.1"}]}
JSON
ursulactl status --config /tmp/local.json
Or hit the underlying JSON endpoint directly:
curl http://127.0.0.1:4437/__ursula/metrics
The raw endpoint is also what ursulactl consumes; reach for it when you want the full snapshot or are building custom tooling.
Next steps
- ursulactl: the operator CLI you'll use once a cluster is up
- Run locally: flags, environment variables, and durability modes
- Deploy a cluster: static gRPC Raft cluster shape
- Configure S3: shared cold storage for clusters
- API overview: the HTTP surface Ursula currently exposes
- Streams: the core stream abstraction and lifecycle
- Architecture: thread-per-core, multi-Raft internals