Skip to content

fix(authz): carry derived posture rung on ExecutionContext (#2947) - #2956

Merged
os-zhuang merged 1 commit into
mainfrom
claude/authz-2947-posture-plumb
Jul 15, 2026
Merged

fix(authz): carry derived posture rung on ExecutionContext (#2947)#2956
os-zhuang merged 1 commit into
mainfrom
claude/authz-2947-posture-plumb

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

目的

关闭 #2947 —— B2/B4 posture 阶梯(ADR-0095 D2:PLATFORM_ADMIN > TENANT_ADMIN > MEMBER > EXTERNAL)由共享 authz 解析器从 capability 授予派生(resolve-authz-context.ts:303),但两个构建 ExecutionContext 的入口都把它丢弃了,导致 enforcement 侧任何读 context.posture 的代码永远拿到 undefined(也是 explain 层不得不自己重算 posture、#2949 修补的那处分叉的根因)。

改动(全部加法、行为不变)

  • specExecutionContextSchema 新增可选 posture 字段(复用 AuthzPostureSchema,无循环依赖)。api-surface 无变化(schema 作为 const 导出,字段不计入表面)。
  • restrest-server.ts 构建 ctx 时携带 authz.posture(present 时)。
  • runtimeresolveExecutionContext 同样透传(guest → 不设,符合「仅认证主体有 rung」)。
  • testsresolve-execution-context.test.ts 断言 PLATFORM_ADMIN / MEMBER 被携带、guest 保持 unset(29 passed)。

为什么安全

行为完全保持:当前没有任何 enforcement 裁决消费 posture——是否让热路径「按 posture 裁决」是更大的 ADR 级决策,本 PR 不涉及。这里只是停止丢弃已算好的值,让 enforcement 与 explain 读到同一个派生 rung,为后续把 #2946 的「平台专属能力探针」收敛成直接读 ctx.posture 铺路。

验证

  • @objectstack/spec build ✓ · api-surface --check unchanged ✓
  • @objectstack/runtime + @objectstack/rest build(tsc typecheck)✓
  • resolve-execution-context.test.ts — 29 passed ✓
  • check:role-word ✓ · ESLint(改动文件)✓

changeset:spec: minor(新增公共字段)+ runtime/rest: patch。非破坏,无需 allow-major

关联:ADR-0095 D2 · #2949(explain 侧分叉)· #2946(未来收敛点)· tracking #2920

🤖 Generated with Claude Code

https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs


Generated by Claude Code

The ADR-0095 D2 posture ladder is derived once by the shared authz resolver
from capability grants, but both entry points that build the ExecutionContext
(rest-server, runtime resolveExecutionContext) dropped it — so any
enforcement-side reader of context.posture always saw undefined (the same drop
that forced the explain layer to re-derive it, #2949).
- spec: ExecutionContextSchema gains an optional `posture` field (reuses
AuthzPostureSchema; no api-surface change — schema tracked as a const).
- rest + runtime: plumb authz.posture through when present.
- tests: assert PLATFORM_ADMIN / MEMBER are carried and guest stays unset.
Additive and behavior-preserving: no enforcement decision consumes posture yet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
@vercel

vercelBot commented Jul 15, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 15, 2026 9:49am

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/rest, @objectstack/runtime, @objectstack/spec.

105 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 @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx(via @objectstack/runtime)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via @objectstack/runtime, 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/runtime, packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/runtime, @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/index.mdx(via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/vercel.mdx(via @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @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/getting-started/your-first-project.mdx(via @objectstack/runtime, @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/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via 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/sms-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/authentication.mdx(via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx(via packages/rest, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @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/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/rest, @objectstack/runtime, @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/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/index.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx(via @objectstack/spec)
  • 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 @objectstack/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/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.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/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • 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.

@os-zhuang
os-zhuang marked this pull request as ready for review July 15, 2026 09:56
@os-zhuang
os-zhuang merged commit c11e24b into mainJul 15, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/authz-2947-posture-plumb branch July 15, 2026 09:56
os-zhuang added a commit that referenced this pull request Jul 17, 2026
…nt path, no explicit deny (#3109)
Follow-through on ADR-0095: the derived posture rung is carried on the
ExecutionContext since #2956 but no enforcement decision reads it — each tier
decision re-derives principal tier from capability bits at its own site, the
divergence class behind #2946 Finding 2 (org admin crossed the tenant wall)
and #2949 (explain/enforcement posture split).
- D1: posture becomes the single tier-adjudication input (Layer 0 exemption,
Layer 1 short-circuit tier, explain); capability probe demoted to narrowing
fallback; behavior-preserving behind an equivalence gate in the authz matrix.
- D2: normative posture → layer action map with six invariants (TENANT_ADMIN
never crosses Layer 0; enforcement and explain read the same carried value;
posture selects tier, never side; never bypasses system-only write guards).
- D3: EXTERNAL rung activation chain (audience:'external' → derivePosture →
explicit-shares-only injection), matrix cells land now, implementation
demand-gated on an external principal type (portal track).
- D4: explicit deny/muting rejected as an adjudication primitive; needs mapped
to capability gates / FLS / admission / validity windows; assembly-time
muting reserved to a future permission-set-groups ADR.
Composes with ADR-0096 (admission vs tiering — orthogonal axes). Number 0099
verified free of open-PR claims before push.
Claude-Session: https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
Co-authored-by: Claude <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 18, 2026
…ture rung (#3211 M2) (#3226)
The Layer 0 cross-tenant exemption gate in computeLayeredRlsFilter now
reads the carried ctx.posture rung (#2956) as authoritative: crossing the
tenant wall requires PLATFORM_ADMIN. The hasPlatformAdminPosture capability
probe demotes to a fallback for resolver-less contexts (delegated-admin
bridge, sharing service, getReadFilter consumers), where behavior is
byte-for-byte unchanged. Read and write (insert/update post-image) tenant
checks share the one decision, so they cannot drift. A probe/rung
disagreement logs an [authz/ADR-0099] defect breadcrumb and enforces the
narrower rung verdict. Retires the stale 'posture not plumbed' comment.
Security narrowing (multi-org / @objectstack/organizations only, the G1-
adjudicated deltas): a scoped admin_full_access grant (a) and a piecemeal
platform-capability grant (b) resolve below PLATFORM_ADMIN and are now
walled to their own org on private / platform-global / better-auth objects,
where the posture-blind probe used to exempt them. Fail-safe (rung is a
strict subset of the probe, ADR-0099 I3); recover with an unscoped
admin_full_access grant. Single-org / env-per-database unaffected.
authz-matrix-gate.test.ts: new ADR-0099 P1 describe (narrowing read/write,
PLATFORM_ADMIN stays exempt, TENANT_ADMIN stays walled per I1, resolver-less
fallback preserved, defect-breadcrumb on disagreement / silence on
agreement). dogfood multi-tenant-exemption-posture ledger note updated.
Docs: authorization.mdx records the unscoped-grant rule.
Verified: plugin-security 503 passed, dogfood 296 passed, build 71/71, tsc +
check:role-word + check:doc-authoring clean.
Refs #3211 · ADR-0099 / #3109 · #2946 Finding 2 · #2956
Claude-Session: https://claude.ai/code/session_01DAHp4K7FvyMPBY1DPNkmRu
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/steststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude