Skip to main content

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 in packages/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).