Skip to content

Professional Standards

This page exists because the platform is built to a single rule: every engineering decision should pass review by a counterparty bank’s security team. The trading network is a hypothetical we are not seeking to enter, but the engineering bar that hypothetical implies is the bar this codebase is held to.

The list below is the operational checklist. Each row names a capability the platform either implements, partially implements, or does not implement. Where it links back to source code, dashboards, or runbooks, those links are the evidence that the claim holds.

A row that reads “not implemented” or “deferred” is also the checklist of work in front of us.

CapabilityStateWhere
Real OAuth 2.0 / OIDC against an external IdPDeferredCurrently uses session cookies + dev-mode user-picker. PR pending: switch to GitHub OAuth or self-hosted Keycloak.
Role-based access control with explicit role listImplementedfrontend/src/auth/rbac.ts, backend/src/user-service/user-service.ts
Per-route authorisation enforced server-sidePartiallyMost routes go through requireAuth, but no integration test proves every route does. Audit pending.
Authorisation matrix tested per role per endpointDeferredTest suite exists for individual routes; no consolidated matrix.
Session replay attack mitigationImplementedTokens validated against user-service on every request; no client-side trust.
Multi-factor authenticationDeferredDepends on the OAuth IdP we choose.
CapabilityStateWhere
Pre-trade limits enforced server-side, not just UIImplementedOMS calls risk-engine /check before publishing orders.submitted; fail-closed on risk-engine outage. See Risk Architecture.
Per-user limits configurable and versionedImplementedMigration 0014_risk_config_versions.sql
Position-aware sizingImplementedRisk controls page
Kill switch with multi-scope cancelImplementedRisk controls page
Bypass-resistance proven by integration testImplementedbackend/src/tests/risk-bypass.test.ts: grep-based allowlist of orders.new and orders.submitted producers; fails on any new producer.
CapabilityStateWhere
Three-pillar OTel (metrics + traces + logs)ImplementedObservability page
Audit log of every privileged actionPartiallyuser.access events captured; needs append-only / hash-chained storage.
Log retention policy documentedDeferredNo formal retention policy yet.
Per-stage pipeline latency trackingImplementedPerformance page
Alerts on service offline, kill-switch events, order rejectionsImplemented (in-app)alertsMiddleware.ts, though only visible to users with a tab open.
Active alert delivery to operators (Slack/Discord/email/page)DeferredPlanned: alert-router service.
NOC dashboard for unattended monitoringPartiallyestate-overview panel exists; dedicated NOC workspace planned.
Synthetic probes that emulate user journeysDeferredPlanned.
Runbooks linked from each alertDeferredPlanned.

The Service Performance dashboard’s “Error rate” stat reflects the percentage of traced HTTP/RPC spans tagged STATUS_CODE_ERROR. We publish the live number rather than only the aspirational target, on the principle that a healthy platform’s claim about its error rate should be falsifiable.

WindowFilterRecent rateNotes
Raw (all spans)None~5%Includes by-design 401/403/404 responses (auth gates), /health probes that timeout, and connection-refused on services still starting. Not a useful “is it broken” signal on its own.
FilteredExcludes /health, /healthz, /api/overview, /logs/query paths and 401/403/404 status codestarget <1%, currently ~0.4% after the OMS-poll-timeout fixThis is the figure we treat as the operational SLO.

The journey here is non-trivial and worth recording: at one point the filtered rate was 98% because of a feature-engine fan-out bug (PR #96) that fired one Journal HTTP fetch per Kafka tick instead of per schedule. The dashboard correctly flagged the failure but the volume drowned out genuine signal until the bug was fixed and the dashboard query was tightened to exclude by-design 4xx responses.

  • /health, /healthz: periodic poll endpoints. The gateway service runs a 5-second chk() loop against ~30 downstream services. Even a 2% timeout rate on those probes shows up visibly in raw error rate, but it represents transient slowness, not a real failure.
  • /api/overview: Traefik dashboard endpoint, only reachable on the server; absence is expected on Fly.
  • /logs/query: the gateway’s logs route, returns 403 to viewer roles. By-design auth gating, not a server error.
  • HTTP 401, 403, 404: by-design auth/authz responses. A viewer hitting an admin route returns 401; the dashboard should not count that as a service failure.

What “Filtered” still includes (and we don’t suppress)

Section titled “What “Filtered” still includes (and we don’t suppress)”
  • 5xx server errors from any service
  • Connection refused / timeout on internal service-to-service calls that aren’t probes. These are real degradation.
  • Span exceptions raised by application code (recordException)

These are the signals we want to surface, and they show how we know when something is genuinely wrong. The remaining ~0.4% is dominated by transient journal /orders polls (OMS expire-orphan loop) that sometimes time out under load. Each follow-up PR named in the “Where” column above will reduce this further.

CapabilityStateWhere
cap_drop: ALL on every service containerImplementedSecurity posture page
Read-only root filesystems where feasibleImplementedSecurity posture page
Non-root user inside containersImplementedSecurity posture page
no_new_privileges: trueImplementedSecurity posture page
Resource limits per containerImplementedcompose.yml per-service mem_limit / cpus
Secrets in env vars vs proper secret storePartially.env files used for dev; production secrets via host env. No Vault/sealed-secrets yet.
mTLS between servicesDeferredCurrently plain HTTP within the docker network.
CapabilityStateWhere
Public surface area documentedPartiallyAPI gateway page lists routes; no formal exposed-vs-internal classification.
Rate limiting per endpoint, per IP, per userImplementedToken-bucket limiter at the gateway: per-IP cap on every request plus tighter per-user cap on authenticated routes. See backend/src/lib/rateLimit.ts, wired in backend/src/gateway/gateway.ts.
DDoS protection at edgeDeferredCloudflare tunnel is the intended posture.
CSP and security headers (HSTS, X-Frame-Options, etc.)DeferredFrontend currently scores ~B at securityheaders.com. Target A+.
WebSocket origin checkingImplementedGateway WS handler validates Origin header.
CapabilityStateWhere
Dependency scanning on PRsImplementedDependabot enabled + CodeQL security-and-quality query suite via .github/workflows/codeql.yml.
Secret scanning of git historyImplementedgitleaks workflow runs on every push, every PR, and weekly against full history.
Static analysis on every PRImplementedCodeQL javascript-typescript on every PR + main + weekly.
SBOM generation per buildDeferredPlanned: syft in the docker-base build step.
Container image signaturesDeferredPlanned: cosign in the docker-services matrix.
Reproducible builds with pinned image digestsPartiallyCompose uses :latest tags from GHCR; pinning by digest planned.
CapabilityStateWhere
Data classification documentedDeferredNeed to identify which fields are PII / PII-equivalent / operational.
Retention policy per data classDeferredJournal events currently retained indefinitely.
Log scrubbing (no PII / tokens leaking into logs)PartiallyLogger has some redaction; no test proving completeness.
Encryption at restPartiallyPostgres on encrypted disk on the server; not enforced for Fly.
Encryption in transit (public surfaces only)ImplementedTLS at the Cloudflare/Fly edge.
CapabilityStateWhere
Repeatable load-test harnessImplementedk6 scenarios in k6/; see k6 load testing.
Mixed-strategy load (all 9 algos)Implementedk6/mixed-strategy.js: weighted realistic distribution.
Open-bell burst patternImplementedk6/burst-open.js: 0 to 200 VUs in 30s, hold 5min.
Sustained soak for memory leaksImplementedk6/soak.js: 25 VUs for 30min, configurable.
Risk-engine pressure testImplementedk6/risk-stress.js: weighted under/at/over-limit mix.
Live load-test telemetry to GrafanaImplementedk6 writes to Prometheus via remote-write; rendered on the k6 Prometheus dashboard.
Performance regression trackingPartiallyJSON summaries written per-run to docs/site/src/data/loadtest/; no automated CI gate yet.
Load tests scheduled in CIDeferredCurrently manual; nightly k6 run on the server is a planned follow-up.
CapabilityStateWhere
Postgres backup running on scheduleDeferredNo scheduled backup yet.
Restore drill performed at least onceDeferredUntested.
Redpanda topic snapshotsDeferredNo snapshotting.
Documented disaster-recovery runbookDeferredNo DR runbook yet.
Incident postmortemsDeferredNo incidents to post-mortem yet, but template should exist before they happen.

These would each be appropriate for a real platform but are out of scope for this codebase. The reasoning is documented so that the gaps are obvious choices rather than oversights.

Non-goalReasoning
Customer fund custodyTrading platforms either custody funds themselves (heavily regulated) or route to a broker. We route hypothetically, with no real funds in flight.
Real-time market data redistributionBloomberg / Refinitiv vendor licensing is six-figures-per-year. We use synthetic data plus delayed Alpha Vantage quotes.
Multi-tenancyA platform serving multiple users is a different system. This is single-user (your own account, your own broker).
Real moneyEven with a real broker, we don’t intend to wire this up to live execution. The simulation provides what we need to demonstrate the engineering.
Regulatory reporting (MiFID II, EMIR, Dodd-Frank)Real platforms ship reports to trade repositories. We log enough to generate such reports if needed but don’t wire up the submission.
Compliance surveillance hooksSpoofing detection, layering detection, wash-trade detection. Out of scope; would need a separate surveillance service.

Each “Implemented” row above has a link to source code, a dashboard, or a runbook. If any of those rot, the row should drop to “Partially” or “Deferred” and a follow-up PR opens. The page is reviewed alongside every PR that touches an implementing component.

If you find a row that says “Implemented” but the underlying evidence has rotted, that is a bug. Open an issue and we will either fix the underlying gap or downgrade the row.