Trade8:Desk - Order Management Service
Audience: anyone evaluating or onboarding to the service. For the wire-level construction read
DESIGN.md; for business functionality readBUSINESS.md. Vertical context: the top-levelDESIGN.md
(system map) and
BUSINESS.md(business view).
1. Summary
Trade8:Desk is the order-management core of the trade8 vertical. It owns the
order lifecycle (submit → validate → authorize → accept → route → track →
terminal), the working-order state for the trading session, the reference
catalogs (markets, symbols, destinations, rules), the short-sale locate pool,
and the keystroke audit trail. It is the single writer for order state; every
transition is emitted to Sentin8:Bus outbox-first (desk.outbox in the same
DB transaction as the desk.order_event append).
The service is a Rust binary on the sentin8 shell (sentin8::shell::ServiceCore):
axum HTTP router, in-memory state machine + catalog cache, RLS-scoped
PostgreSQL store (schema desk in db trade8), an outbox relay owned by the
shell, direct TLS callers to Trade8:Risk (authorize) and Trade8:Feed
(close-only/flatten reference prices), and a /ws two-way order-entry channel.
2. Motivation
Desk-of-the-broker order management has three hard requirements that generic platform services do not satisfy:
- Millisecond-class order acceptance. A submit must complete structural validation, a synchronous pre-trade authorization, the acceptance write, and event publication before the trader's next keystroke. This rules out any queue-poll architecture on the acceptance path.
- Event-sourced, auditable order state. Regulators (and support desks) need who did what to which order, when, and why - including on-behalf-of actions. Desk treats every transition as an append-only journal event, not a mutable row update.
- Session-time volatility with durable state. The working-order map is session memory (fast in-process reads/writes), while the journal is durable; session open/close must park or restore overnight orders atomically.
Before the platform, these lived in scattered desktop-era tooling (order processor, dispatcher, catalog loaders, locate-pool scripts) with no shared tenancy, authorization, or event vocabulary. Desk consolidates them into one service on the Sentin8 identity/tenant/event planes.
3. Architecture & High-Level Design
The order-acceptance path is deliberately synchronous end-to-end: validate → authorize over TLS → single DB transaction (order + event + outbox rows) → HTTP reply. The bus relay is asynchronous and never blocks acceptance.
4. Key Traits and Data Structures
struct DeskState- the service-wide state: shellcore(config, pool, policy),engine(DeskEngine), andwiring(DeskWiring: Risk/Feed callers, feature-flagged byTRADE8_RISK_URL/TRADE8_FEED_URLpresence).DeskEngine(engine.rs) - the heart:submit_pending(),accept_pending(),reject_pending(),cancel_with_actor(),replace(),flatten()over a working-order map, plus the catalog, borrow-pool, and session sub-commands. Enforces theOrderStatelegality table fromtrade8::contracts::enums::OrderState.Catalog(catalog.rs) - markets, symbols (incl. futures), destinations, gateways, order classes, tick rules, offices, trader profiles, trading calendar, market periods, reject codes: loaded at boot from DB, served from memory.borrow- locate-pool arithmetic: pool totals, per-office blocks, ETB/HTB classification, short-exempt marks, decrement-on-accept and restore-on-cancel semantics.session- the market session FSM (preopen → open → close_pending → closed), driven by Cron proxy-back (POST /v1/session) or admin action.store- every multi-row write is one SQL transaction: the domain write- the append-only event +
insert_outbox_row(...)event rows, delivered by the shell relay.
- the append-only event +
- Contracts -
trade8::contracts::order.{Order, NewOrderRequest, ...},trade8::contracts::eventsfor the event-type vocabulary (trade8:desk:order:submitted/accepted/rejected/...), fixed-point money viatrade8::fixed.
5. Rationale, Trade-offs, and Alternatives
- Session-time memory + journal, not DB-only state. In-process maps give O(1) working-set access without pool round-trips on the hot path; the append-only journal is the durable truth. Rejected: DB-per-transition reads (latency), event-sourcing-only (no fast working reads).
- Synchronous Risk call, not bus-routed authorization. Authorization must
block acceptance - latency (sub-ms in-env) and determinism dominate, and
a deny must return to the trader in the same request. Fire-and-forget would
allow an un-authorized order to appear accepted. The point-to-point nature is
reflected in the
trade8::client::callscontract constants. - Catalog cache from DB, invalidation via bus. Boot loads the catalog once;
admin mutations would publish
trade8:desk:catalog:updatedfor consumers to reload. Rejected: per-request catalog reads (pool churn), compile-time catalogs (changes require binary restart). - Mimalloc allocator (master design §11): lower tail latency on the order-acceptance and WS hot paths; one-line per binary.
- Borrow pool in Desk (not Risk): locate approval is desk workflow (block release, ETB/HTB lists, short-exempt) while Risk enforces the check at authorization time. Keeps desk bookkeeping separated from the pre-trade gate.
6. Internal Implementation & Invariants (Private Design)
Invariants (always true, verified in tests/sql_matrix.rs):
- Every state transition is an event in
desk.order_eventwithprev_state,new_state, actor, and reason - the journal row and state change land in the same transaction; illegal transitions cannot be represented. submit_pending→ Risk authorize → accept/reject is atomic per order: a persist failure rolls back acceptance (cancel_with_actor).- Idempotency: resubmitting an existing idempotency key returns the existing
order (
duplicate: true) - no re-authorization, no duplicate row. - Short-locate reserve happens only on short-intent accept and is restored on
cancel/reject - the pool row and
desk.short_pool_office_blockupdate in the same tx. - All store calls run on a transaction obtained via
conn.begin()afterset_tenant_context- the RLS context is transaction-local, so data access and context share the same tx. - Blotter reads filter explicitly by
env_idand order scope; RLS is the backstop. - The
desk.outboxanddesk.audit_journaltables follow the shell-generated platform contract; the shell relay delivers outbox rows to the bus.
7. Unresolved Questions / Future Designs
- Catalog hot-reload event coverage - the
trade8:desk:catalog:updatedevent type exists incontracts::eventsbut no service currently emits it on catalog mutations; whether admin edits should republish the catalog to every env remains an open design point. - Borrow-pool event granularity - the pool emits the summary
trade8:desk:borrow:updatedtoday; whether downstream consumers need per-reservation events (borrow:reserved/released/adjustedconstants exist) is open. - WS channel backpressure contract under bursty submits - bounded-queue behavior under sustained F-key bursts over slow client links.
- Cross-env flatten for office managers - flatten currently acts on the caller's own env scope; org-manager cross-env flatten (via the delegation gateway) is a future design.
Architecture & Design: trade8:desk and its underlying concepts were designed by Hamid Jahanzad.