okuri

Configuration

okuri reads an optional TOML file plus the environment. Point the server at a file with okuri serve --config okuri.toml or OKURI_CONFIG=okuri.toml. The file is optional: defaults plus the environment is a supported way to run.

Precedence is defaults, then file, then environment. The environment wins because it is the layer a scheduler controls, while the file is the layer an image bakes in.

Unknown keys are rejected, so a typo fails at startup instead of silently keeping a default.

The repository ships a fully commented reference at okuri.example.toml.

[store]

key default env notes
bucket none, required OKURI_S3_BUCKET the bucket every queue lives in
endpoint provider default OKURI_S3_ENDPOINT override for MinIO, R2, and other S3-compatible stores
region none OKURI_S3_REGION the region to sign for
access_key none OKURI_S3_ACCESS_KEY leave unset with secret_key to use the provider credential chain
secret_key none OKURI_S3_SECRET_KEY see above

[server]

key default env
admin_addr 0.0.0.0:9090 OKURI_ADMIN_ADDR
grpc_addr 0.0.0.0:8080 OKURI_GRPC_ADDR
drain_deadline_ms 30000 OKURI_DRAIN_DEADLINE_MS
readiness_probe_interval_ms 5000 OKURI_READINESS_PROBE_INTERVAL_MS
receive_poll_interval_ms 500 OKURI_RECEIVE_POLL_INTERVAL_MS
receive_max_wait_ms 20000 OKURI_RECEIVE_MAX_WAIT_MS
receipt_handle_key none, required OKURI_RECEIPT_HANDLE_KEY
receipt_handle_verify_keys [] OKURI_RECEIPT_HANDLE_VERIFY_KEYS
namespace default OKURI_NAMESPACE
api_keys none, required OKURI_API_KEYS
sqs_credentials none, required OKURI_SQS_CREDENTIALS
sqs_endpoint http://localhost:8080 OKURI_SQS_ENDPOINT
sqs_region us-east-1 OKURI_SQS_REGION
sequencer_enabled false OKURI_SEQUENCER_ENABLED
sequencer_ttl_ms 15000 OKURI_SEQUENCER_TTL_MS
sequencer_renew_interval_ms 5000 OKURI_SEQUENCER_RENEW_INTERVAL_MS

The required keys

Three keys have no default and reject empty values, because these failures are better discovered at startup than at the first request.

receipt_handle_key signs receipt handles: 32 bytes in base64, from head -c 32 /dev/urandom | base64. Every node in a fleet carries the same one, so a handle issued by whichever node served the receive is honored by whichever node the load balancer sends the ack to. receipt_handle_verify_keys holds keys that no longer sign but are still honored, which is what makes key rotation possible: put the new key in receipt_handle_key, leave the old one in the verify list for at least the longest visibility timeout plus a grace period, then drop it.

api_keys are the bearer keys the native gRPC and HTTP/JSON surfaces accept, each scoped to a namespace:

[[server.api_keys]]
namespace = "default"
key = "..."

As an environment variable, OKURI_API_KEYS takes comma-separated namespace:key pairs.

sqs_credentials are the SigV4 credentials the SQS surface accepts, separate from the API keys and never derived from them:

[[server.sqs_credentials]]
namespace = "default"
access_key_id = "..."
secret = "..."

As an environment variable, OKURI_SQS_CREDENTIALS takes comma-separated namespace:access-key-id:secret triples.

Namespaces

namespace scopes both storage and signatures. Two deployments pointed at one bucket under different namespaces share the bucket and nothing else, even where they use the same queue names: messages, segments, leases, tombstones, and purge watermarks are all namespace-scoped. Namespace names follow the queue-name rule: [a-z0-9_-], at most 64 bytes.

SQS addressing

sqs_endpoint is the root of every queue URL the compat surface hands out. It is the address clients use to reach this node, which is a different thing from the address this node uses to reach its bucket: a node behind a load balancer has to advertise the balancer’s address. sqs_region is the region every ARN names. Both settings exist separately from [store] for that reason.

Per-queue configuration

Queue-level settings live in the queue’s manifest object in the bucket, not in the server config: visibility_timeout_ms, retention_ms, and max_receive_count. They are exposed through SQS GetQueueAttributes and SetQueueAttributes as well as the native API.

Queue names everywhere follow one rule: non-empty, at most 64 bytes, [a-z0-9_-] only. The -dlq suffix is reserved, so a queue that wants a reachable dead-letter queue is limited to 60 bytes of name.