gordon-risk
Purpose
gordon-risk is the portfolio-level circuit-breaker service. It watches trading.* aggregates and market_data.* (via named views) and intervenes when portfolio-level thresholds are breached. Five independent breakers evaluate on every scheduler tick: DrawdownBreaker (peak-to-trough equity), ConnectivityBreaker (gordon-data freshness), VpinBreaker (toxic-flow kill switch via live VPIN computation), MacroBreaker (FRED macro regime), and CorrelationBreaker (cross-asset correlation density). When any breaker fires, the halt-latch is set and flatten + pause commands are issued to gordon-executor and gordon-bot respectively. The emergency-flatten endpoint supports operator-initiated flattening with a drill target of under 30 seconds end-to-end. The service never touches individual orders; per-order invariants live in gordon-executor.
Version + port + env var
| Field | Value |
|---|---|
| Version | 3.4.1 |
| Port | 8082 |
| Env override | GORDON_RISK_BIND_ADDR |
| DB role | gordon_risk |
| Image | ghcr.io/dlepaux/gordon-risk |
HTTP endpoints
Health / ops
| Method | Path | Purpose |
|---|---|---|
| GET | /healthz | Liveness + degraded-state probe (breaker_eval_stale, vpin_missing, vpin_stale) |
| GET | /readyz | Readiness probe |
| GET | /metrics | Prometheus metrics |
| GET | /config | Redacted config dump |
Business endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /emergency-flatten | Operator-initiated full portfolio flatten (token-gated) |
| POST | /risk/resume | Clear halt latch after operator review (token-gated) |
| POST | /bots/{id}/pause | Pause a specific bot (column-level update on bot_configs.desired_state) |
NATS subjects
| Subject | Direction | Durable consumer |
|---|---|---|
risk.events.{breaker_lowercase} | Publishes breaker state-change events | — |
risk.commands | Publishes flatten + pause commands to executor | — |
gordon-risk does not consume NATS subjects. It receives DB state via direct Postgres reads and emits commands via both pg-NOTIFY (risk_commands channel) and NATS.
Database access
| Action | Detail |
|---|---|
| Reader | trading.* — portfolio valuation, positions, bot configs |
| Writer | trading.risk_events, trading.risk_audit_log (INSERT only) |
| Writer | trading.bot_commands (INSERT) |
| Column-level update | bot_configs.desired_state only (migration 0011 GRANT) |
| Views used | v_klines_reader (VPIN live computation from spot klines), v_metrics_reader, v_macro_reader |
| DB role | gordon_risk — least-privilege, migration 0044 |
Does not depend on gordon-data HTTP — data contract is the Postgres schema, not the service surface.
Prometheus metrics
gordon_risk_breaker_evaluations_totalgordon_risk_breaker_fires_totalgordon_risk_bot_commands_issued_totalgordon_risk_command_write_errors_totalgordon_risk_operator_flatten_requests_totalgordon_risk_operator_resume_requests_totalgordon_risk_operator_pause_bot_requests_totalgordon_risk_flatten_crashed_during_totalgordon_errors_total
Alert: rate(gordon_risk_command_write_errors_total[5m]) > 0 triggers GordonRiskCommandWriteFailed (critical).
Key env vars
| Variable | Purpose |
|---|---|
GORDON_RISK_BIND_ADDR | HTTP bind address (default :8082) |
GORDON_DATABASE_URL | Postgres connection string |
GORDON_BUS_NATS_URL | NATS JetStream URL |
GORDON_RISK_OPERATOR_TOKEN | Token required on /emergency-flatten and /risk/resume |
GORDON_RISK_DATA_URL | gordon-data healthz base URL (ConnectivityBreaker probe, default http://gordon-data:8081) |
Invariants
- Portfolio-level only. Per-order invariants live in gordon-executor. This service commands, never executes.
- Authoritative kill switch. When flatten fires, every bot pauses + every open position closes. No exceptions.
- Halt latch is a hard barrier. Full contract: halt-latch. Resume requires explicit
POST /risk/resume. - Sole writer of audit tables.
risk_events,risk_audit_log, and halt-latch columns onrisk_statewritten by gordon-risk only. bot_configswrite is column-scoped. Risk holds a column-level UPDATE grant ondesired_stateonly. Every write captured bybot_configs_audit_triggertaggedchanged_by = CURRENT_USER.- Fast flatten. Drill target: <30s end-to-end. Every flatten code path is measured.
- Single env chokepoint. Every
std::env::varcall goes throughConfig::from_env(). - No strategy knowledge. Risk does not know what a bot is trading, only that portfolio-level bounds are breached.
- DP-18 startup flatten-reconcile probe (shipped 2026-05-17). On every boot,
reconcile_orphaned_flattens()scansrisk_audit_logforflatten_requestedrows that never transitioned toflatten_completedorflatten_crashed_during, and closes them with a structured audit row. Metric:gordon_risk_flatten_crashed_during_total.
Status
Phase 4 Risk: 7/8 stories done. Five circuit breakers live. Emergency flatten escalation state machine + resume endpoint shipped. Coverage 88.33% overall (safety-critical floor 85%). One story open.
Related
- Contracts: halt-latch
- Contracts: risk-command-protocol
- Architecture: event-bus-topology
- Reference: error codes
- gordon-executor — receives flatten + pause commands
- gordon-manager — consumes
risk.events.>for WS fanout