diff --git a/package.nix b/package.nix index 8270cdc..3dc355e 100644 --- a/package.nix +++ b/package.nix @@ -80,7 +80,21 @@ stdenv.mkDerivation (finalAttrs: { # configHook ran with --ignore-scripts; re-run install to trigger # native-module postinstall (bcrypt). --offline keeps it inside the # store seeded by configHook. - pnpm install --force --offline --frozen-lockfile --reporter=append-only + # + # `dangerouslyAllowAllBuilds` is load-bearing under pnpm 10: unlike + # pnpm 9, pnpm 10 refuses to run *any* dependency lifecycle script + # unless the package is allow-listed (`onlyBuiltDependencies` / + # `pnpm approve-builds`), and it skips them **silently** — the install + # still reports success. Without this, bcrypt's node-gyp postinstall + # never runs, `lib/binding/napi-v3/bcrypt_lib.node` is never produced, + # and the daemon dies at boot with MODULE_NOT_FOUND. That is exactly + # what shipped in 0d8c436 (the nodejs_20/pnpm_9 -> nodejs_24/pnpm_10 + # bump) and took down nsecbunkerd on aio-demo. The flag restores the + # pnpm 9 semantics this build has always relied on; we are inside the + # nix sandbox against a store-seeded offline cache, so "all builds" + # is the same closed set of scripts pnpm 9 ran. + pnpm install --force --offline --frozen-lockfile --reporter=append-only \ + --config.dangerouslyAllowAllBuilds=true pnpm prisma generate pnpm build @@ -132,6 +146,22 @@ stdenv.mkDerivation (finalAttrs: { runHook postInstall ''; + doInstallCheck = true; + + # The bcrypt failure mode is silent at build time and fatal at boot, so + # assert the native binding loads from the *installed* tree exactly the + # way `dist/daemon/index.js` loads it. A build that can't require bcrypt + # must fail here rather than on the deployed host. + installCheckPhase = '' + runHook preInstallCheck + + ${lib.getExe nodejs_24} -e \ + "require('$out/share/nsecbunkerd/node_modules/bcrypt'); \ + console.log('bcrypt native binding loads OK')" + + runHook postInstallCheck + ''; + passthru = { inherit prisma-engines; };