Repository navigation
docs(adr-0069): record ruling C′'s enforcement and the API-key admission seam - #23875
Conversation
…ion seam A dated status note names what PR #23440 and PR #23508 enforce on main: the closed posture vocabulary and its scopes, the held outcome of the shared key-admission seam, every key-admitting seam asking the owner's posture, and the forced reset's revocation, on by default. The seam summary gains the API-key admission seam as its sixth entry, so "each row names exactly one of these seams" stays true for D3. Claude-Session: https://claude.ai/code/session_01XgS6gBdsbPNicv9ELY6ne5 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgmentsRead against Right:
Wrong (one sentence):
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL |
…g is written" On the held path resolveApiKeyAdmission stamps the key (last_used_at, and a no-expiry key's grace end) before it returns, so the status note's "nothing is written" was untrue. The note now says what holds: a refusal, not a revocation, no state of its own, the use stamped so the inactivity window does not run out during the hold. Claude-Session: https://claude.ai/code/session_01XgS6gBdsbPNicv9ELY6ne5 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgmentsPatch round on the FAIL record The patched sentence, checked against
Everything else in the record, unchanged and still right (re-read on this head rather than carried over): the sixth seam entry names ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
席位核对 · ACCEPT (Tier H, the maintainer's hand) · PR #23875 @
|
Fixes #23483
Clause-②: no
What this changes
One governed record edit,
docs/adr/0069-enterprise-authentication-hardening.mdalone (+8 / -0). No code, no test, no other ADR.resolveApiKeyAdmissioninpackages/core/src/security/api-key.ts, fed byuserAuthGates, as the key half of D3's amended note. With it, "Each row in D1-D6 names exactly one of these seams" stays true for D3.[auth] no cache service registered在 CacheServicePlugin 注册前 21ms 就喊了 —— 误报,且把人引向「你需要 Redis」 #4772)" convention and leaves the 2026-07-04 table unchanged. It names PR feat(spec,core,plugin-auth)!: an API key answers as its owner's session does when the ADR-0069 policy holds the account (#23287) #23440 (0f059078ec) and PR fix(core,cloud-connection,mcp)!: every API-key admission names its owner-posture source, so a held owner's key is refused at install-local and on every MCP stdio call (#23455, #23456) #23508 (daafae6d6d) and covers four facts:AuthGateCodeSchemaand the scopesAUTH_GATE_SCOPESdeclares;heldoutcome ofresolveApiKeyAdmission;Every sentence measured on
main(34b740f2d1)packages/spec/src/kernel/execution-context.zod.ts:40:AuthGateCodeSchema = z.enum(['PASSWORD_EXPIRED', 'PASSWORD_RESET_REQUIRED', 'MFA_REQUIRED']).:65:AUTH_GATE_SCOPESisRecord<AuthGateCode, AuthGateScope>(typed total), withMFA_REQUIRED: 'account'and both password codes'credential'.packages/core/src/security/api-key.ts:ApiKeyAdmissionhas aheldarm. Near:822,userAuthGatesis asked for the owner, andaccountScopeAuthGatepicks the account-scope posture. The function returns{ outcome: 'held', principal, gate }. Before returning,stampAdmittedKey(:832-:839) stamps the held key's use —last_used_at, at most hourly, and, where the lifecycle policy anchors one, a no-expiry key's grace end asexpires_at— so its inactivity window does not run out during the hold (pinned byapi-key-lifecycle.test.ts:220-:226). Nothing is revoked, and the hold keeps no state of its own.resolveApiKeyPrincipalcollapsesheldtoundefined.Seam sweep. Every non-test caller of
resolveAuthzContext/resolveApiKeyAdmissionpassesuserAuthGateSourceOf(authService):resolve-execution-context.ts:254;rest-server.ts:3338;settings-service-plugin.ts:333;storage-service-plugin.ts:1251and:1428;service-datasource/admin-routes.ts:509;sharing-plugin.ts:973;marketplace-install-local-plugin.ts:3371;plugin.ts:520,:691, throughresolveStdioUserAuthGates).The parameter is typed required (
UserAuthGateSource | undefined). The onlysys_api_keylookup by hash onmainisapi-key.ts:678, insideresolveApiKeyAdmission. No seam is open.packages/plugins/plugin-auth/src/admin-user-endpoints.ts:697:revokeSessionsAndKeys = body.revokeSessionsAndKeys !== false(on unless a literalfalseis passed). Thesys_useraction field hasdefaultValue: true(sys-user.object.ts:306).forced-reset-revocation.tsends sessions through the D4 mechanism and setsrevoked: trueon every unrevoked key.Both PRs are ancestors of
origin/main:merge-base --is-ancestorexits 0 for each, and the control legorigin/main~5also exits 0.Gates
The gates were derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat683acf2c: 19 families. All 19 exit 0.check:doc-formula-expressionsfirst exited 3 (a prerequisite was not built); it was green afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lintunder the verify lock.--ranreconciliation: "19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED (a DERIVED zero)".pnpm check:pm-governed-prosewas also run outside the derivation and exits 0.Acceptance notes
resolveApiKeyAdmission. The card's file surface is the summary entry and the status note, so the D3 note is left as ruled. The sixth entry points back to it ("the key half of D3's amended note").docs/adr/**publishes nothing (the os-dev fast-path no-publish rule), so this PR takesskip-changeset.docs/adr/**): the maintainer lands this PR after the seat'sCONTRACT_REVIEW_TIERreview of the governed text.维护者速读(草稿)
改了什么:只改 ADR-0069 一个文件(+8 行)。「Enforcement seam summary」新增第 6 个强制点「API-key admission」(
resolveApiKeyAdmission)。「Implementation status」表下新增一条 2026-10-14 的状态注记,写明裁决 C′ 已由 PR #23440 与 #23508 落地:posture 代码集是封闭的,并区分 account/credential 两种作用域;MFA 未达标用户的 API key 被「挂起」(held)而不是吊销——挂起不另存状态,姿态解除即恢复;挂起期间照常记录使用时间(last_used_at),以免闲置期限在挂起中耗尽;所有接收 API key 的入口都会查询属主的 posture;管理员强制重置密码时默认吊销该用户的会话与 API key。为什么改:ADR 原文说「D1-D6 每一行恰好对应一个强制点」,但强制点清单里缺少 API key 入口,而 D3 修订注记已经依赖这个入口,记录与代码不一致。现在两个阻塞项(#23455、#23456)都已合并,代码已经兑现,记录可以如实补上,不会写出代码没有兑现的强制。
风险与代价(含回滚):纯文档,不发布任何包,不影响运行时。风险只在于文字是否与代码一致;每一句都已对照
main实测并附了行号。回滚只需 revert 这一个提交。席位意见:建议批准合并。新增的第 6 个强制点和 2026-10-14 状态注记,席位与契约复核都逐句对照
main的代码核过。第一轮契约复核(6214537670)指出「挂起期间什么都不写」不实:挂起的 key 仍会记录使用时间。补丁轮只改了这一句,第二轮复核在新 head18b3f3215b上 PASS(6214838689)。只改 ADR 一个文件,不发布任何包,回滚只需 revert 这一个提交。你要做的:看过席位的合约复核记录后,批准并合并本 PR(Tier H,需要维护者本人操作)。
Generated by Claude Code