Skip to content

core: isCloudManaged() — one accessor for OS_CLOUD_MANAGED=1 beside isHostKernelComposition, with the production fuse (no bound control plane ⇒ refuse to start) #25294

Closed

Description

@objectstack-fleet

Engine half, item 1, of the coordinating card #25083 (domain:services, p1), filed on the hand-off from domain:services seat 1 (comment 6349658918 on this lane's seat post #20966). The binding text is ruling 6341690661 on #25083, Q2 → D; this card quotes it rather than restating it.

Part of #25083.

What the ruling binds (quoted)

OS_CLOUD_MANAGED=1 is the managed-mode switch on EVERY boot, read by one accessor isCloudManaged() in @objectstack/core beside isHostKernelComposition (core/src/stack-auth.ts); every judgement site on the server (plugin-auth's sign-in surfaces, the admin user doors, the audience posture, invitations, the JIT path) calls it and nothing else, and every kernel in the process reads the same fact

a production boot (not --dev, not NODE_ENV=test) that finds the variable set with no bound control plane (OS_CLOUD_API_KEY / controlPlaneApiKey, the platformManaged input cloud-connection already computes) refuses to start loudly at the accessor's first read / kernel boot — ⛔ not in serve.ts

The names are the maintainer's (OS_CLOUD_MANAGED, no _ENABLED suffix — the recorded exception to Prime Directive 9; isCloudManaged(), not isManaged()).

Wanted

  • export function isCloudManaged(): boolean in packages/core/src/stack-auth.ts (exported from @objectstack/core), synchronous, the one reader of OS_CLOUD_MANAGED.
  • The fuse at the accessor's first read or at kernel boot, never in serve.ts: production (not --dev, not NODE_ENV=test) + variable set + no bound control plane ⇒ a loud refusal naming the variable and the missing binding. Where "bound control plane" is read must be the same fact cloud-connection's platformManaged already computes (one source, no second derivation).
  • If the answer is memoized, export a reset for tests and name it in the PR body (the services half's tests set the variable under vitest and remove it afterwards).

Acceptance (pin 9 as the ruling reads it)

  • A --dev boot with the variable runs managed.
  • A production boot with the variable and a bound control plane runs managed.
  • A production boot with the variable and no control plane refuses loudly.
  • Without the variable nothing is managed.
  • Clause-② / Metadata-protocol declared per AGENTS.md (a new public export of @objectstack/core).

Activity

  1. added
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 22, 2026
  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop (card thread) · 2026-10-22T21:44Z · p1, domain:engine. isCloudManaged() in @objectstack/core: the one reader of OS_CLOUD_MANAGED, with the production fuse (ruling 6341690661 Q2 → D on #25083).
    Session: session_01LCvB7huwegHimuqR4iTjxX (card thread of domain:engine seat 2, seat session session_01FaVyM6YvQLg9CsjPVCYc5K)
    Account: os-bill
    Branch: claude/project-thread-dhsqgf
    Worktree: the card thread's own checkout
    Domain: domain:engine
    Seat: domain:engine#2 (seat post #20966)
    File surface: packages/core/src/stack-auth.ts (and its index export), the kernel boot site the fuse needs inside packages/core, their tests; one changeset.
    (stop on breach; explain in the report)
    ⛔ Not in scope: plugin-auth and every judgement site (#25083's services half); serve.ts; the platform-objects predicates (#25295); the spec features registry.
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates.mjs --tier: no path-derived mandate), effort: high. Review: CONTRACT_REVIEW_TIER (a boot-refusal on the auth path; seat's call).
    Clause-②: yes (widening, provisional: a new public export of @objectstack/core; the dev confirms)
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: open PRs on packages/core/src/stack-auth.ts re-checked at claim time: no open PR touches it.

  3. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment · domain:engine seat 2 (seat post #20966) · os-bill · session_01FaVyM6YvQLg9CsjPVCYc5K · 2026-10-22T21:45Z · to claim 6351990387.

    File surface widened by one consumer line: packages/cloud-connection/src/cloud-connection-plugin.ts:316 (domain:cli lane) calls the new @objectstack/core predicate instead of deriving "control plane bound" inline. Reason: cloud-connection depends on core, so the fuse in core cannot read cloud-connection's platformManaged; the one source of the fact moves to core (resolveControlPlaneApiKey(configured?) beside isCloudManaged() in stack-auth.ts) and cloud-connection reads it — one derivation, as ruling 6341690661 Q2 → D requires. The card thread posts the cross-lane declaration the lane table requires; ⛔ not a claim on any domain:cli card.

    Accepted shape (seat's ruling on the thread's measurement): the fuse reads the environment branch (OS_CLOUD_API_KEY), which cloud-connection documents as "injected by the cloud into the runtimes it hosts"; in-repo, no production construction passes controlPlaneApiKey by config. A production boot binding the control plane by plugin config alone while setting OS_CLOUD_MANAGED=1 is refused — the loud, safe direction — and the PR body names it. The fuse fires at isCloudManaged()'s first read and at ObjectKernel.bootstrap(), memoized, with resetCloudManagedForTesting() exported for the services half's tests. "Production" reuses isDevelopmentBoot() (stack-auth.ts:135).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25294,
      "status": "done",
      "branch": "claude/project-thread-dhsqgf",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25306",
      "session": "session_01LCvB7huwegHimuqR4iTjxX",
      "premise_still_valid": true,
      "summary": "isCloudManaged(), resetCloudManagedForTesting() and resolveControlPlaneApiKey(configured?) are exported from @objectstack/core (stack-auth.ts). isCloudManaged() is synchronous and memoized, and it is the only reader of OS_CLOUD_MANAGED (there were 0 readers before). The production fuse (not isDevelopmentBoot(), not NODE_ENV=test; switch is 1; resolveControlPlaneApiKey() empty) refuses at the first read and at the top of ObjectKernel.bootstrap() and LiteKernel.bootstrap(). cloud-connection's start() now derives cloudApiKey through resolveControlPlaneApiKey(cfg.controlPlaneApiKey), per the seat's claim amendment 6352016263, so the plugin and the fuse use one derivation. The accepted cost is named in the PR body and the changeset.",
      "tests": "head 569f28a5c. pnpm --filter @objectstack/core test: Test Files 111 passed, Tests 2649 passed. core typecheck exit 0 (tsc, examples, check:test-typecheck OK). pnpm --filter @objectstack/cloud-connection test: Test Files 54 passed, Tests 670 passed. cloud-connection typecheck exit 0. New file packages/core/src/cloud-managed.test.ts: 11 tests (pin 9 x4, edges x4, resolveControlPlaneApiKey x1, kernel boot x2). Ablation via scripts/ablation-replace.mjs, src-resolved (the test imports ./stack-auth.js, so no dist leg): fuse removed gives Tests 5 failed / 6 passed (every refusal case red, the three pin-9 run cases green), restored blob == HEAD 655493786dd7. ObjectKernel boot call removed gives 1 failed / 10 passed (only the ObjectKernel boot pin), restored da322014be04. LiteKernel boot call removed gives 1 failed / 10 passed, restored 24dd9282dcf5.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack derived 69 families against merge base fb7501bc6. All were run on 569f28a5c with each exit captured. --ran: 68 run, 1 NOT-MEASURED, 0 UNRUN. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: no dist for about 31 packages; needs a whole-workspace build), so NOT MEASURED and left to CI. node scripts/check-plugin-teardown-shape.mjs --self-test exited 3 on the shallow clone; after git fetch origin 621a48760788 it was re-run and exited 0, and the --ran record carries the re-run. The other 66 exited 0. CI check-runs on PR #25306: in_progress at report time.",
      "line_budget": "278 changed lines (+277/-1) across 6 files, under the 3000 human-merge threshold (dispatch-gates reading)",
      "files_changed": [
        ".changeset/25294-is-cloud-managed.md",
        "packages/cloud-connection/src/cloud-connection-plugin.ts",
        "packages/core/src/cloud-managed.test.ts",
        "packages/core/src/kernel.ts",
        "packages/core/src/lite-kernel.ts",
        "packages/core/src/stack-auth.ts"
      ],
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 GitHub MCP calls (reads went through gh api REST; writes went through scripts/pm tools)",
      "api_writes": "3: comment 6352046857 on #6024 (cross-lane declaration, post-stamped via relay run 39860046812); pr_create #25306 with assignees [os-bill] (relay run 39866670852, read-back identical); this os-dev-report comment on #25294 (post-stamped). git push is not counted.",
      "deviations": [
        "PR body attribution link points to this card thread (cmsg_...X3AnP5C73ugJYnWGR6GGcn) as the session context requires, not to the seat thread link the dispatch order quoted.",
        "PR body ends with the session-URL footer AGENTS.md prescribes for PR bodies, not the harness's own footer.",
        "The fuse also sits in LiteKernel.bootstrap(), in addition to ObjectKernel.bootstrap() as the seat named: the ruling says every kernel in the process reads the same fact, and LiteKernel is inside packages/core.",
        "The cloud-connection edit is two lines (the import and the derivation), because the one-line change needs its import.",
        "The MCP reviewer request from the session context was skipped (the order forbids MCP writes); left to the seat."
      ],
      "open_questions": [
        {
          "question": "Should a non-1, non-empty OS_CLOUD_MANAGED value (true, 0, yes) be refused on every boot?",
          "options": [
            "A: refuse it (as built), so a mistyped switch cannot run a hosted runtime unmanaged",
            "B: treat anything but 1 as off, the ruling's literal reading"
          ],
          "recommendation": "A, because a hosted container with a mistyped switch would otherwise silently drop every managed-mode judgement; the cost is one more refusal shape no in-repo producer can hit"
        }
      ],
      "out_of_scope_findings": []
    }

    Report taken 2026-10-22T22:41Z.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25306 comment 6353155460 (contract review PASS 6353098458 on 569f28a5c; the head moved to 79aa35b1c by one prose commit). Ready and auto-merge follow once every check on the landing head is green.

  6. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:engine seat 2 (seat post #20966) · os-bill · session_01FaVyM6YvQLg9CsjPVCYc5K · 2026-10-23T00:08Z

    Probe Reading
    PR #25306 (Fixes #25294, Part of #25083) merged: true at 2026-10-23T00:07Z, merge commit db1c7ccad, through the merge queue
    origin/main db1c7ccade feat(core): isCloudManaged() is the one reader of OS_CLOUD_MANAGED, with the production fuse at first read and at kernel boot (#25306)
    queue branch gh-readonly-queue/main/pr-25306-* gone
    card closed completed by the PR

    @objectstack/core now exports isCloudManaged() as the only reader of OS_CLOUD_MANAGED, plus resolveControlPlaneApiKey() and resetCloudManagedForTesting(). The production fuse runs at first read and at boot in both ObjectKernel and LiteKernel, and cloud-connection reads the core predicate. This unblocks #25083's services half and the joint spec PR #25309 (Blocked-by: #25294). One item stays open with the maintainer: confirm the cloud injects OS_CLOUD_API_KEY as an environment variable before the next pin move. State: pm:dispatched removed in this act.


    Generated by Claude Code

  7. hotlong commented on Oct 23, 2026

    @hotlong
    Contributor

    Cloud reading for the open item in the landing comment ("confirm the cloud injects OS_CLOUD_API_KEY as an environment variable before the next pin move"). Measured 2026-10-23, read-only: secret names and redacted boot snapshots only, no value read.

    Probe Production (objectos Worker) Staging (objectos-staging Worker)
    OS_CLOUD_API_KEY on the Worker secret, set secret, set
    Forwarded into the container yes: FORWARDED_ENV_KEYS, cloud apps/objectos/cloudflare/worker.ts:283 same code
    Inside the container at boot ([objectos config] snapshot, last 24 h) set(len=64) on 19 of 19 boots set(len=64) on 12 of 12 boots
    NODE_ENV production production
    OS_CLOUD_MANAGED exactly 1 (default in ObjectOSContainer.envVars) exactly 1

    So the fuse is armed on both hosted planes, and it passes: the next cloud pin move past db1c7ccad does not take the hosted containers down. The open question in the dev report (refusing a non-1, non-empty value) does not reach cloud, which sets exactly 1. Cloud's control plane and the self-hosted images never set OS_CLOUD_MANAGED. The cloud pin in flight (objectstack-ai/cloud#3150, b6e87070) is 66 commits behind db1c7ccad, so the accessor and its fuse first reach cloud in the next pin move. The same reading is recorded on objectstack-ai/cloud#3169.


    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions