Linear: https://linear.app/engineereddev/issue/ENG-247
What's in this PR:
- Config: zod covers every §12.3 variable with its default, including the
RAILWAY_PUBLIC_DOMAIN→PUBLIC_URLandRAILWAY_ENVIRONMENT→TRUST_PROXYfallbacks. Errors list names, never values, and exit 1. - Logging: pino with the §12.4 redaction paths and a child logger per request.
- App and startup:
createApp(deps)injects config, storage (stub), clock, logger and instanceState.index.tsuses real deps,@hono/node-server, and a graceful-shutdown hook list (close code 1012, 10 s drain). - Middleware: a JSON
ErrorBodyerror handler (via@pneumo/protocol), a 128 KiB body limit → 413, and request logging with no query strings on/api/files. - Routes and serving:
GET /api/health; staticWEB_DIST_DIRwith §12.6 cache rules; SPA fallback; JSON 404 for unknown/api. - Headers: CSP built from a bucket-origin parameter, HSTS over HTTPS, nosniff,
no-referrerand Permissions-Policy. - Build (lead decision): tsup bundles
@pneumo/*intodist/index.js, so plainnodecan run it. This fixes the ENG-239 follow-up recorded on this issue.check:servernow builds and launches the bundle (health, HTML, config failure, SIGTERM).
Verify (lead, clean install):
pnpm install --frozen-lockfile → exit 0
mise run check:server → exit 0 · Test Files 5 passed · Tests 54 passed
pnpm --filter @pneumo/web build && pnpm --filter @pneumo/server build → exit 0
.env.example + PORT=8099 node apps/server/dist/index.js:
/api/health → {"ok":true,"version":"0.1.0","initialized":false}
/ → 200, no-cache, CSP "default-src 'self'; script-src 'self'; … connect-src 'self' wss://localhost:8080; … frame-ancestors 'none'; base-uri 'none'; form-action 'self'", nosniff, no-referrer, Permissions-Policy
/devices → index.html (SPA) · /api/nope → {"error":{"code":"not_found","message":"Not found."}}
server log: 0 lines containing authorization/bearer
mise run check → exit 0
Deviations:
- README Development notes were updated; README was added to Touches by the lead.
- Package version 0.1.0.
- The spec leaves HSTS and Permissions-Policy values open; this PR uses
max-age=31536000; includeSubDomainsandcamera=(), microphone=(), geolocation=().