Add a canonical license gate that aborts the process unless the operator's
environment carries a Lux commercial license token whose Scope list
includes "dex". Same fail-closed pattern cevm v0.50.0 established at the
GPU state backend factory (~/work/luxcpp/cevm/bin/cevm/state_backend.hpp)
and that strict-PQ already uses for SignedOrder dispatch.
pkg/lx/license_gate.go
func EnforceMatchingEngineLicense()
const LicenseScope = "dex"
cmd/dex-server/main.go
cmd/dex-api-server/main.go
lx.EnforceMatchingEngineLicense() — first statement of main(),
runs before any matching-engine state is constructed.
The gate consults $LUX_LICENSE, $LUX_LICENSE_FILE, then
$HOME/.lux/license.jwt — same lookup luxfi/license uses everywhere.
On success it is silent so production logs stay quiet. On failure it
prints an actionable FATAL line and exits 1 (luxfi/license's
HasFeatureOrDie does the printing — one place, one message).
Tests (pkg/lx/license_gate_test.go) re-exec the test binary and assert
exit codes / stderr for the three observable states:
no_license → exit 1, "no valid license is present"
wrong_scope → exit 1, `does not include scope "dex"`
correct_scope → exit 0
Fixtures issued by `lux-license issue --dev` (no expiry) and live under
pkg/lx/testdata/license/. Dev-key warning is expected in test output.
go.mod adds github.com/luxfi/license v0.1.0 (the immutable initial tag).
dex-api-server — Standalone DEX HTTP API
dex-api-server is a single-process daemon that serves a pkg/lx.OrderBook
over REST and WebSocket. It is the simplest deployment shape of the LX
matching engine and is what most local-dev workflows reach for first.
Topology
There is exactly one operating mode: standalone.
| Aspect | Behavior |
|---|---|
| Orderbook | In-process pkg/lx.OrderBook. No replication. |
| Consensus | None. The daemon "mines" orders by calling AddOrder synchronously. |
| Persistence | None — the book is rebuilt from scratch on every start. |
| Auth | None (CORS open). Do not expose this binary to the public internet. |
Multi-node consensus deployments use a different binary,
cmd/dag-network (build tag zmqtest), which
wires pkg/consensus.LuxDAGOrderBook and broadcasts vertices over ZeroMQ.
That code path has its own multi-node test in pkg/consensus/multinode_test.go.
Standalone start
dex-api-server -port 8080
Endpoints:
| Method | Path | Purpose |
|---|---|---|
| GET | / |
HTML index (debug) |
| GET | /api/orderbook |
Snapshot of bids / asks |
| GET | /api/trades |
Recent trades |
| GET | /api/stats |
Market stats |
| POST | /api/order |
Place a limit or market order |
| GET (Upgrade) | /ws |
WebSocket for live updates |
Why no built-in single-node consensus mode
Adding a Quasar / LuxDAG self-loop here would be redundant: the orderbook
inside dex-api-server already finalises orders synchronously inside
OrderBook.AddOrder, so a single-validator consensus loop on top would only
add latency without altering semantics. The consensus engine
(pkg/consensus) is meaningful when there are real peers to vote with —
hence cmd/dag-network for that case and dex-api-server for the local
dev / single-process case.
If a future use-case requires a single binary that does both, the right
refactor is to extract a Daemon struct with a --peers flag where
len(peers) == 0 selects the in-process orderbook and len(peers) > 0
selects the consensus-bound one. That work is not in this commit —
it would require defining clean interfaces between the two orderbook
implementations and is a separate change.
Smoke test
go test -tags=integration ./cmd/dex-api-server/... -count=1 -timeout 60s
standalone_test.go builds the daemon, starts it on a free port, places a
limit order via /api/order, and confirms the orderbook is queryable. It
exercises the HTTP plumbing end-to-end.