Skip to Content
DeploymentConfiguration

Configuration

topics is configured entirely from the environment — there is no config file. This page is the complete reference: every TOPICS_* variable plus RUST_LOG, with its exact default and what it controls. Unset variables fall back to the defaults shown here.

Every TOPICS_MAX_* limit accepts 0 to mean unlimited. Exceeding a configured cap returns 429 throttled with the offending bound in error.detail.limit. See Resource limits below and Errors.

Networking & auth

Where the server binds, whether auth is on, and which keys are accepted. The default bind is loopback, so an unconfigured server is never accidentally public.

Env varDefaultMeaning
TOPICS_HOST127.0.0.1Bind host (loopback by default). May also be a full host:port — in which case it carries the port and TOPICS_PORT is ignored.
TOPICS_PORT4000Listen port. Used unless TOPICS_HOST is a full address.
TOPICS_API_KEYS(unset)Comma-separated bearer keys, hashed SHA-256 at rest. Each entry is key | key:scopes | key:scopes:prefixes | key::prefixes. Unset ⇒ auth disabled (dev mode).
TOPICS_ALLOW_INSECURE_NO_AUTH0Required to start on a non-loopback bind with no keys — otherwise the server refuses to start. Local/dev only.
TOPICS_PROBE_AUTHfalseWhen true, require auth on the /v0/health, /v0/ready, and /v0/metrics probes too. By default probes skip auth.

On a non-loopback bind with no TOPICS_API_KEYS, the server refuses to start unless TOPICS_ALLOW_INSECURE_NO_AUTH=1 — it would otherwise be an open, unauthenticated event store. Never set that flag on a network-exposed deployment. The key entry syntax (scopes and the topic-name prefix allowlist) is documented in full on the Security page.

A bare key grants full access. The scopes field draws from read/write/delete/admin (single-letter aliases r/w/d/a, plus rw); an empty scopes field means all scopes. The prefixes field is a topic-name prefix allowlist. A malformed scope token makes the server refuse to start (fail-closed). See Security for the per-route scope requirements and how the allowlist is enforced on paths, request bodies, and list results.

Storage & tiering

Where durable state lives, when segments are sealed, and the optional cold tier. TOPICS_DATA_DIR is the hot-tier root; the segment-sealing knobs control segment rotation; the hot-retention knobs govern when sealed segments relocate to cold storage.

Env varDefaultMeaning
TOPICS_DATA_DIR./topics-dataRoot for the WAL, segments, and snapshots (the hot tier). Replayed on start; a missing/empty dir is a fresh start.
TOPICS_WAL_SHARDSmin(num_cpus, 8)Number of independent WAL shards (each its own writer thread, file set, and group-commit loop). Each topic maps to one shard by a stable hash of its id, so per-topic ordering + durability still hold; recovery is shard-count-agnostic (replays all shards by topic_id), so this may change between restarts. 1 = the flat single-writer layout.
TOPICS_COLD_DIR(unset)Cold-tier directory. Set ⇒ sealed segments past the hot-retention bound relocate here off the hot path. Unset ⇒ tiering disabled (everything stays hot).
TOPICS_SEGMENT_MAX_EVENTS10000Seal the active segment after this many records.
TOPICS_SEGMENT_MAX_BYTES67108864 (64 MiB)Seal after this many .data bytes.
TOPICS_SEGMENT_MAX_AGE_MS3600000 (1 h)Seal an idle/partial segment after this age. 0 disables age-based sealing.
TOPICS_HOT_RETAIN_SEGMENTS4Number of newest sealed segments kept hot before relocating older ones to cold.
TOPICS_HOT_RETAIN_BYTES0 (off)Optional hot sealed-byte bound. When both this and TOPICS_HOT_RETAIN_SEGMENTS are set, the stricter of the two wins.

Tiering is opt-in: with TOPICS_COLD_DIR unset, every segment stays in the hot tier and the retention knobs have no effect. Cold reads run on a separate I/O pool and never block writes or live SSE delivery. See Storage & Tiering.

Resource limits

DoS-hardening caps. Each is a guardrail with a sane default; 0 means unlimited, and crossing a cap returns 429 throttled carrying the bound in error.detail.limit.

Env varDefaultMeaning
TOPICS_MAX_BODY_BYTES67108864 (64 MiB)Maximum request body size.
TOPICS_MAX_TOPICS100000Maximum number of topics.
TOPICS_MAX_ROUTERS10000Maximum number of routers.
TOPICS_MAX_WATCH_SESSIONS10000Maximum live watch sessions.
TOPICS_MAX_SSE_CONNECTIONS10000Maximum concurrent SSE connections, server-wide.
TOPICS_MAX_SSE_CONNECTIONS_PER_KEY1000Maximum concurrent SSE connections per key.
TOPICS_MAX_INFLIGHT_PER_KEY1000Maximum concurrent in-flight requests per key.
TOPICS_MAX_TOTAL_BYTES0 (unlimited)Global quota on total retained record bytes across all topics. A write past it ⇒ 429 throttled.

An over-cap request returns 429 throttled, distinct from the elastic throttling the scheduler applies under CPU pressure (which adds detail.retry_after_ms instead). A cap rejection is a hard limit; elastic throttling degrades latency, never correctness. See Errors.

Logging

Env varDefaultMeaning
RUST_LOGinfoTracing filter. Standard Rust tracing syntax (e.g. RUST_LOG=topics=debug,info) — not topics-specific.

A worked example

A production-style invocation that binds a non-loopback address, turns auth on, points at a dedicated data directory with a cold tier, and tightens a couple of limits:

TOPICS_HOST=0.0.0.0 \ TOPICS_PORT=4000 \ TOPICS_API_KEYS="prod-key:rw:tenant42:,admin-key:a" \ TOPICS_DATA_DIR=/var/lib/topics \ TOPICS_COLD_DIR=/mnt/cold/topics \ TOPICS_MAX_TOPICS=50000 \ TOPICS_MAX_TOTAL_BYTES=10737418240 \ ./target/release/topics

Run this behind a TLS-terminating reverse proxy — topics speaks plain HTTP. The same variables apply to the container image; see Docker.

See also

  • Security — the key entry syntax, scopes, the prefix allowlist, and the resource model.
  • Storage & Tiering — the data directory, segment sealing, and hot→cold relocation.
  • Running topics — start, recovery, binding, and graceful shutdown.
  • Docker — passing these variables to the published image.
Last updated on