Skip to content

docs(permissions,qa): the unscoped admin_full_access anchor confers platform-admin standing under single only - #19876

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-19145-walled-admin-anchor-docs
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-19145-walled-admin-anchor-docs

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #19145
Clause-②: no

Carriers 1 and 2 of #19145 (the two documentation carriers), plus one more carrier of the same claim that the sweep found in the same tree. Carriers 3 (packages/runtime/src/domains/automation.ts RUN_LIFECYCLE_DENY_MESSAGE) and 4 (packages/platform-objects/src/identity/sys-user.object.ts role.description) are not touched here. They belong to other lanes, and a split has been requested from triage. #19145 remains open for them.

The rule as the code decides it (all at 2bbb4623)

  • packages/core/src/security/resolve-authz-context.ts:865: legacyGrantAnchorRetired = postureEnforcesWall(resolveTenancyPosture()). This reads the requested posture (the environment), not the effective one, so a walled deployment that is running degraded is still treated as walled.
  • packages/spec/src/security/tenancy-posture.ts:54: postureEnforcesWall returns posture !== 'single'. The walled postures are group and isolated.
  • resolve-authz-context.ts:958: an unscoped admin_full_access row sets hasPlatformAdminGrant only when !legacyGrantAnchorRetired, so only under single. The set's name and capabilities are still pushed on every posture, so the anchor is retired but the grant is not (pinned at resolve-authz-context.platform-admin-config.test.ts:404).
  • resolve-authz-context.ts:1009: the config anchor (OS_PLATFORM_OWNER_EMAIL, verified stored row) has no posture gate and confers on every posture.
  • The request path has no per-request pointer any more. reportLegacyPlatformAdminGrant and resetLegacyPlatformAdminGrantReport are gone (absence pinned at platform-admin.test.ts:267, with positive controls). The only operator-facing line left is the boot-time fail-closed error at packages/plugins/plugin-security/src/bootstrap-platform-admin.ts:897, which names the legacy holder at :912.
  • The single half is described as it ships. Its fate is platform-admin re-anchor follow-up (Choice 4B): config-anchor the single posture — first-user promotion becomes development-only fallback #11979's (Choice 4B), and nothing here anticipates that decision.

Per carrier: old text, new text, code site

1. content/docs/permissions/authorization.mdx (combination semantics, step 3)

  • Old: "That posture is derived at one site from two anchors, either of which is sufficient: the configured administrator list … and an unscoped admin_full_access grant row."
  • New: The anchors depend on the tenancy posture. The configured list is sufficient under every posture. The unscoped grant row is sufficient only under single. Under group / isolated it is no longer an anchor: the set still grants what any held set grants, but it confers no PLATFORM_ADMIN standing, so standing comes from configuration alone. Adds a link to the tenancy-modes page.
  • Code: resolve-authz-context.ts:865, :958, :1009; tenancy-posture.ts:54.

1b. content/docs/permissions/permission-sets.mdx (who holds admin_full_access). The sweep found this; it was not in the card.

  • Old: "derived at a single site from two anchors, and either one is sufficient" and, for the grant row, "It still confers standing, and it is deprecated: a deployment resolving through it logs a pointer, once per process, at the configuration line that re-anchors it."
  • New: Which anchor is sufficient depends on posture. The config anchor is read under every posture. The grant row confers standing under single only. Under walled postures it is not an anchor, and nothing is logged about it per request. If a walled deployment holding such a row has no usable configured administrator, the boot log reports zero administrators, names the account holding the row, and points at OS_PLATFORM_OWNER_EMAIL.
  • Code: as above, plus bootstrap-platform-admin.ts:897 / :912 for the boot line.

2. docs/qa/platform-checklist/areas/access-security.json, item access-security.platform-owner-email-anchor, revision 1 to 2 (history appended; revision 1 untouched)

  • Step 6. Old: "grep the process log for the once-per-process legacy-grant re-anchor pointer". New: run the [Design] Re-anchor platform-admin: admin_full_access becomes a kernel metadata declaration; WHO holds it comes from env-configured verified emails — retiring the org-less row anchor #11663 L5 posture-split unit pins named in clause 6 and cite their output. The step states that there is no live log line to grep and that the only operator-facing line left is the boot-time fail-closed error.
  • Clause 6. Old: "the legacy anchor is honoured and loudly re-pointed … the once-per-process [authz] pointer names the OS_PLATFORM_OWNER_EMAIL config line … (reportLegacyPlatformAdminGrant, platform-admin.ts)", oracle log. New: the legacy grant is an anchor under single only, and a silent one. Under a walled posture (postureEnforcesWall, the requested posture) it confers no PLATFORM_ADMIN standing, although the held set keeps its capabilities. The config anchor still confers on that rig. The request path logs nothing. The oracle is now test (the L5 describes in resolve-authz-context.platform-admin-config.test.ts and platform-admin.test.ts), and the clause records that a change to the single half is platform-admin re-anchor follow-up (Choice 4B): config-anchor the single posture — first-user promotion becomes development-only fallback #11979's to make.
  • Source parenthetical. Old: "… matchesConfiguredPlatformAdmin verified-only, reportLegacyPlatformAdminGrant". New: "… resolvePlatformAdminEmails loud-once resolve, matchesConfiguredPlatformAdmin verified-only" (all live symbols). Adds two anchors: resolve-authz-context.ts#legacyGrantAnchorRetired and tenancy-posture.ts#postureEnforcesWall.
  • The same claim appeared elsewhere in the item, so these parts were fixed to keep the item true:
    • Step 1 booted isolated with the variable unset. That boot is refused today (packages/plugins/plugin-auth/src/auth-plugin.ts:551).
    • Under a walled posture the seeded admin holds no grant-anchored standing, because no row is written and an existing row confers nothing. That made clause 1, clause 3 and the seeded-admin both-sides control false under isolated.
    • The live legs now boot single (the zero-config default) and keep it across restarts. Clause 1, the persona and the knownGap name the posture. A walled variant was added. automated.ref now says clause 6 is test-only.
  • Code: as above, plus auth-plugin.ts:551.

Verification (head 2505ca61)

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 45 commands. All 45 were run and each exit code was recorded. All 45 exited 0. check:skill-examples first exited 3 (PREREQUISITE NOT MET: client-react not built). That is not a measurement. It exited 0 after @objectstack/client-react / @objectstack/client were built. --ran reconciles as 45 derived / 45 run / 0 unrun.
  • pnpm check:platform-checklist: OK. Symbol anchors went from 620/638 to 622/640 resolved. The areas/access-security.json anchor census went from 44 to 46 against its floor of 44, and the floor and ledger are unchanged.
  • The control-byte self-scan over the three files found 0 hits.
  • Not measured locally, and declared to CI: the Build Docs job, the Test Core shards and the type-check lanes that the derivation lists as CI-only.

Changeset

None. content/docs/** and docs/qa/** are not in any published package's files[] (checked over every non-private package.json), so nothing publishes. This is skip-changeset territory, and no label was written by this seat, per the dispatch.

Acceptance notes

  • Sweep (content/docs/**, docs/qa/**, excluding the auto-generated references/ and releases/) for either of which, two anchors, once-per-process, reportLegacyPlatformAdminGrant, unscoped admin_full_access, re-anchor and admin_full_access:
    • The only other carrier of the claim was permission-sets.mdx:167-183, fixed above.
    • authorization.mdx (validity-window and deactivation sections): "an expired / deactivated unscoped admin_full_access grant no longer derives platform_admin" is still true on every posture, so it was left alone.
    • deployment/self-hosting.mdx first-boot section: already posture-correct.
  • File surface beyond the claim: content/docs/permissions/permission-sets.mdx is outside the claim's declared file surface. It was fixed in place because it is the same defect class, a mechanical prose fix in the same tree, and no other claim holds it. The claim's surface needs to be widened by the seat.
  • Step 1 of the checklist item could not run as written, independently of feat(security)!: retire the walled legacy platform-admin grant dual read and its deprecation log (#11663 L5) #19136, because an isolated boot with the variable unset is refused. It was fixed in the same edit because the item's other clauses depended on the boot posture.

Generated by Claude Code

…form-admin standing under single only

Since the walled legacy-anchor retirement (74832b6), resolveUserAuthzGrants
derives PLATFORM_ADMIN from an unscoped admin_full_access grant only when
postureEnforcesWall(resolveTenancyPosture()) is false, i.e. under `single`.
The authorization and permission-sets pages still said either anchor is
sufficient everywhere, and that the grant logs a once-per-process pointer.

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
…grant retirement

Step 6 and clause 6 asked the runner to grep the process log for the
retired once-per-process re-anchor pointer and cited the deleted
reportLegacyPlatformAdminGrant. Clause 6 now states the posture split
with the L5 unit pins as its oracle, the live legs boot `single`
(an isolated boot with the variable unset refuses to start, and a walled
seeded admin holds no grant-anchored standing), and the source anchors
name live symbols. Revision 2, history appended.

Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2505ca61395c30533d28709e66601427f60d3f13

① Derived judgments

Every changed sentence checked against origin/main at 1f89ba0d704a797225ab8cf306e4cdec89edae8e, not against the PR body.

The rule as it ships:

  • Walled postures: packages/spec/src/security/tenancy-posture.ts:53-55 postureEnforcesWall(posture) { return posture !== 'single'; } over enum ['single','group','isolated'] (:40). Walled = group / isolated, exactly as both docs and the checklist say. True.
  • When the unscoped grant confers: packages/core/src/security/resolve-authz-context.ts:865 legacyGrantAnchorRetired = postureEnforcesWall(resolveTenancyPosture()) (REQUESTED posture, env-read, packages/types/src/env.ts:147-163); :955-960 sets hasPlatformAdminGrant from an unscoped admin_full_access row only when !legacyGrantAnchorRetired. Sufficient under single only. True.
  • The set still grants under a wall: :948 pushes ps.name, :962-968 its system_permissions, :970-982 its tabs, independent of the anchor gate; what goes is hasPlatformAdminGrant and the built-in platform_admin position at :1037-1039. Pinned at resolve-authz-context.platform-admin-config.test.ts:404-419. True.
  • Config anchor is posture-free: :1005-1019 has no posture test; pinned at test :393-402. True.
  • "nothing is logged about it per request": the else if (hasPlatformAdminGrant) arm is gone (:1020-1030); remaining console.warn sites in the file are unrelated (:191, :242). Pinned at test :378-390. reportLegacyPlatformAdminGrant / resetLegacyPlatformAdminGrantReport not exported (absence pinned by platform-admin.test.ts:266). True.
  • Boot log (plugin-security): packages/plugins/plugin-security/src/bootstrap-platform-admin.ts:897-920, inside if (walled) (:867), when resolvePlatformAdminEmails().emails.length === 0: "ZERO config-derived platform administrators", and with an unscoped holder it names the holder by user id and says declaring that account's verified address in OS_PLATFORM_OWNER_EMAIL restores it. The !walled && unscopedHolder early exit (:849) confirms only the walled branch emits it. True.
  • Walled boot with the variable unset refused: packages/plugins/plugin-auth/src/auth-plugin.ts:551-559 throws in init(). Step 1's parenthetical true.
  • single default: env.ts:163 returns 'single' when OS_TENANCY_POSTURE is unset and legacy OS_MULTI_ORG_ENABLED unset/false (:121-124). Holds under that reading (nit).
  • platform-admin re-anchor follow-up (Choice 4B): config-anchor the single posture — first-user promotion becomes development-only fallback #11979 not pre-empted: both mdx passages describe the single half as it ships; the unchanged permission-sets paragraph (:185-193) matches bootstrap-platform-admin.ts:1059, :1101. Clause 6 and the history entry name platform-admin re-anchor follow-up (Choice 4B): config-anchor the single posture — first-user promotion becomes development-only fallback #11979 as owner.

Checklist item access-security.platform-owner-email-anchor (rev 1 → 2):

  • Test references resolve: resolve-authz-context.platform-admin-config.test.ts:351, :472, :592 (cited by its unique prefix — nit), platform-admin.test.ts:266.
  • New source anchors resolve-authz-context.ts#legacyGrantAnchorRetired (:865) and tenancy-posture.ts#postureEnforcesWall (:53) resolve.
  • history append-only: history[0] md5 identical on main and head.
  • No floor or ledger file in the PR; anchors added (2), none removed.
  • Satisfiability under single: clause 1 holds at :955-960; clauses 2-5 exercise the posture-free config anchor; clause 6 is a test oracle with real pins; the added walled negative variant is unit-pinned. The old walled-plus-unset boot could not run (auth-plugin.ts:551), so the posture move was required.
  • Clause-②: no is right: prose in content/docs/** and docs/qa/** only.

② Semver level

None (docs/QA only). No .changeset. Neither content/docs/** nor docs/qa/** is in any non-private package files[] on origin/main. Check Changeset red on head as expected pending skip-changeset.

③ Boundary flags

  • Governed surfaces and release-owned trees: untouched.
  • Carriers 3-4 (packages/runtime/src/domains/automation.ts, packages/platform-objects/src/identity/sys-user.object.ts): untouched; PR body is Part of #19145.
  • permission-sets.mdx: admitted by the seat; content true (its old "either one is sufficient" / "still confers standing" / once-per-process pointer were false on main).
  • Check-runs at read time: Check Changeset failure (expected), 7 in progress; no other failure. Check Documentation Links green.

Implemented-by: claude/issue-19145-walled-admin-anchor-docs
Reviewed-by: session_01VDtqoecgES7ScQYGbFVDRv

VERDICT: PASS

Isolated at-tier reviewer, adopted by the domain:devx#1 seat.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant