NATS Subjects
All events flow through a single JetStream stream: gordon-bus.
Stream configuration
| Property | Value |
|---|---|
| Stream name | gordon-bus |
| Retention | 168h (7 days) |
| Max bytes | 768 MiB |
| Storage | File |
| Subjects | market.klines.>, intents.>, risk.>, trading.> |
Subject registry
| Subject pattern | Producer | Consumer(s) | Durable consumer name | Notes |
|---|---|---|---|---|
market.klines.binance.{spot|perp}.{symbol_lc}.{tf} | gordon-data | gordon-bot | bot-{bot_id}-{symbol_lc}-{tf}-klines | Lowercase symbol in subject (e.g. btcusdt). 1m canonical; higher TFs also published. |
intents.executor | gordon-bot | gordon-executor | executor-default | OrderIntentEvent payloads |
risk.commands | gordon-risk | gordon-executor | executor-risk | Flatten + pause commands from circuit breakers |
risk.events.{breaker_lowercase} | gordon-risk | gordon-manager | manager-risk | Breaker trip events. {breaker_lowercase} examples: drawdown, connectivity, vpin, macro, correlation |
trading.fills.{bot_id} | gordon-executor | (consumers TBD — browser via NATS-WS) | — | Fill events per bot. {bot_id} uses underscores (hyphens replaced). |
Schema versioning invariant
Every payload carries schema_version: u16 as the first field. Evolution is additive only — new optional fields may be appended; existing fields may not be renamed or removed. Consumers must tolerate unknown fields.
If a breaking change is unavoidable, the version number increments and a migration window is required before old producers are retired.
Dual-write / outbox pattern
Gordon-bus uses an outbox pattern for durability:
- Producers (gordon-data, gordon-executor, gordon-risk) write to
bus.outboxwithin the same SQL transaction as the primary write. - A leader-elected drain worker reads the outbox and publishes to NATS JetStream.
- NATS provides at-least-once delivery with dedup via
Nats-Msg-Id.
This means a service crash between the SQL commit and the NATS publish does not lose the event — the drain worker picks it up on the next pass.
Case convention
- Bus payloads on NATS —
snake_case(Rust serde default). - Browser-bound JSON (REST responses, WS from manager
/ws) —camelCase. - Conversion happens at the edge (gordon-manager WS broadcaster) — never in the producer.
Consumer durable naming
Bot consumers follow the pattern bot-{bot_id}-{symbol_lc}-{tf}-klines. The bot_id is a UUIDv7. This ensures each bot gets its own durable cursor in the stream even across restarts.
Executor durables (executor-default, executor-risk) are service-scoped — a single durable per logical consumer role, not per bot.