Skip to content

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)

RepoVersionRole
gordon-kernel2.0.0Error taxonomy (GordonError, ErrorKind, ErrorCode) + W3C TraceId. Frozen-ish leaf. No I/O.
gordon-domain1.1.0Cross-service business types and the ExecutionModel trait. Churns with business changes. No I/O.
gordon-protocol7.2.1NATS bus subjects + payload structs. Every payload carries schema_version: u16. Additive-only evolution.
gordon-platform0.4.0Runtime libs: HTTP client + circuit breaker, Axum trace middleware, tracing-subscriber init, IPC. Features: http, http-middleware, telemetry, ipc.
gordon-strategy4.6.4Strategy trait, MarketContext, Signal, 14 entry strategies, overlays, BacktestExecution. Pure math. No I/O.
gordon-bus2.1.0Publisher + Consumer traits over NATS JetStream + Postgres outbox.
gordon-exchange5.0.2Binance REST + WS + rate limiter + FeeModel. Futures and Spot, live and testnet.
gordon-test-db1.1.1Per-test fresh Postgres clone (template-based). Optional NATS test helper.

Runtime services

RepoPortRole
gordon-data8081Market data ingest from Binance. Sole writer of market_data.*. Produces klines on NATS market.klines.>.
gordon-risk8082Five circuit breakers. Halt-latch state machine. Commands executor and bots via risk.commands.
gordon-manager8083Control plane + console BFF. Bot lifecycle (declarative). Backtest replay engine. WS fanout to console.
gordon-bot8084Pure strategy engine. No exchange keys. One container per strategy. Emits intents on NATS.
gordon-executor8085Sole trading-key holder. Consumes intents from NATS. Reconciles on restart.
gordon-migrateOne-shot schema migrator. Owns migrations/. Exits 0 or 1.

Frontend and research

RepoRole
gordon-consoleNext.js 16 operator UI. App Router. Auth.js v5 + Kanidm OIDC. Port 3000.
gordon-labPython research lab. Walk-forward validation, ablation, parameter sweeps. Read-only DB access via gordon_lab_reader role.

Infrastructure and docs

RepoRole
homelabAnsible IaC — manages srv-apps, srv-core, srv-compute.
srv-appsCompose stack config for the Pi5 runtime host.
gordon-docsThis 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 directory

Meta-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:

toml
[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:

BoundaryRule
Market datagordon-data is the sole writer of market_data.*. No other service writes these tables.
Trading datagordon-executor writes orders/fills/trades. gordon-risk writes risk_events. gordon-manager writes runs/equity_points/bot_configs.
Exchange keysgordon-executor is the sole holder of trading-capable Binance keys. No other service holds or uses them.
Strategy mathgordon-strategy is pure math. No I/O, no tokio tasks, no exchange dependency.
Researchgordon-lab has read-only DB access (gordon_lab_reader role revokes INSERT/UPDATE/DELETE). Never executes, never duplicates strategy logic.
BFF ruleBrowser stateful reads and writes go through gordon-manager BFF. No direct browser-to-service write paths.

Port registry

ServicePortEnv var
gordon-data8081GORDON_DATA_BIND_ADDR
gordon-risk8082GORDON_RISK_BIND_ADDR
gordon-manager8083GORDON_MANAGER_BIND_ADDR
gordon-bot8084GORDON_BOT_BIND_ADDR
gordon-executor8085GORDON_EXECUTOR_BIND_ADDR
gordon-console3000Next.js default

Only gordon-console (3000) is operator-exposed on srv-apps. The rest are Docker-internal.

Gordon — keep compounding without blowing up