gordon-executor
Purpose
gordon-executor is the sole exchange integration point in the v7 stack. It holds the only trading-capable Binance API keys, consumes OrderIntent rows from the bus, enforces per-order safety invariants, submits orders to Binance (live or testnet), tracks fills via dual user-data WebSockets, and reconciles state on every startup to detect orphan orders and exchange divergence. It is the only service that can place or cancel orders. Portfolio-level risk commands (flatten, pause) arrive from gordon-risk via NATS risk.commands and trigger the FlattenExecutor pipeline.
Version + port + env var
| Field | Value |
|---|---|
| Version | 5.3.0 |
| Port | 8085 |
| Env override | GORDON_EXECUTOR_BIND_ADDR |
| DB role | gordon_executor |
| Image | ghcr.io/dlepaux/gordon-executor |
HTTP endpoints
Health / ops
| Method | Path | Purpose |
|---|---|---|
| GET | /healthz | Liveness + degraded-state probe (returns {"status":"degraded","reason":"..."} on soft failures) |
| GET | /readyz | Readiness — only releases after reconcile completes for ALL networks |
| GET | /metrics | Prometheus metrics |
| GET | /config | Redacted config dump |
Business endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /orders | List orders (paginated) |
| GET | /orders/{intent_id} | Single order by intent ID |
| GET | /positions | Current open positions |
| GET | /status | Executor status summary |
| POST | /flatten | Trigger flatten (risk-command path — not operator-direct) |
| POST | /clear-quarantine | Clear quarantine state after operator review |
| POST | /executor/break-glass/flatten | Break-glass flatten (emergency, bypasses standard pipeline) |
NATS subjects
| Subject | Direction | Durable consumer |
|---|---|---|
intents.executor | Consumes OrderIntent payloads from gordon-bot | executor-default (blue) / executor-shadow-<deploy_id> (shadow during green/blue) |
risk.commands | Consumes flatten + pause commands from gordon-risk | executor-risk |
trading.fills.{bot_id} | Publishes fill events after every confirmed fill | — |
Fill events are also written via pg-NOTIFY on order_intents / bot_events channels. Both paths fire on every fill — NATS is additive (story 08a, 2026-05-14).
Database access
| Action | Detail |
|---|---|
| Writer | trading.orders, trading.trades, trading.order_events, trading.fill_events |
| Outbox | bus.outbox INSERT + UPDATE (drain path for NATS dual-write) |
| Fence | EXECUTE on increment_fence function |
| DB role | gordon_executor — least-privilege, migration 0044 |
Does not read market_data.* directly. Last/mark price comes from the Binance exchange client.
Prometheus metrics
executor_intents_totalexecutor_orders_placed_totalexecutor_orders_filled_totalexecutor_orders_rejected_totalexecutor_reconcile_duration_secondsexecutor_reconcile_anomalies_totalexecutor_flatten_totalexecutor_flatten_runs_totalgordon_executor_daily_notional_used_usdgordon_executor_daily_notional_used_usd_globalgordon_executor_daily_notional_reset_ts_secondsgordon_executor_daily_notional_would_reject_totalgordon_executor_daily_notional_rejected_totalgordon_errors_total
Key env vars
| Variable | Purpose |
|---|---|
GORDON_EXECUTOR_BIND_ADDR | HTTP bind address (default :8085) |
GORDON_DATABASE_URL | Postgres connection string |
GORDON_BUS_NATS_URL | NATS JetStream URL |
BINANCE_API_KEY / BINANCE_API_SECRET | Prod trading-capable keys |
BINANCE_TESTNET_API_KEY / BINANCE_TESTNET_API_SECRET | Testnet keys |
GORDON_EXECUTOR_MAX_DAILY_NOTIONAL_USD_PER_BOT | DP-01 per-bot daily cap (omit = warn-only) |
GORDON_EXECUTOR_MAX_DAILY_NOTIONAL_USD_GLOBAL | DP-01 global daily cap (omit = warn-only) |
Invariants
- Sole holder of trading keys. Any other service that places orders is a bug. Vault slot:
gordon-executor. - SL is mandatory. No order leaves without an attached stop-loss. Intents without SL are rejected at the invariant layer.
- Risk halt is a hard barrier. Every fresh intent is gated on the halt latch before submission. Contract: halt-latch.
- Startup probes (all networks, fatal on failure):
canWithdraw=falseon prod keys (testnet excluded — endpoint behaviour verified 2026-05-12).canTrade=trueon both networks.dualSidePosition=false(hedge mode corrupts reconcile).
- Reconcile-before-readyz ordering.
/readyzis not released untilquarantine.seed_from_outcomes()completes for ALL networks, not just the first finisher. - Idempotency on retry. Duplicate
intent_idis a no-op — no duplicate order. - Fill dedup by content-hash.
trade_fingerprintis the dedup key, notexchange_trade_id. Full contract: fill-tracker-contract. FlattenExecutor::run()always emits a structured completion log. A silent flatten dispatch is a defect.- Dual-network isolation. Prod and testnet keys are distinct credentials; intent
networkfield routes correctly. - DP-01 daily notional caps.
GORDON_EXECUTOR_MAX_DAILY_NOTIONAL_USD_PER_BOTandGORDON_EXECUTOR_MAX_DAILY_NOTIONAL_USD_GLOBALgate orders by UTC-day rolling notional. Unset = warn-only mode (logswould_rejectcounter but does not block).
Status
Phase 3 Executor: 10/10 stories done (2026-04-16). Backend close-out audit passed (2026-04-24). Coverage ≥85% on safety-critical modules (invariants, submission, reconcile, flatten). Stable.
Related
- Contracts: halt-latch
- Contracts: risk-command-protocol
- Architecture: event-bus-topology
- Reference: error codes
- gordon-bot — intent producer
- gordon-risk — issues flatten + pause commands