Skip to content

[Decision] Is an API key an agent under ADR-0090 D10 rule 4, or a non-agent tool? Today it is classed human and audits as its owner — split out of #17022 #18335

Description

@os-sam

Blocked-by: #17022

Split out of #17022 by the triage seat (#6015, session_01UXnFshug1c4AVcjrqPy4jq, R+238) answering that card's pm:retriage. The reasoning and the full measurement table are in that card's comment; this card carries only the door that was split off, plus the question that made it separable.

Measured (origin/main, this act)

reading result
agent channel in packages/core/src/security/assemble-execution-context.ts oauth?.clientId only:293, consumed at :316 (principalKind) and :317 (onBehalfOf)
keyId / apiKey / api_key in that file 0 — controls userId 9, onBehalfOf 4 fire ⇒ discriminating probe
principalKind in api-key.ts + resolve-authz-context.ts 0 — control: same term reads 7 in assemble-execution-context.ts, 9 in its test, same directory

An API-key caller is not classed as an agent anywhere. It carries no principalKind from its own path and falls through the :316 ternary to 'human' — so its writes audit as a human's own, and no ADR-0090 D10 dual attribution is even attempted for it.

⛔ This is not the same defect as #17022. There the agent exists and has nowhere to record its identity; here the principal is never classed as an agent at all. Different upstream, different files, different fix — which is why it is a separate card.

Why it is a decision and not execution

ADR-0090 D10 rule 4 says 「every write records performed_by (agent) + on_behalf_of (user) + run id」. Whether an API key is an agent acting on behalf of its owner or a service principal in its own right changes who the audit row blames and what the permission model grants — ⛔ the tree does not answer it, and it sits on the 安全/权限边界 floor.


维护者速读

事情。 平台有两种"不是人在按键"的调用方式:一种是 MCP 的 OAuth 客户端(AI 代理),另一种是 API key。审计日志要求记下"谁代谁干的"。OAuth 那一路的修复已经在 #17022 上,这张卡只问 API key 这一路。

现状(实测)。 API key 调进来时,系统根本不把它当成代理 —— 它被当作"人"记账,写进审计日志的就是钥匙主人本人,看不出这是一把钥匙自动跑的。

问题。 一把 API key 到底算什么?

  • A 算"人的工具",不是代理 —— 维持现状,把"API key 不做双重归属"这件事写进 ADR-0090 D10 记录在案,说明为什么它不在 rule 4 的范围内。一行文档,零代码。
  • B 算"代理" —— 让 API key 也走双重归属:审计行记下是哪把钥匙、代表谁。要动认证上游两个文件,并且要决定钥匙在权限上是不是独立主体。

席位推荐:A。 三条理由:今天没有任何人提出过要按 API key 区分审计(零实测拉动);创业阶段默认不扩散能力面;而 B 会把"钥匙"变成权限模型里的新主体类别,那是比审计大得多的一件事。⛔ 但这是安全/权限边界,所以不代裁。

你要做的:回一个字母 —— A 或 B。


四棱分析

合议:① ④ 指向 A,② 倾向 A,③ 中性但给 A 附一个强制交付物。 ⛔ 安全/权限边界在人工地板上 ⇒ 呈报,⛔ 不代裁。

Governing text: ADR-0090 D10 agent rule 4(逐字「Dual attribution: every write records performed_by (agent) + on_behalf_of (user) + run id」)· 维护者 2026-08-04 创业阶段聚焦指示 · SKILL.md 人工地板「安全/权限边界」条。


Sequencing

Blocked-by: #17022either answer needs that card's carrier landed first: under B it is the field to populate; under A the ADR sentence being amended is the same one #17022's follow-up docs PR touches, and ⛔ two cards must not edit docs/adr/0090-*.md in parallel (#17189 already collided there once).

Dedup words: API key agent principal · principalKind service · ADR-0090 D10 rule 4 · dual attribution keyId · resolve-authz-context principalKind


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions