Repository navigation
feat(core): isCloudManaged() is the one reader of OS_CLOUD_MANAGED, with the production fuse at first read and at kernel boot - #25306
Conversation
…oduction fuse at first read and at kernel boot Claude-Session: https://claude.ai/code/session_01LCvB7huwegHimuqR4iTjxX Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCvB7huwegHimuqR4iTjxX Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 31 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 4e5e79b20d36392a75344f7dc003583a35077af7 && git checkout 4e5e79b20d36392a75344f7dc003583a35077af7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 09e07c259a633eb02fdb29bedfe911e808529f88 79aa35b1c41bba90effa9e6abbf567c8a9da4bac && git checkout -B drift-repro 09e07c259a633eb02fdb29bedfe911e808529f88 && git merge --no-ff 79aa35b1c41bba90effa9e6abbf567c8a9da4bac
node scripts/docs-audit/affected-docs.mjs --json 09e07c259a633eb02fdb29bedfe911e808529f88
|
Contract reviewServed-tier: Adversarial read of head ① Derived judgments1. The ruling, sentence by sentence (Q2 → D).
2. One source for "control plane bound". 3. Where the fuse fires. First read of the accessor; 4. The dev's open question — verdict: A stands; no code change. The ruling spells the switch as 5. Public surface. Three new exports of ② Semver level
③ Boundary flags
Check-runs on Implemented-by: VERDICT: PASS |
Claude-Session: https://claude.ai/code/session_01LCvB7huwegHimuqR4iTjxX Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT ·
|
Requested by Bill · project thread
Fixes #25294
Part of #25083 (the engine half, item 1; the services half in plugin-auth stays open on #25083).
Clause-②: yes (widening)
Metadata-protocol: none
What this lands
The engine half of ruling 6341690661 on #25083, Q2 → D:
OS_CLOUD_MANAGED=1is the managed-mode switch on every boot, read by one accessor in@objectstack/corebesideisHostKernelComposition, with the production fuse.The exports (
@objectstack/core,packages/core/src/stack-auth.ts; names are final)isCloudManaged(): booleanis synchronous and has no arguments. It returnstruewhenOS_CLOUD_MANAGEDis exactly1andfalsewhen the variable is unset or empty. Any other value (true,0,yes) throws an error naming the value. The answer is memoized for the process, and a refusal is memoized too: every later read throws the same error.resetCloudManagedForTesting(): voidforgets the memo, so the next read decides again fromprocess.env. It is for tests only, and no boot calls it. Usage in a test: setprocess.env.OS_CLOUD_MANAGED = '1', then callresetCloudManagedForTesting(). At the end, delete the variable and callresetCloudManagedForTesting()again. Under vitestNODE_ENVistest, so the fuse does not fire and the variable alone reads managed.resolveControlPlaneApiKey(configured?: string): stringreturns the configured value if one is given, elseOS_CLOUD_API_KEY, trimmed, or an empty string when neither is set. It is the one derivation of "a control plane is bound".The fuse
A production boot that finds
OS_CLOUD_MANAGED=1andresolveControlPlaneApiKey() === ''refuses. Production means not a development boot and notNODE_ENV=test. The development check reuses the existing reading,isDevelopmentBoot()(stack-auth.ts);os serve --devsetsNODE_ENV=developmentbefore anything is imported — only whenNODE_ENVis unset, soNODE_ENV=production os serve --devreads as a production boot and is refused the same way. The refusal message namesOS_CLOUD_MANAGED, the missingOS_CLOUD_API_KEY, and both remedies. It fires:isCloudManaged()'s first read, andObjectKernel.bootstrap()andLiteKernel.bootstrap(), before any plugin'sinit(). Every kernel in the process reads the same memoized fact, including per-environment kernels that carry no reader. ⛔ It is not inserve.ts.One source for "control plane bound"
cloud-connection depends on core, so core cannot read cloud-connection's
platformManaged. The derivation therefore moves into core.packages/cloud-connection/src/cloud-connection-plugin.tsstart()now computescloudApiKeyasresolveControlPlaneApiKey(this.cfg.controlPlaneApiKey), which is the same precedence and trim as the inline expression it replaces. The seat widened the claim's file surface to that line in claim amendment 6352016263. The cross-lane declaration on thedomain:cliseat post is comment 6352046857 on #6024. The diff touches two lines there: the import and the derivation.The known cost (accepted by the seat): a process-wide accessor cannot see any plugin's options, so the fuse reads only the environment arm. A production runtime that binds its control plane only through
CloudConnectionPlugin'scontrolPlaneApiKeyoption and also setsOS_CLOUD_MANAGED=1is refused, and the remedy is to setOS_CLOUD_API_KEY. This is the loud, safe direction. In this repository, no production construction passescontrolPlaneApiKey. cloud-connection documentsOS_CLOUD_API_KEYas "injected by the cloud into the runtimes it hosts". The cloud repository is private and was not read.Measured before the edit (origin/main
fb7501bc6)OS_CLOUD_MANAGED: 0.git grep -n OS_CLOUD_MANAGEDhas no hits outside changelogs, so nothing was folded onto the accessor. After this PR,decideCloudManaged()instack-auth.tsis the only reader.platformManagedwas derived inline atcloud-connection-plugin.ts:316/335.--devis read once, byisDevelopmentBoot().Tests (
packages/core/src/cloud-managed.test.ts, head569f28a5c)--devboot (NODE_ENV=development) with the variable runs managed; a production boot with the variable andOS_CLOUD_API_KEYruns managed; a production boot with the variable and no key refuses, and the test asserts the message's first sentence; without the variable nothing is managed.NODE_ENV=testwith the variable runs managed. An unsetNODE_ENVcounts as production. A blank key is no binding. An empty variable is off, and other values are refused. The memo holds, including a refusal, until reset.resolveControlPlaneApiKeyprecedence is pinned.ObjectKerneland forLiteKernel,bootstrap()rejects before the probe plugin'sinit()runs, and boots once a key is set.pnpm --filter @objectstack/core test: 111 files, 2649 tests passed.typecheckpassed (tsc, examples, check:test-typecheck).pnpm --filter @objectstack/cloud-connection test: 54 files, 670 tests passed.typecheckpassed.Ablation (
scripts/ablation-replace.mjs, which restores the file and verifies its blob equals HEAD):if (false && production && …)): 5 failed and 6 passed. The refusal pin and the four other refusal cases (unsetNODE_ENV, memo, both kernels) go red. The three pin-9 run cases stay green.ObjectKernel.bootstrap()'s call removed: only the ObjectKernel boot pin goes red (1 failed, 10 passed). The same holds forLiteKernel.Gates
dispatch-gates.mjs --commandsderived 69 families on this diff. All 69 were run locally on569f28a5c, and--ranreconciles them: 68 run, 1 NOT MEASURED.pnpm check:dual-build-cjs-loadsexited 3 (PREREQUISITE NOT MET: it needs the whole workspace built). It is left to CI.check-plugin-teardown-shape.mjs --self-testexited 3 on the shallow clone. Aftergit fetchof the fixture commit it was re-run and exited 0. The record carries the re-run.Acceptance notes
OS_CLOUD_MANAGEDkeeps the maintainer's name: no_ENABLEDsuffix, the recorded exception to Prime Directive 9 (ruling 6341690661).1turns it on; other non-empty values are refused on every boot) is this PR's reading of the ruling's "OS_CLOUD_MANAGED=1is the switch". It is there so a mistyped switch cannot run a hosted runtime unmanaged. The reviewer may narrow it to the ruling's literal reading.serve.ts, the platform-objects predicates (platform-objects: hide invite_user ×4, create_user and set_user_password when features.cloudManaged is on (visible predicates, hide-when-on) #25295), and the spec features registry (spec: declare the console flag features.cloudManaged — a PUBLIC_AUTH_FEATURES entry with a hide-when-on gate semantics, and AuthFeaturesConfigSchema declares it (child of #25083, ruling 6341690661 Q3 A) #25292).Generated by Claude Code