ADR 0003 — Bus: NATS JetStream
Status: Accepted
Date: 2026-05-05
Context
Agent ↔ agent messaging, presence/heartbeat, event streams (artefact.created, artefact.linked). Throughput modest (10–1000 msg/s); durability needed for inbox; latency should be sub-millisecond intra-stack.
Decision
NATS 2.10 with JetStream, running as a dedicated nats container in the Manthan stack.
Subjects:
manthan.bus.broadcast— town square; all agents see.manthan.bus.persona.<name>.inbox— private inbox per persona.manthan.bus.persona.<name>.outbox— observable outbox per persona.manthan.bus.events.artefact.*— substrate-driven events.manthan.bus.presence— heartbeat / status changes.
JetStream streams persist 24 h or 100k messages, whichever comes first. Long-term churn lives in the substrate; the bus carries now.
Consequences
- Familiar to the team. Maestro-platform already uses NATS (port 24222); the build swarm knows the operational shape.
- Subject hierarchy fits naturally. Wildcards (
manthan.bus.persona.*.outbox) make the viewer trivial. - JetStream durability gives us at-least-once delivery for inbox messages and replay for the viewer's "join late and see history" use case.
- Bounded resources. Streams capped at 100k msgs / 4 GB store / 24 h age — agents cannot fill the disk by accident.
- Manthan runs its own NATS. No shared topology with maestro-platform — the principle of total independence holds. Different host port (24322) so dev machines run both side by side.
Alternatives considered
- Redis Streams. Lower ops familiarity in this codebase; lacks JetStream's first-class subject hierarchy.
- Kafka. Massive over-engineering for this scale; ZooKeeper/Kraft adds an ops surface we don't need.
- Postgres LISTEN/NOTIFY. Tempting (zero new infra), but lacks durability and history; viewer's "show me the last 100 messages" use case is awkward.
- In-process pub/sub. Defeats the multi-service architecture and rules out cross-host scale-out later.