Server P0: one composition root, application-path test, Fly showcase container
Summary
This is the first phase of the server application-path plan. Goal: one consistent path from instrument create/import → market inputs → pricing → publication.
-
One composition root.
convex_server::compose(config)builds storage, engine and server without starting or binding anything.main.rsnow only loads config and calls it, and the tests use it too, so there is no second wiring. A failure reports the step that failed (StartupError::Composition { stage, .. }). -
Acceptance test (
tests/application_path.rs). It drives the real composition over HTTP using the showcase seed, which keeps the container fixture under CI. The test:- commits reference data,
- prices the stored bond by ID from the same repository,
- saves curve recipes (Unix only),
- restarts and reads the same versions back.
Book ingest (P6) and live quotes (P2) are
#[ignore]tests for now; they get switched on when those phases land. -
Fly showcase (
deploy/showcase,Dockerfile,fly.toml). The image built from currentmainexits at startup: it binds0.0.0.0under the Production profile, andvalidate_startuprefuses both. This PR fixes that:- Caddy listens publicly on
:8080and proxies to the server on loopback:8081. - Caddy uses a fail-closed allowlist: reads,
/wsand stateless calculator POSTs pass; every other write gets 403. tiniplus anshentrypoint reseeds redb fromseed.jsonon every boot. The seed is one fictional bond and six demo curve recipes, all taken from existing fixtures.- CORS allows exactly
https://convex-demo.pages.dev. fly.tomladds a/healthcheck and request concurrency limits.
- Caddy listens publicly on
Deferred from the plan
- Demo quote feed → P2. It needs the P2 quote ingress route.
- Demo profile as the real runtime composition → P2. Every
tools/core_*_workflow.pyCI step starts in demo mode without a plan, so demo startup is unchanged here. - Windows CI job → P4. Curve recipes go through
CoreWorkflow, which is Unix-only until the redb snapshot adapter lands.
Testing
- Windows:
cargo test -p convex-server --test application_pathpasses, including the restart. - Linux (WSL):
cargo test --all-features -p convex-serverpasses (254 tests).tools/core_workflow.pypasses, so demo startup is unchanged.cargo clippy -D warnings,cargo docandtools/assurance/check.py(including--base main) all pass.
- Entrypoint run under
dashwith Caddy 2.8.4, without Docker:- The seed imports.
- Reads return 200, calculator POSTs reach the server, and blocked writes return 403.
- CORS echoes the Pages origin, and
/wsreturns 101 through Caddy. - A reboot on a reused data directory works.
- SIGTERM to the process group leaves nothing running.
- Not tested:
docker build(no Docker daemon was available).
Before fly deploy
- Build the image once with
docker build -t convex-showcase .. - Clear any leftover
CONVEX_*Fly secrets or env (fly secrets list -a convex-pricing-api). The server rejects unknownCONVEX_*variables, and environment values overrideserver.toml.
Known risks
- Caddy is the only write guard. The server runs
local_developmentaccess on loopback, and the access code forbidsproxymode together withlocal_development. - The curve calibration page is unavailable on the showcase until P1.
POST /api/v1/curvesand/curves/bootstrapstore into the unbounded in-memoryCurveBuilder, so Caddy blocks them. P1 makes bootstrap stateless and re-allows it. - Some pages were already broken and still are. The streaming-simulation and market-data pages call separate demo provider services that the Pages build never points to. P2's demo feed replaces them.
Review follow-up (d80804ed)
- The entrypoint now supervises both processes and exits non-zero if either dies, because a failed Fly health check only removes the machine from routing. SIGTERM drains both and exits 0. Verified under
dash: after a server kill the entrypoint exits 1, after SIGTERM it exits 0, and no processes are left either way. - The curve routes are blocked publicly (they return 403 through Caddy).
make_seed.pynow materializes the packed fixtures first. Regenerating the seed produces identical bytes.- The docs no longer overstate the restart behaviour, the
OPTIONSpolicy or thecompose()error contract. - Not changed: the seed curve recipes are a dated capture. Nothing public consumes them in P0; P1 handles freshness.
🤖 Generated with Claude Code
Summary by CodeRabbit
-
New Features
- Added a containerized showcase deployment with a preloaded pricing dataset and local startup seeding.
- Added a read-only public edge for supported calculator requests, with JSON responses for blocked operations.
- Added support for pricing workflows including bond retrieval, identifier-based pricing, curve recipes, reference versions, and ETags.
- Added persistent behavior across server restarts within the configured storage.
-
Deployment
- Added service health checks, request concurrency limits, and documented local Docker/Fly.io execution.