Skip to content

feat(authz): ADR-0099 two-axis model — Layer 1 tier stays the per-object scope bit; amendment + P2′ pins (#3211 M3/M3′) - #3245

Merged
os-zhuang merged 3 commits into
mainfrom
claude/authz-improvement-tasks-1f5u82
Jul 19, 2026
Merged

feat(authz): ADR-0099 two-axis model — Layer 1 tier stays the per-object scope bit; amendment + P2′ pins (#3211 M3/M3′)#3245
os-zhuang merged 3 commits into
mainfrom
claude/authz-improvement-tasks-1f5u82

Conversation

@os-zhuang

@os-zhuangos-zhuang commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

目的

#3211 的收尾 PR:记录 M3 裁决(maintainer,2026-07-18)——原 P2「Layer 1 tier 收敛读 posture」否决,ADR-0099 修正为两轴模型——并落 P2′ 钉住格把修正后的模型钉进 CI。docs + test,零行为变更。

背景:P2 前核实推翻了「行为保持」声明

D2 表 Layer 1 行写着 tier 读 posture、「per today's semantics」——核实证明不是今天的语义:Layer 1 的 tier 输入是每对象超级位,一个可委派给非管理员的原语(自定义集给审计员岗授「某对象全部可见」= Salesforce 对象级 View All 同款)。posture 是每主体一个 rung,结构上装不下每对象范围。照原文强制会 (a) 让一个已声明、可授予的位条件性失效(ADR-0049 禁止的 declared-but-unenforced 类),(b) 砍掉主流委派能力、逼出过度授权。

内容

1. ADR-0099 两轴 Amendment(docs)

  • 两轴,每轴单一权威:posture=边界轴(每主体,管阶梯两端——Layer 0 豁免 P1 已落 + EXTERNAL 替换 P3 门控);per-object 超级位=范围轴(每主体×每对象,Layer 1 tier 唯一输入,语义不变)。
  • D1「单一裁决输入」精化为「每轴单一权威」——治的病(多站点派生)保持死透。
  • 新增 I7(I5 对偶):范围轴永不越 posture 未开的边界(超级位持有者低于 PLATFORM_ADMIN 仍被租户墙——P1 已强制)。
  • I2 澄清:嵌套是每 rung 的 floor 不变量;加法式对象级授权在 rung 内加宽不违反阶梯。
  • P2 → P2′;四处内联标记(Status / D1 bullet / D2 表 / Sequencing)指向 Amendment。

2. P2′ 钉住格(test,authz-matrix-gate.test.ts

  • 种子等价格:种子面「持超级位 ⟺ posture ≥ TENANT_ADMIN」一致(两轴在种子面重合——正是收敛「看似安全」的原因,直到委派格暴露它们是两个事实)。
  • 委派格(LOAD-BEARING):MEMBER 持 invoice_auditor(某 private 对象委派 view/modify all)→ Layer 1 短路(组织内全行)被 Layer 0 墙住(org-1)。审计员模式,未来任何「posture 收敛清理」必须保持它绿、否则静默删掉该能力。
  • I7 格:该持有者写侧被墙(pre-image=org-1,非 BYPASS)、供异租户 org_id 的 insert 被拒。
  • 对照格:无委派的 member 在 private 对象被 DENY → 证明位是真实可授予能力、非条件性死位。

3. changeset

@objectstack/plugin-security patch(test-only,循 #3218 先例)。

裁决依据(四视角)

① 主流平台(Salesforce 对象级 View All / Dynamics org 级实体访问 / Frappe)委派原语不绑管理员人格;② posture(每主体) 与位(每主体×对象) 粒度不可互换,压轴=类型错误;③ 审计员/风控式 view-all 委派是低代码日常;④ 条件性死位是 AI 作者最踩的静默 miss 类(#3003/#3005 同族)。两轴给 AI 的规则一句话:位定对象内行范围,posture 定租户边界

验证(全绿)

  • pnpm --filter @objectstack/plugin-security test510 passed / 20 files(含 P2′ 全部钉住格)
  • tsc --noEmit(plugin-security)通过;check:role-word / check:doc-authoring clean;plugin-security build 绿
  • G2 单一权威核查:边界事实一处派生(core resolver)+ Layer 0 门消费(fallback 探针只收窄);范围事实仅 hasSuperuser*Bypass 求值,无第三处派生 ✓

收口

合并后 #3211 走 G2 关闭(P3/EXTERNAL 留 #2776 item 6;advisory lint 另立后续 issue 追踪)。

Refs #3211 · ADR-0099 / #3109 · #3218(P0)· #3226(P1)· ADR-0049 · #2946 Finding 2

🤖 Generated with Claude Code

https://claude.ai/code/session_01DAHp4K7FvyMPBY1DPNkmRu

…bject scope bit; P2 superseded by P2' (#3211)
The pre-P2 verification found D2's Layer 1 row was not behavior-preserving
as written: Layer 1's tier input is the per-object super-bit, a
per-principal-x-per-object delegation primitive (custom set granting
viewAllRecords on one object to a non-admin position — the auditor
pattern, the industry-standard shape). Posture is per-principal and
structurally cannot carry per-object scope; enforcing the MEMBER cell
literally would inert a declared grantable bit (the ADR-0049
declared-but-unenforced class) and delete a mainstream delegation
capability.
Amendment (maintainer, 2026-07-18): two orthogonal axes, each with a
single authority — posture is the boundary axis (Layer 0 exemption at
PLATFORM_ADMIN, EXTERNAL replacement; both ladder ends), the per-object
super-bits are the scope axis (the only Layer 1 tier input, unchanged).
D1's single-input principle refines to single-authority-per-axis; new
invariant I7 (scope never crosses a boundary posture has not opened —
enforced by P1); I2 clarified as a per-rung floor. Original P2 rejected;
P2' = documentation cells only (seeded-face agreement, the delegation
cell, I7), zero behavior change.
Inline markers added at the D1 bullet, D2 table, and Sequencing P2 entry;
full rationale in the Amendment section. Docs-only; no changeset.
check:role-word OK - check:doc-authoring 200 files clean.
Refs #3211 - ADR-0099 / #3109 / #3218 / #3226
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAHp4K7FvyMPBY1DPNkmRu
@vercel

vercelBot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specErrorErrorJul 19, 2026 2:15am

Request Review

…yer 1 scope axis (#3211 M3′)
The two-axis Amendment's documentation cells (zero behavior change). The
original P2 (collapse Layer 1 tier onto posture) was rejected; these cells
pin the corrected model in CI:
- seeded-face agreement: every seeded super-bit holder is already
>= TENANT_ADMIN, so the two axes coincide on the seeded surface (why the
collapse looked safe until the delegation case);
- the delegation cell (LOAD-BEARING): a MEMBER holding a delegated
per-object viewAllRecords/modifyAllRecords on a private object
short-circuits Layer 1 (sees all rows in-org) AND stays walled by Layer 0
(org-1 only) — the auditor pattern; a future posture-convergence cleanup
must keep this green or it silently deletes the capability;
- I7: the holder cannot read/write/insert cross-tenant (the scope axis never
crosses a boundary posture has not opened);
- contrast: a plain member without the bit is denied on the private object,
so the bit is a real grantable capability, not conditionally inert
(the ADR-0049 class the collapse would have introduced).
New fixture invoice_auditor (delegated per-object super-bit). G2 audit:
single authority per axis holds — boundary fact derived once (core resolver)
+ consumed at the Layer 0 gate (fallback probe narrows only); scope fact
evaluated only via hasSuperuser*Bypass.
plugin-security 510 passed; tsc + check:role-word + check:doc-authoring clean;
plugin-security build green.
Refs #3211 - ADR-0099 two-axis Amendment (#3245)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAHp4K7FvyMPBY1DPNkmRu
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-security.

11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/cli.mdx(via @objectstack/plugin-security)
  • content/docs/permissions/access-recipes.mdx(via packages/plugins/plugin-security)
  • content/docs/permissions/authorization.mdx(via @objectstack/plugin-security)
  • content/docs/permissions/explain.mdx(via @objectstack/plugin-security)
  • content/docs/permissions/permissions-matrix.mdx(via packages/plugins/plugin-security)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/plugin-security)
  • content/docs/plugins/index.mdx(via @objectstack/plugin-security)
  • content/docs/plugins/packages.mdx(via @objectstack/plugin-security)
  • content/docs/releases/implementation-status.mdx(via @objectstack/plugin-security)
  • content/docs/ui/audience-based-interfaces.mdx(via packages/plugins/plugin-security)
  • content/docs/ui/dashboards.mdx(via @objectstack/plugin-security)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuangos-zhuang changed the title docs(adr): ADR-0099 two-axis amendment — Layer 1 tier stays the per-object scope bit; P2 → P2′ (#3211 M3 裁决)feat(authz): ADR-0099 two-axis model — Layer 1 tier stays the per-object scope bit; amendment + P2′ pins (#3211 M3/M3′)Jul 19, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 19, 2026 02:57
@os-zhuang
os-zhuang merged commit f9b118d into mainJul 19, 2026
15 of 16 checks passed
@os-zhuang
os-zhuang deleted the claude/authz-improvement-tasks-1f5u82 branch July 19, 2026 05:30
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude