Security
Ursula does not terminate TLS, authenticate clients, or restrict admin endpoints. Treat the listening port as fully trusted. Run it on a private network behind a reverse proxy that owns TLS termination and request authentication.
The current v0.x security model is deliberately narrow. Ursula is built to slot behind your existing edge layer, not to be one.
What Ursula does
- Quorum-acknowledged writes. An append is acknowledged only after a majority of voters has replicated it.
- Per-group backpressure. When a group's hot ring exceeds
URSULA_COLD_MAX_HOT_BYTES_PER_GROUP, appends to that group return503withRetry-Afteruntil cold flush catches up. Per-group, not global or per-client. - Stream-level isolation. Streams hash to disjoint Raft groups and disjoint owner cores. A hot stream on one group cannot starve writes on a different group on a different core.
What Ursula does not do
Handle the following outside Ursula:
- TLS / HTTPS. The public listener serves plain HTTP. No built-in
rustls. - Inter-node encryption. Peer gRPC (Raft heartbeats, append-entries, snapshots, forwarded writes) runs over h2c. Peers must share a private network.
- API authentication. No bearer-token, API-key, mTLS, or signed-request validation on the public API. Any caller with network reach can create buckets, append, read, delete.
- Authorization / multi-tenancy. No per-user, per-bucket, or per-scope ACLs. Enforce isolation upstream.
- Admin endpoint isolation.
/__ursula/metrics,/__ursula/flush-cold/*,/__ursula/raft/*, and the public stream endpoints share the same listener with no auth gate. - Per-client rate limiting. A single noisy client can saturate a core's mailbox or a group's hot ring.
- Health/readiness endpoints. No
/healthzor/readyz. Use/__ursula/metricsas a process-alive probe (it serves only after the runtime initializes). - At-rest encryption. Hot ring is in memory. WAL and Raft log directories live on disk in plaintext. Use full-disk encryption at the host level.
CORS is permissive (Access-Control-Allow-Origin: *). Restrict at the proxy for browser traffic.
Recommended deployment
Untrusted internet
│
v
┌─────────────────────┐
│ Reverse proxy │ TLS, authn, per-client
│ (nginx / Envoy / …) │ rate limiting, CORS
└──────────┬──────────┘
│ plain HTTP, private network
┌────────────┼────────────┐
v v v
┌────────┐ ┌────────┐ ┌────────┐
│ Ursula │ │ Ursula │ │ Ursula │
│ node │ │ node │ │ node │
└────────┘ └────────┘ └────────┘
↕ gRPC h2c on private network
(Raft replication)
Checklist
- Bind to the private interface.
--listen 10.0.0.X:4437or a security group / firewall so the listener is unreachable from public networks. - Terminate TLS at the proxy. Ursula stays plain HTTP on the internal side.
- Authenticate at the proxy. Validate the caller (OAuth2, mTLS, signed requests) and reject unauthenticated traffic before it reaches Ursula.
- Block admin paths from public traffic. Deny
/__ursula/*from the public listener; allow it only on an internal or ops network. - Use IAM roles for S3. Omit the static
URSULA_COLD_S3_ACCESS_KEY_ID/_SECRET_ACCESS_KEYand let the AWS SDK credential chain discover credentials. - Encrypt data volumes. Apply full-disk encryption to
--disk-path. - Keep peer traffic private. Never route gRPC peer traffic across the public internet.
Reporting vulnerabilities
Open a GitHub Security Advisory on tonbo-io/ursula rather than a public issue.