Skip to content

fix(infra): keep the boot checks in the production web build - #399

Merged
mindsers merged 2 commits into
mainfrom
fix/boot-checks-bundle
Oct 13, 2026
Merged

mindsers merged 2 commits into
mainfrom
fix/boot-checks-bundle

Conversation

@mindsers

Copy link
Copy Markdown
Contributor

Description

The built web server runs neither validateEnv() nor the Row-Level Security boot guard.

entry.server.tsx imports ~/shared/infra/boot.server for its side effects only. package.json lists the modules that have side effects, boot.server is not on that list, and the bundler drops a bare import of anything that is not. In a production build of main, build/server contains no occurrence of validateEnv or assertRuntimeRoleEnforcesRls, and starting it without UNITAE_CRON_SECRET prints no warning.

So the web process started in every situation the self-hosting guide says it refuses: a session secret shorter than 32 characters or left on the placeholder, no DB_RUNTIME_URL, a database role that is a superuser or has BYPASSRLS. The dev server and the worker are not bundled and kept running the checks, which is why nothing noticed.

Fix. boot.server is declared in sideEffects, along with the worker's locale module, the only other bare import of an app module.

Guard. scripts/side-effect-imports.test.ts fails when a side-effect-only import of an app module is missing from the list, and names the import. It was written first and failed on the three imports above.

Verified on a production build of this branch:

Started with Result
a valid environment, no UNITAE_CRON_SECRET boots, logs the cron-secret warning
a 12-character session secret exits 1: UNITAE_SESSION_SECRET is insecure
no DB_RUNTIME_URL exits 1: Row-Level Security cannot be enforced
an unreachable database exits 1: Row-Level Security cannot be verified

docs/self-hosting/health-monitoring.md notes what follows for probes: the web process needs the database once, at startup.

Upgrade impact

An instance that boots today may refuse to after this change. That is the documented behaviour, enforced for the first time in the web process. It happens when, outside NODE_ENV=development/test:

  • UNITAE_SESSION_SECRET is shorter than 32 characters or is the example placeholder;
  • DB_URL is missing;
  • DB_RUNTIME_URL is missing, or its role is a superuser or has BYPASSRLS;
  • the database cannot be reached when the process starts.

Each case exits with a message naming the cause. The worker has enforced the same checks all along, so an instance whose worker starts with the same environment already passes them.

Type de changement

  • Bug fix
  • Nouvelle fonctionnalité
  • Refactoring (pas de changement fonctionnel)
  • Documentation
  • Autre : ___

Checklist

  • J'ai formaté mon code avant de pousser (pnpm build:format)
  • J'ai mis à jour la documentation si nécessaire

Issue liée

None.

package.json lists the modules that have side effects, and the bundler drops
a bare import of any module that is not on the list. boot.server was not on
it, so the built web server ran neither validateEnv() nor the Row-Level
Security guard. It started with a short or placeholder session secret,
without DB_RUNTIME_URL, or as a database role that bypasses RLS: everything
the self-hosting guide says it refuses. The dev server and the worker are
not bundled and kept running the checks, which is why nothing noticed.

boot.server is now declared, along with the worker's locale module, the
other bare import of an app module. A test fails when a side-effect-only
import of an app module is missing from the list.

The health-monitoring guide notes what follows for probes: the web process
needs the database once, at startup.
@mindsers
mindsers merged commit eb97728 into main Oct 13, 2026
7 checks passed
@mindsers
mindsers deleted the fix/boot-checks-bundle branch October 13, 2026 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant