From c2ffd1c1b241904ccedcd94ec8f9a5ed4ba9109c Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 13 Jun 2026 05:14:08 -0400 Subject: [PATCH] feat(16-01): add boot-time refuse-to-boot guard for dev-bypass in production - Create apps/api/src/lib/bootGuards.ts with assertNotDevBypassInProduction() - Guard exits non-zero when NODE_ENV=production AND DEV_AUTH_BYPASS=true (D-08) - Wire import + call as first statement in isMainModule() block in index.ts - 3/3 unit tests pass, typecheck green --- apps/api/src/index.ts | 4 ++++ apps/api/src/lib/bootGuards.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 apps/api/src/lib/bootGuards.ts diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index b49262d..71c9900 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -15,6 +15,7 @@ import { persistSessionCookie } from './auth/persistSessionCookie.js'; import { startBrokerPoller } from './broker/poller.js'; import { startOutboxWorker, initOutboxTrigger } from './broker/outboxWorker.js'; import { startReminderScheduler } from './broker/reminderScheduler.js'; +import { assertNotDevBypassInProduction } from './lib/bootGuards.js'; import webpush from 'web-push'; export const app = new Hono(); @@ -110,6 +111,9 @@ function isMainModule(): boolean { // (not imported in tests). WR-04: gating the cron schedules here keeps them out of the // test process. if (isMainModule()) { + // D-08: Production safety guard — must be FIRST, before VAPID config, workers, or serve(). + assertNotDevBypassInProduction(); + // Configure VAPID credentials for web-push before starting background workers. // VAPID_SUBJECT must be a mailto: or https: URL identifying the operator. // The private key is NEVER served to clients; it signs push requests server-side only. diff --git a/apps/api/src/lib/bootGuards.ts b/apps/api/src/lib/bootGuards.ts new file mode 100644 index 0000000..315c53b --- /dev/null +++ b/apps/api/src/lib/bootGuards.ts @@ -0,0 +1,34 @@ +/** + * Boot-time production safety guards (D-08). + * + * Exported as a standalone function so it can be unit-tested without + * forking a process or importing the full app module graph. + * + * Call assertNotDevBypassInProduction() as the FIRST statement inside + * the isMainModule() block in index.ts, before VAPID config, workers, + * or serve(). Placement after any network/DB calls would allow a misconfigured + * production container to partially start before the guard fires. + */ + +/** + * Refuses to start the process when NODE_ENV==='production' AND + * DEV_AUTH_BYPASS==='true'. + * + * Rationale (D-07 + D-08): The production Dockerfile bakes NODE_ENV=production, + * engaging the devBypass.ts hard guard. This boot guard is defense-in-depth — it + * converts a silent misconfiguration (operator accidentally sets DEV_AUTH_BYPASS=true + * in the production compose) into an immediate, loud, non-zero-exit failure instead + * of a silently bypassed auth layer. + * + * The function evaluates env vars at call time (when the app starts), not at import + * time, so the test suite can set env vars before calling it without module-cache tricks. + */ +export function assertNotDevBypassInProduction(): void { + if (process.env.NODE_ENV === 'production' && process.env.DEV_AUTH_BYPASS === 'true') { + console.error( + '[FATAL] DEV_AUTH_BYPASS=true is set in a production environment. ' + + 'This configuration is forbidden. Refusing to start.', + ); + process.exit(1); + } +}