Project Structure
Gordon v7 is a split topology. The workspace contains sibling repositories, each with a hard ownership boundary. There is no monolith.
Sibling repositories
Library crates (published to kellnr)
| Repo | Version | Role |
|---|---|---|
gordon-kernel | 2.0.0 | Error taxonomy (GordonError, ErrorKind, ErrorCode) + W3C TraceId. Frozen-ish leaf. No I/O. |
gordon-domain | 1.1.0 | Cross-service business types and the ExecutionModel trait. Churns with business changes. No I/O. |
gordon-protocol | 7.2.1 | NATS bus subjects + payload structs. Every payload carries schema_version: u16. Additive-only evolution. |
gordon-platform | 0.4.0 | Runtime libs: HTTP client + circuit breaker, Axum trace middleware, tracing-subscriber init, IPC. Features: http, http-middleware, telemetry, ipc. |
gordon-strategy | 4.6.4 | Strategy trait, MarketContext, Signal, 14 entry strategies, overlays, BacktestExecution. Pure math. No I/O. |
gordon-bus | 2.1.0 | Publisher + Consumer traits over NATS JetStream + Postgres outbox. |
gordon-exchange | 5.0.2 | Binance REST + WS + rate limiter + FeeModel. Futures and Spot, live and testnet. |
gordon-test-db | 1.1.1 | Per-test fresh Postgres clone (template-based). Optional NATS test helper. |
Runtime services
| Repo | Port | Role |
|---|---|---|
gordon-data | 8081 | Market data ingest from Binance. Sole writer of market_data.*. Produces klines on NATS market.klines.>. |
gordon-risk | 8082 | Five circuit breakers. Halt-latch state machine. Commands executor and bots via risk.commands. |
gordon-manager | 8083 | Control plane + console BFF. Bot lifecycle (declarative). Backtest replay engine. WS fanout to console. |
gordon-bot | 8084 | Pure strategy engine. No exchange keys. One container per strategy. Emits intents on NATS. |
gordon-executor | 8085 | Sole trading-key holder. Consumes intents from NATS. Reconciles on restart. |
gordon-migrate | — | One-shot schema migrator. Owns migrations/. Exits 0 or 1. |
Frontend and research
| Repo | Role |
|---|---|
gordon-console | Next.js 16 operator UI. App Router. Auth.js v5 + Kanidm OIDC. Port 3000. |
gordon-lab | Python research lab. Walk-forward validation, ablation, parameter sweeps. Read-only DB access via gordon_lab_reader role. |
Infrastructure and docs
| Repo | Role |
|---|---|
homelab | Ansible IaC — manages srv-apps, srv-core, srv-compute. |
srv-apps | Compose stack config for the Pi5 runtime host. |
gordon-docs | This VitePress documentation site. |
Cross-cutting files in the workspace root
gordon-workspace/
├── Cargo.toml # Meta-workspace: [patch.<kellnr-url>] entries for local cross-crate dev
├── Cargo.lock # Single lock file for all Rust repos under the meta-workspace
├── docker-compose.yml # Dev Postgres + NATS (used by make dev-up and e2e)
├── Procfile.dev # overmind process list for make dev-up
├── Makefile # All workspace commands (dev-init, dev-up, test, e2e, backtest, ...)
├── .env.dev # Dev environment (gitignored — generated by make dev-init)
├── docs/ # Architecture docs, runbooks, ADRs, contracts (source for gordon-docs)
├── plan/ # Active epics, backlog, archives
├── memory/ # Persistent session memory (committed to git)
├── scripts/ # Workspace-level shell scripts
└── <repo>/ # Each sibling repo as a directoryMeta-workspace and local cross-crate development
The root Cargo.toml defines a meta-workspace with [patch.<kellnr-url>] entries pointing every kellnr-published crate at its local sibling directory:
[patch."https://kellnr.lepaux.com/"]
gordon-kernel = { path = "gordon-kernel" }
gordon-domain = { path = "gordon-domain" }
gordon-protocol = { path = "gordon-protocol" }
# ... etc.This gives you one target/ directory and a single rust-analyzer index across all repos. When working on a library crate change, you get live feedback across all consuming services without publishing to kellnr first.
After changing a library crate and bumping its version, run ./scripts/sync-crate-lock.sh to propagate the new lock to all consumer repos before pushing.
Ownership rules
These boundaries are enforced by CI and convention:
| Boundary | Rule |
|---|---|
| Market data | gordon-data is the sole writer of market_data.*. No other service writes these tables. |
| Trading data | gordon-executor writes orders/fills/trades. gordon-risk writes risk_events. gordon-manager writes runs/equity_points/bot_configs. |
| Exchange keys | gordon-executor is the sole holder of trading-capable Binance keys. No other service holds or uses them. |
| Strategy math | gordon-strategy is pure math. No I/O, no tokio tasks, no exchange dependency. |
| Research | gordon-lab has read-only DB access (gordon_lab_reader role revokes INSERT/UPDATE/DELETE). Never executes, never duplicates strategy logic. |
| BFF rule | Browser stateful reads and writes go through gordon-manager BFF. No direct browser-to-service write paths. |
Port registry
| Service | Port | Env var |
|---|---|---|
| gordon-data | 8081 | GORDON_DATA_BIND_ADDR |
| gordon-risk | 8082 | GORDON_RISK_BIND_ADDR |
| gordon-manager | 8083 | GORDON_MANAGER_BIND_ADDR |
| gordon-bot | 8084 | GORDON_BOT_BIND_ADDR |
| gordon-executor | 8085 | GORDON_EXECUTOR_BIND_ADDR |
| gordon-console | 3000 | Next.js default |
Only gordon-console (3000) is operator-exposed on srv-apps. The rest are Docker-internal.