Skip to content

security(core, plugin-security): an unauthenticated principal never resolves the permission sets bound to the guest anchor; ADR-0090 D9 is declared and seeded but not enforced #21158

Description

@objectstack-fleet

Seam: spec:GUEST_POSITION / AUDIENCE_ANCHOR_POSITIONS (ADR-0090 D9) → runtime:core resolveAuthzContext + plugin-security resolvePermissionSetsForContext

Filing gate: ① a product defect with a measured reach: (a declared capability with no enforcement: declared ≠ enforced). ⚠️ Disclosure discipline, the same as #21061's and #21079's: doors, caller classes, files, functions, codes and statuses only. Every reading is private.

Filed by the domain:services execution seat (#6021, session_01XY5uCwTjZj7884yYtyur4H) from #21079's phase-0 report (5929705833, out-of-scope finding, class b). ⛔ Not a claim.

Governing text

  • ADR-0090 D9: "guest — NEW builtin, undeletable; unauthenticated principals hold it implicitly and nothing else." The guest position answers "which object classes are anonymously reachable at all".
  • ADR-0090 P2's proof: "an anonymous-principal e2e (a guest sees exactly the guest bindings and nothing else)".

What was measured (by class)

The positions (source at origin/main)

  • packages/core/src/security/resolve-authz-context.ts: an unauthenticated request returns before any position or binding expansion (if (!userId) return ctx;).
  • packages/plugins/plugin-security/src/security-plugin.ts, resolvePermissionSetsForContextUnmemoized: for a context with no user id it resolves position NAMES as set names only. It never reads the anchor's bindings.
  • packages/spec/src/identity/position.zod.ts: GUEST_POSITION and AUDIENCE_ANCHOR_POSITIONS are declared and seeded.

Re-check: grep -n 'if (!userId) return ctx;' packages/core/src/security/resolve-authz-context.ts → 1 hit.

Why it matters now

#21079 makes an empty set list the deny baseline for a caller that carries a principal. Under that baseline, the guest doors that serve by design need a supported grant channel, and D9's anchor is the declared one. Which route keeps those doors working is the open decision on #21079. This card is that decision's input: under every option it records, the D9 gap stays a gap to close or to carry explicitly.

Open semantics (not decided here)

An unauthenticated request carries no organization. On a deployment with more than one organization, whose guest anchor bindings apply is a product question. It is raised on #21079 for the maintainer.

Reader who acts

Triage (grade and route; a Seam: card), then the seat that owns the route #21079's ruling picks.

Dedupe

mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:

Dedupe words: guest anchor binding unauthenticated · guest position binding not resolved · ADR-0090 D9 guest bindings · anonymous principal sets anchor


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · security · priority:p1 · domain:spec · area:access · pm:on-hold. A Seam: card that waits on the maintainer's ruling for #21079

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-01T10:54Z. ⛔ Not a claim, ⛔ not a dispatch. ⛔ Classes and positions only.

    Restart-when: the maintainer rules on #21079's decision request (5929776594): which channel grants the guest-by-design doors, and whose guest anchor bindings apply on a multi-organization deployment.

    Why p1. A declared ADR-0090 D9 capability is not enforced. It fails closed today (a binding grants nothing), so it is not exposure. But #21079's deny baseline makes it the declared channel the guest-by-design doors would need.

    Routing. It carries a Seam: line, so it goes to the domain:spec seat by the anchoring rule and is dispatched vertically. The landing is packages/core (resolve-authz-context.ts) and plugin-security, declared as the claim's cross-lane surfaces.

    Why on hold, not queued. The card's own open semantics (whose guest anchor applies when no organization is carried) is a product question, and it is with the maintainer on #21079. Building D9 before that ruling would fix one answer to it.


    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    priority:p1High: required for production / M2
    and removed on Oct 1, 2026
  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Re-hold: pm:on-hold → pm:on-hold, with the restart condition rewritten. Director seat, summon #32, session_016tKoy8NJa35Yih1FdzrVmn, 2026-10-01T14:05Z. ⛔ Not a claim.

    Restart-when: git grep -n -E '^\s*authRequired:\s*false' origin/main -- examples in objectstack-ai/objectstack, or the same with -- src in objectstack-ai/hotcrm, returns a hit (a first-party anonymous endpoint exists)

    Why the condition changed.

    Carried with this card when it restarts: Q2 from #21079's request 5929776594, i.e. whose guest anchor bindings apply on a multi-organization deployment. The director's analysis recommended (a): resolve only where one organization is unambiguous, and refuse elsewhere. ⛔ That was not ruled, so it is re-presented when this card restarts.

    Unchanged: the grade (priority:p1, domain:spec, the Seam: line, the cross-lane surfaces in core and plugin-security). The anchor fails closed today: a binding grants nothing, so nothing is exposed.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed not_planned on the maintainer's ruling: no demand for the guest anchor's grants

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-04T13:39Z. ⛔ Classes and positions only.

    Reopen condition (the hold's restart condition, kept):

    • a first-party anonymous endpoint appears (authRequired: false in objectstack examples or hotcrm src); or
    • a customer asks to grant anonymous callers through the guest anchor.

    Carried on reopen: #21079's question 2 (5929776594), whose guest anchor bindings apply on a multi-organization deployment. The director recommended (a), resolve only where one organization is unambiguous. It is unruled.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer · this card's question is folded into the guest-model design card #22146 · director seat, summon #35, session_01VYToj6PQehTEKNrjGM9akg (via the relay) 2026-10-08T03:04Z

    This card closed not_planned on 2026-10-04 for want of demand (5980599467). Today the maintainer named the demand in the director seat's chat (「最为一个元数据开发平台,guest 是常见的需求吧?」, then 「同意 p1」 to a design card), so the D9 grants channel this card measured as declared-not-enforced is now question 3 of #22146 (domain:spec, p1, target:v18): the complete guest model in one ADR. This card stays closed; its readings and positions are the design round's starting census. The dispatcher's anonymous path, which the 17.6 release note described beside this card, is #22147 (ruled C: the guest entry, deny-all until the channel lands).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p1High: required for production / M2security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions