Repository navigation
fix(plugin-auth): a migrate-and-exit boot (OS_MIGRATE_AND_EXIT=1) never provisions the dev admin, whatever NODE_ENV says - #24415
Conversation
isDevAdminSeedArmed() now returns false when OS_MIGRATE_AND_EXIT is '1', the spelling objectstack serve compares. The seed and the walled owner check both read this one function, so they stay in agreement. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6ff8e42d1805488baf493f34e4f32a54da95d5d2 && git checkout 6ff8e42d1805488baf493f34e4f32a54da95d5d2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 97cb5c5a821cb1e07b0e0026db9923bdbc860863 da64a0936064031c71fafc248a29901032a2ecbe && git checkout -B drift-repro 97cb5c5a821cb1e07b0e0026db9923bdbc860863 && git merge --no-ff da64a0936064031c71fafc248a29901032a2ecbe
node scripts/docs-audit/affected-docs.mjs --json 97cb5c5a821cb1e07b0e0026db9923bdbc860863
|
|
CI note ·
Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
Fixes #24409
Clause-②: no
What changed
isDevAdminSeedArmed()(packages/plugins/plugin-auth/src/walled-owner-verification-path.ts) now returnsfalsewhenOS_MIGRATE_AND_EXITis exactly'1'. That is the same spellingobjectstack servecompares before it exits. The clause sits after theNODE_ENV === 'development'check and before theOS_SEED_ADMINoff spellings, so it also wins over an explicitOS_SEED_ADMIN=1.The seed (
AuthPlugin.maybeSeedDevAdmin) and the walled owner check (resolveWalledOwnerVerificationPathWarning) both read this one function, so the disarm reaches both.auth-plugin.tsandpackages/cli/src/commands/serve.tsare not edited: the seed already gates onisDevAdminSeedArmed()and nothing else, and the fix does not need the flag read any earlier inserve.Why
servechecksOS_MIGRATE_AND_EXITonly after the kernel bootstrap. The dev-admin seed runs onkernel:ready, inside that bootstrap, so a migrate-and-exit boot withNODE_ENV=developmentseeded exactly as a dev boot does. Run against an empty database, it left a known-credential login behind. Per the card's Wanted: "A migrate-and-exit boot is never a login-provisioning boot, whateverNODE_ENVsays."Evidence: real
os servemigrate-and-exit boots of an empty SQLite fileEach run went through the CLI source entry (
packages/cli/bin/run-dev.js, which pinsNODE_ENV=development), withOS_MIGRATE_AND_EXIT=1, noOS_SEED_ADMIN, a freshOS_HOMEand a new database file. The fixture and database were deleted after each run. Rows were counted from the file the boot left behind.44a5011fe)sys_user1,sys_account1sys_user0,sys_account0OS_SEED_ADMIN=0(control)sys_user0OS_SEED_ADMIN=1sys_user0,sys_account0OS_MULTI_ORG_ENABLED=true, owner declared, membership policy declared (invite-only)sys_user1sys_user0sys_user1sys_user0OS_MULTI_ORG_ENABLED=true, owner undeclaredAuthPlugin.init(): the walled posture requiresOS_PLATFORM_OWNER_EMAILOS_MULTI_ORG_ENABLED=true, owner declared, no membership policyThe walled case (the card's Done-when 3): it completes. A walled migrate-and-exit boot of an empty database still finishes its schema sync with the seed disarmed. The walled owner check is never a refusal (it only warns), and it did not warn in either run. The two walled refusals in the table read only
OS_PLATFORM_OWNER_EMAILandOS_AUTH_MEMBERSHIP_POLICY, never the seed. They refuse the same way before and after this change, so the fix does not widen the walled case.Tests
New pin:
packages/plugins/plugin-auth/src/dev-admin-seed-migrate-and-exit.test.ts, 9 cases. No credential is named in it. The pins count rows and never read the seeded address or password.isDevAdminSeedArmed(): armed underNODE_ENV=development(control). Disarmed byOS_MIGRATE_AND_EXIT=1, also whenOS_SEED_ADMIN=1. Any other value ('0','true','') stays armed, becauseservekeeps serving on those. The existing clauses are unchanged.kernel:readystep (maybeSeedDevAdmin) over a realObjectQLand SQLite engine, on an empty store. Control: without the flag it provisions onesys_userand onesys_account(armed and seeding). With the flag, zero of each, and no banner hint.Run at
da64a0936:The existing dev-seed pins (
dev-admin-seed-credential-gate,dev-admin-seed-workspace-timezone,auth-plugin-walled-owner-verification-path) stay green. That covers Done-when 2: theobjectstack devseed is unchanged.Ablation, run once.
node scripts/ablation-replace.mjsdeleted the newif (env()[MIGRATE_AND_EXIT_ENV] === '1') return false;line (anchor 1 to 0, blob8dcb3ca08dbatoebb01c7489a8) and ran the new file. Result: 5 failed, 4 passed. The two disarm cases readexpected true to be false, the empty-store case readexpected 1 to be +0, and both walled cases turned. The four controls stayed green. Restore was proven: the blob matched HEAD (8dcb3ca08dba) andgit diff HEADwas empty. No build was needed, because the test imports the subject fromsrc/by relative path.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackatda64a0936: 67 commands, all run locally against that tree. 66 exited 0 on the first run.pnpm check:dual-build-cjs-loadsfirst exited 3 (PREREQUISITE NOT MET: some workspace packages had nodist/). Afterpnpm turbo run build --filter='!@objectstack/docs'it was re-run and exited 0 ("111 published require entry point(s) across 69 package(s) load"). The other dist-reading gates (check:dts-closure,check:lean-entry-closure,check:published-files,check:sourcemap-no-sources-content) were re-run after that build too, and all exited 0.Reconciliation with
--ran(each line carrying its exit code): "67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN", "a DERIVED zero — all 67 recorded an exit code and none of them is 3". Families that only CI runs are read from this PR's check-runs, not re-run here.test:repowas not run locally: norepo-project test changed, and the derivation does not name it.origin/mainmoved to97cb5c5a8while this ran. The two new commits touch onlyAGENTS.md, two ADRs andexamples/README.md, none of which this diff touches, so the branch was not re-merged before opening.Acceptance notes
auth-plugin.tskeeps two comments onmaybeSeedDevAdminthat predate the new clause. One says "Both clauses (and the seeded address below) are resolved bywalled-owner-verification-path.ts". The other says "HARD-GATED to development… Opt out within dev via OS_SEED_ADMIN=0". Both are still true, but they no longer list every clause, and the docblock ofisDevAdminSeedArmed()now does. They were left alone because the claim allows edits toauth-plugin.tsonly if the disarm needs them. Carrier: none.content/docs/deployment/environment-variables.mdx("When1, run pending migrations and exit") is still true, and every--seed-admin/OS_SEED_ADMINsentence incontent/docs,skills/andAGENTS.mddescribesobjectstack dev, which is unchanged. So no docs edit was made..objectstack-shamove.Generated by Claude Code