Skip to content

feat(controller): capability foundation for the L2 permission model (#1220) - #1237

Open
LUOSENGWA wants to merge 3 commits into
agentscope-ai:mainfrom
LUOSENGWA:feat/capability-foundation
Open

feat(controller): capability foundation for the L2 permission model (#1220)#1237
LUOSENGWA wants to merge 3 commits into
agentscope-ai:mainfrom
LUOSENGWA:feat/capability-foundation

Conversation

@LUOSENGWA

Copy link
Copy Markdown
Contributor

Capability foundation for the L2 permission model (#1220)

Summary

This PR is step 1 of the #1220 sequencing — the capability model foundation: grantable, queryable, auditable, but gating no existing operation. It adds:

Interface-stability promise for the follow-on PRs: the Capability type, the five constants, and the HasCapability signature are the consumer contract and will not be renamed.

Design

Decision Rationale
Five-value set, unknown values rejected at admission #1220 §3 fixes the value set; open-ended strings would let the audit taxonomy and the UI picker drift. A drift-pin test asserts the set against the spec.
Merge-patch semantics (absent / replace / clear) Mirrors the accessibleTeams pattern from #1209 — the human-update API is a single merge-patch endpoint, and a third "set" mode would be inconsistent with it.
HasCapability = role baseline first, and never implies team scope The #1220 §3 check order is role baseline AND TeamMatches AND HasCapability; a helper that implied scope would let a consumer skip TeamMatches and silently widen scope.
SA identities never carry capabilities #1220 §5: leaders (SA) never hold capabilities. The authenticator never populates the field for SAs (pinned both ways).
Dual-layer audit: log line + JSONL append-only #1220 §8: the log line is the immediate trail, the JSONL is the durable replayable record. ETag-optimistic append with 3 retries keeps the single-replica deployment conflict-free and makes exhaustion all-or-nothing.
Zero gating This PR changes no existing decision: no endpoint gains a new rejection, no authorizer path is consulted. The field is data until a consumer PR (per #1220 §12) opts in.

What this PR does not do

  • No existing operation is gated or newly rejected — purely additive (verified: HasCapability has zero production call sites; the audit wiring is grant/revoke events only).
  • No queryable audit store (Design: L2 permission & capability model (worker config, channels, approval, skill catalog) #1220 §13 Q3) and no audit rotation — the durable layer assumes a single controller replica, the embedded and k8s deployment forms today.
  • No secret_reveal consumer (reserved value; the secret contract is a follow-on PR).
  • No MCP/ACP or worker-app change; the only CRD shape change is the additive capabilities field.

Tests

  • merge-patch semantics — apply / dedupe / omit / clear / unknown-value 400 (5 cases)
  • HasCapability matrix — admin / manager / L2±value / full_access / worker / leader / nil caller
  • value-set drift pin against Design: L2 permission & capability model (worker config, channels, approval, skill catalog) #1220 §3 (renaming or dropping a value fails CI)
  • Matrix authenticator population ± (L2 human carries the set; SA never does)
  • audit client — create / append / retry-on-conflict / exhausted-retries all-or-nothing / 20-way concurrency with no dropped lines / closed-schema secret hygiene (7 tests)
  • handler wiring E2E — PUT /humans grant×2 + revoke → JSONL event assertions
  • Gates: gofmt / go build ./... / go vet ./... / go test ./... — 22 packages ok, zero failures (baseline + the new internal/audit package). Branch diff scanned: no credential patterns.
  • The three commits are independently reviewable: ① CRD + value set + HasCapability + authenticator population ② API field + validation + tests ③ audit client + wiring + docs.

Related


L2 权限模型的 capability 地基(#1220

摘要

本 PR 是 #1220 定序的第 1 步——capability 模型地基:可授、可查、可审计,但不门控任何现有操作。新增:

接口稳定承诺(面向后续 PR):Capability 类型、五个常量、HasCapability 签名 = 消费契约,不改名。

设计

决策 理由
五值集,未知值准入即拒 #1220 §3 固定值集;开放字符串会让审计分类和 UI picker 漂移。漂移钉测试对值集断言。
merge-patch 语义(省略/替换/清空) 镜像 #1209accessibleTeams 模式——human-update API 是单一 merge-patch 端点,再加第三种 "set" 模式不自洽。
HasCapability = 角色基线优先,且从不蕴含团队范围 #1220 §3 检查顺序 = 角色基线 AND TeamMatches AND HasCapability;若 helper 蕴含 scope,消费侧可能跳过 TeamMatches 而静默扩大范围。
SA 身份永不携带 capability #1220 §5:leader(SA)永不持有 capability。authenticator 对 SA 永不填充该字段(正负向钉住)。
双层审计:日志行 + JSONL 追加写 #1220 §8:日志行是即时轨迹,JSONL 是持久可回放记录。ETag 乐观追加 + 3 次重试在单副本部署下无冲突,重试耗尽 = 全或无。
零门控 本 PR 不改变任何现有决策:没有端点新增拒绝,没有鉴权路径被咨询。字段是数据,直到消费方 PR(按 #1220 §12)显式接入。

本 PR 不做

  • 不门控、不新增拒绝任何现有操作——纯增量(实锤:HasCapability 0 生产调用;审计接线仅 grant/revoke 事件)。
  • 无可查询审计库(Design: L2 permission & capability model (worker config, channels, approval, skill catalog) #1220 §13 Q3)、无审计轮转——持久层假设单 controller 副本(当前 embedded 与 k8s 部署形态)。
  • secret_reveal 消费方(保留值;secret 契约是后续 PR)。
  • 不碰 MCP/ACP、worker-app;CRD 形状唯一变化 = additive capabilities 字段。

测试

  • merge-patch 语义——应用/去重/省略/清空/未知值 400(5 例)
  • HasCapability 矩阵——admin/manager/L2±值/full_access/worker/leader/nil caller
  • 值集对 Design: L2 permission & capability model (worker config, channels, approval, skill catalog) #1220 §3 漂移钉(改名或删值 CI 即红)
  • Matrix 认证填充正负向(L2 human 带值集;SA 永不带)
  • 审计 client——创建/追加/冲突重试/重试耗尽原子性/20 路并发无丢行/封闭 schema secret 卫生(7 测试)
  • handler 接线 E2E——PUT /humans grant×2 + revoke → JSONL 事件断言
  • 门:gofmt / go build ./... / go vet ./... / go test ./...——22 包全 ok、0 失败(基线 + 新 internal/audit 包)。分支 diff 扫描:无凭据形态。
  • 三个 commit 各自独立可 review:① CRD+值集+HasCapability+认证填充 ② API 字段+校验+测试 ③ 审计 client+接线+文档。

相关

…e set and HasCapability helper

L2 permission model (agentscope-ai#1220) step 1 of 3 — capability foundation:

- Human CR gains spec.capabilities (five-value set per agentscope-ai#1220 §3:
  full_access, channel_secrets, external_sources, approval_policy,
  secret_reveal). CRD schemas (controller config + helm) updated;
  deepcopy regenerated via controller-gen.
- internal/auth/capability.go is the single source of truth for the
  value set (constants + ValidCapabilities + Normalize +
  ValidCapabilityList); TestValidCapabilitiesMatchesDocumentedValueSet
  pins the set against the documented five values.
- CallerIdentity.Capabilities is populated for L2 humans by the Matrix
  authenticator (the Human CR is already fetched there — zero new I/O);
  SA-based identities (admin/manager/leader/worker) never carry
  capabilities (agentscope-ai#1220 §5).
- HasCapability(caller, cap): role baseline first (admin/manager true,
  worker false, human/team-leader set membership with full_access
  meta-implication). A capability never implies team scope — callers
  still enforce TeamMatches.

This commit gates no existing operation; the human-update API field and
the audit client land in the follow-up commits.
…ate API

PUT /api/v1/humans/{name} accepts capabilities with the same
merge-patch semantics as accessibleTeams (landed in agentscope-ai#1209): absent =
unchanged, explicit list = replaces, [] = clears. Unknown values are
rejected with a 400 listing the closed value set (the set itself is
owned by internal/auth, so there is exactly one source of truth).
Values are stored in canonical form (deduped + sorted).

Access control is unchanged: only admin/manager may update humans —
self-grant was already structurally impossible (the authorizer has no
human-resource case for leader/human/worker roles) and is pinned by the
pre-existing TestAuthorizer_HumanUpdateAdminOnly (agentscope-ai#1209).

HumanResponse now echoes capabilities so grants are observable on GET.
…nt/revoke events

internal/audit (agentscope-ai#1220 §8):
- layer 1: an immediate structured log line, always (nil storage degrades
  to log-only);
- layer 2: append-only audit/<YYYY-MM-DD>.jsonl (UTC date) written
  read-modify-write with PutObjectIfMatch ETag-optimistic concurrency and
  3 retries (100/200/400ms + jitter); in-process concurrency serialized
  by a client mutex; durable layer assumes a single controller replica.
- Event is a closed schema (no free-text field a secret could hide in),
  pinned by a secret-hygiene test.

UpdateHuman now records one event per changed capability value
(capability_grant / capability_revoke) through the same client later
consumers (agentscope-ai#1216 OFF gate, external_sources restore, secret contract)
will reuse. Audit failures never break the update.

Docs: new design/capability-foundation.md (value set, contract table,
audit design, known limitations); humans-update-api.md, usage
resource-management EN/ZH updated.
@LUOSENGWA
LUOSENGWA marked this pull request as ready for review September 13, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant