What It Is
packages/api/src/app.ts’s createApp() is the single function that assembles every HTTP
route this system exposes into one Express application, in a specific, deliberate mounting
order. Everything an external caller can reach, from an unauthenticated health check to a
signed execution request, passes through this one file’s wiring.
Why It Was Built
An HTTP API needs a consistent, auditable answer to two questions for every route: does this route require a caller identity, and what happens when something goes wrong.app.ts
answers both in one place rather than leaving each route handler to decide independently,
which is what makes it possible to state precisely (not just claim) which routes are public
and why.
How It Works
Mounting order, and why it matters
Routes are mounted in this order, and the order is load-bearing. The caller-auth middleware (createCallerAuthMiddleware) is added to the pipeline partway through, after the public
routes and before the protected ones. Everything mounted before it is unauthenticated by
construction, not by an exception carved out of a uniform rule.
(
packages/api/src/app.ts)
Error mapping
createErrorHandler() (packages/api/src/middleware/error-handler.ts) is the single place
a thrown domain error becomes an HTTP response. The mapping is in the handler’s last section,
roughly lines 54 to 243:
The full route inventory
Every route module lives inpackages/api/src/routes/:
trust proxy
app.set("trust proxy", 1) (packages/api/src/app.ts:216) trusts exactly one proxy hop, which
matches this codebase’s actual deployment fronting (Fly.io’s edge, per fly.toml’s
force_https). Without this, req.ip and req.protocol/req.secure reflect the proxy’s
own connection to the process, not the original client’s.
How It Enables Things, With a Concrete Example
examples/tutorials/102-distinguishable-http-status demonstrates the NonceAlreadyConsumedError
vs. generic-RuntimeError distinction in the error handler directly. A policy denial and a
replay attempt against the same endpoint return genuinely different, distinguishable status
codes rather than both collapsing into an opaque failure.
How to Validate This Yourself
packages/api/src/app.ts, the actual mounting order. Read it top to bottom rather than trusting the table above once this file changes.packages/api/src/middleware/error-handler.ts, the full error-to-status mapping.packages/api/src/routes/*.ts, one file per route group.packages/api/tests/integration/*.integration.test.ts. Most route groups have a corresponding integration test that exercises the real HTTP path, not just the handler function in isolation.
Integration Requirements
createApp() requires a CallerAuthOption, either a real { authenticator, auditSink }
pair or the literal string "disabled". "disabled" is used only for local development and the
tutorial suite. There is deliberately no default, so omitting this choice is not possible.
Optional: RateLimitOption (Chapter 16), killSwitch (ADR-0017, mounted only when supplied), and policyChangeCheckers (which callers may approve each policy’s changes). stepUpVerifier and policyChangeApprovalService
are optional at the type level, but in effect they are required once caller-auth is enabled and
the Policy Governance approve and reject endpoints are reachable (see Chapter 7).