Skip to content

feat(security): ADR-0090 D5/D9 suggested-audience-binding surface + confirm/dismiss API - #2746

Merged
os-zhuang merged 4 commits into
mainfrom
claude/loving-knuth-y1g4d3
Jul 10, 2026
Merged

feat(security): ADR-0090 D5/D9 suggested-audience-binding surface + confirm/dismiss API#2746
os-zhuang merged 4 commits into
mainfrom
claude/loving-knuth-y1g4d3

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

摘要

补上 #2696 中标记为"缺失框架表面"的 D5 安装建议确认能力:包权限集声明 isDefault: true 是安装时的建议("把此权限集绑定到 everyone 岗位"),此前服务端只在启动时把它读成 fallback 权限集名称——安装后既没有可查询的待确认建议列表,也没有确认/绑定端点。本 PR 提供两者,绝不自动绑定(D5 语义)。

plugin-security

  • 新增系统对象 sys_audience_binding_suggestion(数据 API 只读;package_id × permission_set_name × anchor 唯一;anchor 取值 everyone/guest,为 D9 的 guest 泛化预留,当前唯一生产来源是 isDefaulteveryone)。
  • 新增收敛式对账 syncAudienceBindingSuggestions:同时读取启动声明的 stack 元数据和已安装包的 manifest(registry 在 installPackage 时同步更新,所以运行时 POST /api/v1/packages 安装后立即可见,无需重启);声明消失(卸载/升级去掉标志)→ 删除 pending 行;绑定在别处已建立(启动基线绑定、管理员手工绑定)→ 标记 confirmed(observed)。在启动、permission 发布物化之后、以及每次 list 调用时运行。
  • security 服务新增 listAudienceBindingSuggestions / confirmAudienceBindingSuggestion / dismissAudienceBindingSuggestion,三者都预先用租户级管理员(ADR-0066 超级用户通配符)门禁——受众锚点是租户级的(D12,委托管理员不可触碰)。
  • confirm 用调用者的执行上下文写 sys_position_permission_set(绝不 isSystem),因此 D5/D9 受众锚点门禁(高权限位拒绝)和 D12 委托管理门禁真实生效;另做一次 describeAnchorForbiddenBits 预检,让被拒时报错信息友好。本会话内刚安装、尚未物化的权限集,先经由与启动 seeder / 发布物化器相同的 provenance 检查 upsert(ADR-0086 D4)落库再绑定。

HTTP 表面(rest + runtime 双层)

hono 开发/生产栈的 /api/v1@objectstack/rest 提供,适配器部署走 runtime HttpDispatcher——两层都注册了路由,委托同一个 security 服务:

  • GET /api/v1/security/suggested-bindings?status=&packageId=
  • POST /api/v1/security/suggested-bindings/:id/confirm
  • POST /api/v1/security/suggested-bindings/:id/dismiss

未认证 401;服务端类型化错误映射 403/404/409;未装 plugin-security 时 501(rest)/503(dispatcher)。

测试与验证

  • suggested-audience-bindings.test.ts(18 个用例):对账的幂等/剪枝/observed-confirm、confirm 的物化+调用者上下文断言、高权限位早期拒绝、外包名冲突拒绝、404/409、非管理员拒绝等。
  • http-dispatcher.test.ts 新增 6 个路由用例;rest 218 个用例全绿。
  • pnpm turbo test --filter=...@objectstack/plugin-security --filter=...@objectstack/runtime 全绿(69 个任务)。
  • 真实环境端到端验证(fresh showcase,objectstack dev --seed-admin --fresh):匿名 list → 401;管理员 list → showcase 的第二个 isDefault 权限集(showcase_member_default)以 pending 出现;confirm → 200、bindingCreated: truesys_position_permission_set 行由调用者(非 system)创建;重复 confirm → 409。
  • CodeQL 高危告警(请求路径上的多项式回溯正则)已修复(改用 split('/').filter(Boolean))。

文档

content/docs/permissions/permission-sets.mdxisDefault 小节补充了新表面与端点说明。

关联

🤖 Generated with Claude Code

https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq

…m/dismiss API (ADR-0090 D5/D9)

A package permission set declaring isDefault: true is an install-time
SUGGESTION to bind the set to the builtin everyone position — never
auto-bound. Until now the flag was only read at bootstrap as the
fallback-set name; after an install there was no way to see or act on
the suggestion (framework#2696 remaining item).

plugin-security:
- new sys_audience_binding_suggestion system object (read-only data API,
  unique per package × set × anchor)
- convergent reconciler syncAudienceBindingSuggestions reading declared
  stack metadata AND installed package manifests (a runtime
  POST /api/v1/packages install is visible immediately); prunes pending
  rows on uninstall, marks out-of-band bindings confirmed (observed);
  runs at boot, after a permission publish, and on every list call
- security service gains listAudienceBindingSuggestions /
  confirmAudienceBindingSuggestion / dismissAudienceBindingSuggestion,
  all pre-gated on tenant-level admin (anchors stay tenant-level, D12);
  confirm inserts sys_position_permission_set WITH the caller's
  execution context so the D5/D9 anchor gate and D12 delegated-admin
  gate enforce the write; not-yet-materialized sets are seeded through
  the provenance-checked upsert (ADR-0086 D4)

runtime:
- GET  /api/v1/security/suggested-bindings?status=&packageId=
- POST /api/v1/security/suggested-bindings/:id/confirm
- POST /api/v1/security/suggested-bindings/:id/dismiss
  (401 unauthenticated; 403/404/409 from typed service errors; 503
  without plugin-security)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 10, 2026 5:54am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/l labels Jul 10, 2026
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/error-catalog.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest, @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • 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/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-security, packages/rest)
  • 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, @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-security, @objectstack/rest, @objectstack/runtime)
  • content/docs/protocol/objectos/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-security, @objectstack/rest, @objectstack/runtime)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security)
  • content/docs/ui/role-based-interfaces.mdx (via packages/plugins/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.

…DR-0090 D5)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
Comment thread packages/runtime/src/http-dispatcher.ts Fixed
… polynomial-redos)

split('/')+filter(Boolean) already discards empty segments; the
^\/+|\/+$ regex over request-controlled input was flagged by CodeQL
as polynomial backtracking.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
…ace (ADR-0090 D5/D9)

The hono dev/production stack serves /api/v1 through @objectstack/rest,
not the runtime HttpDispatcher, so the suggested-bindings routes must be
registered there too (same pattern as sharing rules: a security service
provider injected by rest-api-plugin, resolveExecCtx + enforceAuth per
request, 501 when plugin-security is absent, typed 403/404/409 mapping).

Verified end-to-end against a fresh showcase run: anonymous list → 401;
admin list → the showcase's second isDefault set (showcase_member_default)
appears as a pending suggestion; confirm → 200 with the binding row
created by the caller (not system) in sys_position_permission_set;
re-confirm → 409.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
@os-zhuang
os-zhuang marked this pull request as ready for review July 10, 2026 08:02
@os-zhuang
os-zhuang merged commit bd39dc5 into main Jul 10, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/loving-knuth-y1g4d3 branch July 10, 2026 08:02
os-zhuang pushed a commit that referenced this pull request Jul 10, 2026
Both sides added a securityServiceProvider (mine for /security/explain,
main's for /security/suggested-bindings): kept one shared provider, kept
main's registerSecurityEndpoints (suggested-bindings) and renamed the
explain registrar to registerSecurityExplainEndpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTubNprerCmJoU1VtUWA93
os-zhuang added a commit that referenced this pull request Jul 10, 2026
Ships the ADR-0090 v2 UI surface into the vendored console: spec-13
adoption + position relabel + external OWD dial + matrix OWD badges
(objectui#2369), the D5 suggested-audience-binding install prompt +
Access-pillar banner (objectui#2376, pairs with #2746), and the D6
'why can this user access?' explain panel (objectui#2375, pairs with
#2743). Console rebuilt and bundle canary verified at this sha.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants