Skip to content

NATS Subjects

All events flow through a single JetStream stream: gordon-bus.

Stream configuration

PropertyValue
Stream namegordon-bus
Retention168h (7 days)
Max bytes768 MiB
StorageFile
Subjectsmarket.klines.>, intents.>, risk.>, trading.>

Subject registry

Subject patternProducerConsumer(s)Durable consumer nameNotes
market.klines.binance.{spot|perp}.{symbol_lc}.{tf}gordon-datagordon-botbot-{bot_id}-{symbol_lc}-{tf}-klinesLowercase symbol in subject (e.g. btcusdt). 1m canonical; higher TFs also published.
intents.executorgordon-botgordon-executorexecutor-defaultOrderIntentEvent payloads
risk.commandsgordon-riskgordon-executorexecutor-riskFlatten + pause commands from circuit breakers
risk.events.{breaker_lowercase}gordon-riskgordon-managermanager-riskBreaker 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:

  1. Producers (gordon-data, gordon-executor, gordon-risk) write to bus.outbox within the same SQL transaction as the primary write.
  2. A leader-elected drain worker reads the outbox and publishes to NATS JetStream.
  3. 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 NATSsnake_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.

Gordon — keep compounding without blowing up