NATS Subject Taxonomy
choco intermediate 4 min read
ELI5
Every event the factory emits goes onto a labelled conveyor belt. The label format is choco.events.<room>.<action>. If you want everything from the Fudge Room you subscribe to choco.events.coordination.>; if you only want conflict events you narrow it down.
Technical Deep Dive
Subject names are documented in the per-event proto comments under proto/events/<domain>/v1/*.proto. The pattern is choco.events.<domain>.<action>, with domain matching the proto package’s middle segment.
Domain Inventory
flowchart LR subgraph streams["JetStream Streams"] onb[choco:events:onboarding] mon[choco:events:monetization] coord[choco:events:coordination] docu[choco:events:documentation] hyd[choco:events:hydration] ref[choco:events:refinement] res[choco:events:resolution] end onb -->|onboarding.started, site.provision.*| C1[cho-co-web SSE] mon -->|golden_ticket.issued| C2[airlock + choco-ledger] coord -->|property_sync.*, conflict.*| C3[the-conch] docu -->|page.generated, content.published| C4[choco-search, analytics] hyd -->|hydration.*| C5[the-mould] ref -->|slop_detected, smartblock_created| C6[choco-consumers] res -->|tag_resolved, cross_repo_link_verified| C7[the-wrapper]Ordering Disciplines
Two values appear in the Ordering: comment annotation across protos:
causally_ordered(per-aggregate) —OnboardingStartedand most domain events (onboarding/v1/lifecycle.proto:11). Order is preserved within oneaggregate_id; cross-aggregate order is not guaranteed.globally_ordered—GoldenTicketIssuedandGoldenTicketRedeemed(monetization/v1/golden.proto:51-52,:65-66). Backed by a single-writer constraint (advisory lock — seechoco-008).
Subject ↔ Service Map
Each factory-equipment service owns one subject prefix:
| Service | Subject prefix | Source |
|---|---|---|
| the-winnower | choco.events.refinement.> | services/the-winnower/README.md:9 |
| the-conch | choco.events.coordination.> | services/the-conch/README.md:9 |
| the-mould | choco.events.hydration.> | services/the-mould/README.md:9 |
| the-wrapper | choco.events.resolution.> | services/the-wrapper/README.md:9 |
Retention
monetization events involving Golden Tickets carry Retention: infinite (golden.proto:52). Other domains do not declare retention in the proto and inherit the JetStream default configured at infrastructure time (not in this repo).
Key Terms
- subject → NATS hierarchical topic name; dotted, single-token wildcard
*, multi-token wildcard>. - stream → JetStream’s persisted, replayable view over one or more subjects.
- aggregate ordering → events for the same
aggregate_idare delivered in publish order; the proto’sOrdering:line is the contract.
Q&A
Q: Why is golden_ticket.issued globally_ordered instead of causally_ordered?
A: The five-ticket cap is a system-wide invariant (golden.proto:53-54), so issuance must serialise across all writers — not just per-user.
Q: What does the wildcard > do?
A: Matches the rest of the subject path (any depth). choco.events.coordination.> catches property_sync.started, conflict.detected, etc.
Q: Where is the literal subject string for OnboardingStarted defined?
A: It is documented in the proto comment (Stream: choco:events:onboarding) but the actual nats.Publish(subject, ...) call lives in the producer service, not the proto.
Examples
the-conch subscribes to choco.events.coordination.> to consume sync requests, then re-publishes conflict.detected and conflict.resolved on the same prefix. Stream name uses colons (choco:events:coordination) per JetStream convention; subject names use dots.
neighbors on the map
- EventEnvelope Wire Wrapper publishing a new domain event proto
- Factory Equipment Services deciding which factory service should own a new pipeline step
- NATS Event Bridge subscribing a Choco service to STRATT lifecycle events
- CI Transition Event Schema vendoring kahn_emit.py into a CI producer