Skip to content

feat(security): ADR-0090 P4 — explain engine (D6), access-matrix snapshot gate, recalibrated benchmark - #2716

Merged
os-zhuang merged 2 commits into
mainfrom
claude/adr-0090-p4-explain-matrix
Jul 9, 2026
Merged

feat(security): ADR-0090 P4 — explain engine (D6), access-matrix snapshot gate, recalibrated benchmark#2716
os-zhuang merged 2 commits into
mainfrom
claude/adr-0090-p4-explain-matrix

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Fourth and final launch-shape wave of ADR-0090 (Permission Model v2): the D6 explain engine + access-matrix snapshot gate, plus the Addendum-recalibrated benchmark. Follows #2695 (ADR), #2697 (P1), #2708 (P2), #2711 (P3). Purely additive — no breaking changes.

D6 — explain engine ("explained by construction")

  • Contract (@objectstack/spec): ExplainRequestSchema / ExplainDecisionSchema / ExplainLayerSchema — the decision plus every pipeline layer's verdict in evaluation order (principal → required_permissions → object_crud → fls → owd_baseline → depth → sharing → vama_bypass → rls), per-layer contributor attribution (which permission set, reached via which position / additive baseline / direct grant), the composed read filter as the machine artifact, and D10 dual attribution (principalKind, onBehalfOf).
  • Engine (@objectstack/plugin-security): explainAccess receives the middleware's OWN internals injected from SecurityPlugin — the shared set resolution, PermissionEvaluator, folded FLS mask, and RLS composition — so the report cannot drift from enforcement. Exposed on the security kernel service as explain(request, callerContext).
  • Authorization: explaining another user requires manage_users (or system); the target's context is reconstructed from sys_user_position / sys_user_permission_set with everyone-anchor semantics (buildContextForUser).
  • Answers the dogfood incident's question directly: "why can 张三 PATCH 李四's leave_request?" — including the D1 fail-closed reading of an unset OWD.

D6 — access-matrix snapshot gate

  • @objectstack/lint: buildAccessMatrix(stack) derives the (permission set × object) capability matrix purely from metadata; diffAccessMatrix renders semantic review lines — 'crm_admin' gains delete on 'crm_lead', depth changes (unit → org), OWD swings, entry additions/removals.
  • os compile: opt-in gate — when access-matrix.json is committed next to the config, any drift fails the build with those lines until re-snapshotted via --update-access-matrix; the snapshot's git diff is the review artifact. Unchanged matrix auto-passes (zero cost until someone changes who-can-do-what).
  • Seeded for examples/app-crm (6 entries) and verified live.

Benchmark (ADR-0090 Addendum)

scripts/bench/permission-bench.mts — the recalibrated single-org gate (10k users × 1M rows; the 100k-user mega-unit moved to non-goal). Asserts the O()-shape property: per-request cost independent of user population; unit-depth IN-set cost tracks unit size. Passing: ~0.1µs/eval, 59ms per 1M-row IN-set scan.

Out of scope (per ADR phasing)

Tiered human-approval publish workflow and the what-if simulator (product track on top of this substrate); D10 agent assignment ceilings (needs principal-linked user rows); enterprise hierarchy-scope-resolver implementation (cloud repo).

Verification

  • New: explain engine 10/10, access matrix 5/5 (both suites include the leave_request incident shape)
  • Suites: plugin-security 235, lint 161, spec-security 111, cli 466 (green in isolation; 3-file parallel-load flake reconfirmed unrelated)
  • Dogfood 38 files passed + 1 conditional skip / 189 tests passed, 0 failures
  • All four examples compile with the new gate active; app-crm exercises the snapshot check
  • check:liveness ✓ · gen:api-surface committed · changeset: minor × 4 (additive)
  • Full 10k×1M bench run included in the summary above

🤖 Generated with Claude Code

https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H


Generated by Claude Code

…shot gate, recalibrated benchmark
Explain contract (spec): ExplainRequest/ExplainDecision/ExplainLayer — nine
pipeline layers reported in order with per-layer contributor attribution and
the composed read filter as the machine artifact; carries D10 dual
attribution (principalKind / onBehalfOf).
Explain engine (plugin-security): explainAccess walks the SAME resolution/
evaluator/FLS/RLS code the enforcement middleware uses (injected from
SecurityPlugin) — explained by construction. Exposed on the 'security'
kernel service as explain(); explaining another user requires manage_users
(target context reconstructed via buildContextForUser with everyone-anchor
semantics).
Access-matrix snapshot gate (lint + cli): buildAccessMatrix derives the
(permission set × object) capability matrix purely from metadata;
diffAccessMatrix renders semantic review lines; os compile fails on drift
against a committed access-matrix.json until re-snapshotted with
--update-access-matrix. Seeded for examples/app-crm.
Benchmark (Addendum): scripts/bench/permission-bench.mts — single-org
10k users × 1M rows; asserts per-request cost independent of population.
Passing at ~0.1µs/eval, 59ms per 1M-row IN-set scan.
NOTE: code + unit suites verified (plugin-security 235, lint 161,
spec-security 111, cli green); full dogfood + example builds pending —
run before opening the PR.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
@vercel

vercelBot commented Jul 9, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 9, 2026 7:27am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 9, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/lint, @objectstack/plugin-security, @objectstack/spec.

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

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via packages/cli, @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/api/data-flow.mdx(via @objectstack/cli)
  • content/docs/api/environment-routing.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/cli, packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/cli, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via packages/cli)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/cli, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/permissions/access-recipes.mdx(via packages/plugins/plugin-security)
  • content/docs/permissions/authentication.mdx(via @objectstack/cli)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint, packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/permissions/profiles.mdx(via @objectstack/spec)
  • content/docs/permissions/roles.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/cli, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx(via @objectstack/cli, @objectstack/spec)
  • content/docs/protocol/objectos/realtime-protocol.mdx(via @objectstack/cli)
  • content/docs/protocol/objectos/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/cli, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/role-based-interfaces.mdx(via packages/plugins/plugin-security)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)

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.

Comment threadpackages/cli/src/commands/compile.ts Fixed
Comment threadpackages/cli/src/commands/compile.ts Fixed
@os-zhuang
os-zhuang marked this pull request as ready for review July 9, 2026 08:11
@os-zhuang
os-zhuang merged commit a5a1e41 into mainJul 9, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/adr-0090-p4-explain-matrix branch July 9, 2026 08:11
os-zhuang added a commit that referenced this pull request Jul 9, 2026
…on Model v2 vocabulary (ADR-0090) (#2717)
- roles.mdx → positions.mdx: flat positions (岗位), assignment BU anchor,
built-in identity positions, everyone/guest audience anchors; the
hierarchy narrative moves to business units.
- profiles.mdx → migration tombstone: the Profile concept was removed
(D2); maps each former use to everyone-anchor bindings, isDefault
suggestions, and position-bound sets.
- permission-sets.mdx: rewritten as the single capability container —
union semantics, access depth (moved here from profiles), capabilities,
built-in sets (additive member_default baseline), governed assignment
tables, isDefault suggestion, adminScope delegated administration,
package provenance.
- sharing-rules.mdx: OWD default corrected to fail-closed private (D1 —
the page previously documented the pre-v2 fail-open default), canonical
four values only, externalSharingModel dial (D11), recipient types
position / unit_and_subordinates / team.
- authorization.mdx: position vocabulary, D1/D11 in the enforcement
chain, delegated-admin gate in anti-escalation, new explain-engine
section (D6), D7 linter + access-matrix snapshot added to governance,
ADR-0090 in the index.
- permissions-matrix.mdx: role-hierarchy section replaced with
business-unit hierarchy & positions; isProfile removed from samples;
position recipients.
- permission-metadata.mdx: isProfile → isDefault/adminScope in the field
table; union-semantics section replaces Profile-vs-Set;
current_user.positions.
- index.mdx: five-concepts overview, v2 implementation-status callout,
best practices and example updated.
- access-recipes.mdx / field-level-security.mdx: link + heading fixes.
Authoritative reference: docs/design/permission-model.md; decision
record: ADR-0090 (P1 #2697, P2 #2708, P3 #2711, P4 #2716).
Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@os-zhuang@github-advanced-security@claude