Files
rutster/docs/adr/0005-event-bus.md
Aaron D. Lee bdadfd9057
All checks were successful
CI / fmt (push) Successful in 1m36s
CI / clippy (push) Successful in 2m21s
CI / test (1.85) (push) Successful in 5m3s
CI / test (stable) (push) Successful in 4m23s
CI / deny (push) Successful in 1m35s
slice-5: scalability seams — addressing, admission, drain, events (review B1/M1-M7) (#14)
Co-authored-by: Aaron D. Lee <himself@adlee.work>
Co-committed-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 04:35:38 +00:00

3.6 KiB

ADR-0005 — Event bus & state store: Valkey

  • Status: Accepted
  • Date: 2026-06
  • Closes: the event-bus open-decision carried in the founding docs (NATS vs. Kafka vs. Redis Streams)

Context

The fused per-call core (ADR-0002) needs:

  • a bus for cross-service control events (replacing Asterisk's internal Stasis bus for anything crossing the boundary),
  • a KV state store replacing astdb + sorcery / realtime config state,
  • presence sets for MWI/BLF signaling aggregation.

The founding docs listed the choice as NATS vs. Kafka vs. Redis Streams.

Decision

Valkey — the Linux-Foundation BSD-3-Clause fork of Redis 7.2.4 (wire-protocol-compatible drop-in).

Why not "Redis"

Redis relicensed in March 2024 to RSALv2 + SSPLv2not OSI-open-source, and license-incompatible with this project's posture (FOSS-clean + GPL-3.0-or-later per ADR-0004). Recommending "Redis" undercuts the data-ownership pillar (ADR-0002) and the clean-license story. Valkey is Redis-the-thing with a compatible license, maintained by the community (AWS, Google, Oracle, et al.).

Why Valkey fits the wedge specifically

  • Collapses bus + state store + presence into one dependency. A solo operator runs one process with compose up. NATS is a cleaner pure-bus but doesn't also replace astdb/sorcery; Kafka is structural overkill at self-hosting scale and violates the one-binary / operational-simplicity pillar.
  • One tool, three roles:
    • streams + consumer groups → CDR/CEL/analytics pipeline fanout and replay,
    • pub/sub → presence / MWI / BLF signaling,
    • KV → state store (config + runtime state replacing astdb/sorcery).

Alternatives

  • Kafka — rejected. Structural overkill at self-hosting scale; operational weight violates the operational-simplicity pillar.
  • NATS — retained as a config-pluggable alternative. An operator outgrowing Valkey (notably needing NATS JetStream's durable streaming model at larger scale) can swap it in. The bus backend seam stays pluggable at the config boundary, not architecture-load-bearing.

Constraints (load-bearing, not preferences)

  1. The 20ms media loop never rides the bus. Media timing stays in-core on dedicated timing threads (ADR-0002 §7; vision-revision §7). The bus carries control / cross-service events only — not latency-pinned media — so Valkey's sub-ms local latency is plenty.
  2. The bus is NOT the source of truth for billing- or call-loss-critical state. Valkey persistence (RDB/AOF) is async-ish — fine for transport / replay / fleeting retention, wrong for "the CDR that proves what we billed." CDR and recordings emit durably to object storage in their own services; the bus only flows events into that pipeline and lets services react.
  3. Constraint 2 governs the durable CDR, not enforcement counters. The spend gate's live accounting state (spend/pacing/rate counters) is ephemeral control state and DOES belong in Valkey KV at N>1 — see the ADR-0009 amendment 2026-07-04.

Consequences

  • Positive: one OSS dep for bus + state + presence; self-hostable and license-clean; pluggable to NATS later without architecture changes; aligns with operational-simplicity.
  • Negative: Valkey is "good enough" at each role, not best-of-breed for each — accepted deliberately for the ops-simplicity pillar.