Repository navigation
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
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·security·priority:p1·domain:spec·area:access·pm:on-hold. ASeam:card that waits on the maintainer's ruling for #21079Triage 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 whoseguestanchor 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 thedomain:specseat by the anchoring rule and is dispatched vertically. The landing ispackages/core(resolve-authz-context.ts) andplugin-security, declared as the claim's cross-lane surfaces.Why on hold, not queued. The card's own open semantics (whose
guestanchor 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
- addedarea:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2and removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsRe-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 -- examplesin objectstack-ai/objectstack, or the same with-- srcin objectstack-ai/hotcrm, returns a hit (a first-party anonymous endpoint exists)Why the condition changed.
- Triage's hold 5929888096 waited for the maintainer to rule on security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079. That ruling is in: E (record 5933054144, maintainer 「同意E」). security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079 lands the deny baseline alone, and spec(forms): retire
publicPicker— anonymous public forms stop taking lookup / master_detail / user fields, and the anonymous picker route goes (ruling E on #21079) #21180 retires the anonymous public-form picker. - Under E no first-party door needs the
guestanchor's grants yet:- the picker leaves the platform;
- an app-declared anonymous endpoint (
authRequired: false) has 0 producers: the probe above answers 0 at objectstackfbcc05f400, with the controlauthRequired: truehittingexamples/app-showcase/src/system/apis/index.ts:82; hotcrmsrcdeclares no endpointauthRequiredat all.
- So this card restarts on pull, not on a date.
Carried with this card when it restarts: Q2 from #21079's request 5929776594, i.e. whose
guestanchor 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, theSeam: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
- Triage's hold 5929888096 waited for the maintainer to rule on security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079. That ruling is in: E (record 5933054144, maintainer 「同意E」). security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079 lands the deny baseline alone, and spec(forms): retire
- added a commit that references this issue
on Oct 2, 2026 objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsClosed
not_plannedon the maintainer's ruling: no demand for theguestanchor's grantsTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-04T13:39Z. ⛔ Classes and positions only.- The ruling, verbatim: 「21158 既然没有需求那就关闭」. The maintainer gave it in this triage seat's Claude Code session, after reading this card's state there.
- Measured before closing (
main, read in this round): the restart condition in5933116435has 0 producers.- No
authRequired: falseendpoint exists in objectstackexamplesor in hotcrmsrc. - Ruling E on security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079 retired the anonymous picker (spec(forms): retire
publicPicker— anonymous public forms stop taking lookup / master_detail / user fields, and the anonymous picker route goes (ruling E on #21079) #21180). - No first-party door needs the
guestanchor's grants.
- No
- Nothing is exposed. The anchor fails closed: a binding grants an unauthenticated caller nothing. Closing leaves ADR-0090 D9 declared but unenforced. That gap is recorded here, not hidden.
Reopen condition (the hold's restart condition, kept):
- a first-party anonymous endpoint appears (
authRequired: falsein objectstackexamplesor hotcrmsrc); or - a customer asks to grant anonymous callers through the
guestanchor.
Carried on reopen: #21079's question 2 (
5929776594), whoseguestanchor 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
- added 3 commits that reference this issue
on Oct 7, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsPointer · 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:04ZThis card closed
not_plannedon 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
Seam:
spec:GUEST_POSITION / AUDIENCE_ANCHOR_POSITIONS (ADR-0090 D9) → runtime:core resolveAuthzContext + plugin-security resolvePermissionSetsForContextFiling gate: ① a product defect with a measured⚠️ Disclosure discipline, the same as #21061's and #21079's: doors, caller classes, files, functions, codes and statuses only. Every reading is private.
reach:(a declared capability with no enforcement: declared ≠ enforced).Filed by the
domain:servicesexecution seat (#6021,session_01XY5uCwTjZj7884yYtyur4H) from #21079's phase-0 report (5929705833, out-of-scope finding, class b). ⛔ Not a claim.Governing text
guest— NEW builtin, undeletable; unauthenticated principals hold it implicitly and nothing else." The guest position answers "which object classes are anonymously reachable at all".What was measured (by class)
reach:measured on a fixture deployment, privately. A permission set bound to the seededguestanchor position changed nothing at an anonymous door: an app-declared anonymous endpoint answered the same status with and without the binding. The guest envelope resolved no set either way, before and after security(plugin-security): a non-system caller who resolves no permission set is admitted to every object and read with no row scope; an empty set list grants by absence instead of answering the deny baseline #21079's phase-0 edit.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_POSITIONandAUDIENCE_ANCHOR_POSITIONSare 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
guestanchor 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:/auth/me/permissionsand/me/appsstill apply the baseline only when the caller resolves to ZERO sets — the ADR-0090 D5 fallback cliff, one plane over from where it was abolished #7608 (closed) is the D5 fallback cliff.tenancyPosturecallers (plugin-sharing: the exec-context seam supplies notenancyPosturetoresolveAuthzContext— an ex-member's org-stamped API key keeps its claim #15349–service-settings: the manifest gate supplies notenancyPosturetoresolveAuthzContext— and returns the unvettedtenantIdto its callers #15351, closed).Dedupe words:
guest anchor binding unauthenticated·guest position binding not resolved·ADR-0090 D9 guest bindings·anonymous principal sets anchorGenerated by Claude Code