Skip to content

qa(authz-conformance), docs(api): record that POST /api/v1/keys mints only for a signed-in session - #27718

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-27708-keys-session-mint-docs
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-27708-keys-session-mint-docs

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #27708

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:授权一致性矩阵新增一行 keys-mint-session-required,记录「只有登录会话才能创建 API key」;两处文档各加一句说明这条规则。
  • 为什么改:harden(keys): minting an API key requires an interactive session #27536 已上线该规则(用 API key 调 POST /api/v1/keys 得到 403 PERMISSION_DENIED),但矩阵和文档都没写,脚本作者只能从 403 才发现。
  • 风险与回滚:零运行时改动,只动一个私有测试包的数据文件和两页文档;回滚即 revert 本 PR。
  • 先看:packages/qa/dogfood/test/authz-conformance.matrix.ts 新行的 enforcement(对照 runtime/domains/keys.ts 第 146 行);content/docs/api/index.mdx 的 API key 条目;content/docs/ai/connect-mcp.mdx 的 "Or mint one over REST" 一句。
  • 两分钟验证:pnpm --filter @objectstack/dogfood exec vitest run test/authz-conformance.test.ts test/authz-probe-blind-spot.test.ts,预期 2 个文件、70 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • packages/qa/dogfood/test/authz-conformance.matrix.ts
    • New row keys-mint-session-required beside anonymous-deny-keys, state: 'enforced'. Its enforcement is read from packages/runtime/src/domains/keys.ts: the domain-wide positive check context.admission?.credential === 'session' (L146), directly below the anonymous decision (L129) and ahead of the body read and the method check. Every other admission (an API key in any of its three header spellings, or no recorded admission) answers 403 PERMISSION_DENIED. The note names the pin file runtime/domains/keys-mint-interactive-session.pin.test.ts (14 cases).
    • The row carries no covers key. This is a predicate inside a domain that anonymous-deny-keys already classifies, so it adds no entry point for discover(). A covers key that no probe mints fails as STALE. This is the matrix's in-resolver convention, and the row's note says so.
    • anonymous-deny-keys: "a signed-in caller reaches the mint exactly as before" now reads "a session-admitted caller ... (a caller admitted by an API key is refused by the next row, keys-mint-session-required)", so the two rows agree.
    • Docblock covers census: "49 of 67 ... 43 of the file's 60 enforced rows" now reads "51 of 69 ... 45 of 62". See the Acceptance notes.
  • content/docs/api/index.mdx (the API key bullet): one sentence saying that minting needs a signed-in session, a request authenticated with an API key is refused 403 PERMISSION_DENIED, and so a key cannot mint another key. "Send it as" becomes "Send the key as" so the pronoun stays unambiguous.
  • content/docs/ai/connect-mcp.mdx (under "Or mint one over REST"): the lead-in now names the signed-in session (the -b cookies.txt in the snippet) and the 403 for an API-key request. Open PR fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 edits this file only at its L242 and L392 hunks, so the two do not overlap.

No runtime change. packages/qa/dogfood is private: true and content/docs does not publish, so no changeset is owed and the PR carries skip-changeset.

Tests

Every reader of the matrix that imports or reads its text was run. authz-conformance.test.ts imports AUTHZ_CONFORMANCE. authz-probe-blind-spot.test.ts reads the matrix text. The other files that grep the name only mention it in comments.

Matrix readers: 2 files, 70 tests, pass (head 2ba6263)
pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/authz-conformance.test.ts test/authz-probe-blind-spot.test.ts
 Test Files  2 passed (2)
      Tests  70 passed (70)
os-verify-lock: VERDICT command-exit 0
The pin the new row cites: 14 tests, pass
pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/domains/keys-mint-interactive-session.pin.test.ts
 Test Files  1 passed (1)
      Tests  14 passed (14)
Dogfood typecheck: exit 0, and the program does include the matrix file

pnpm --filter @objectstack/dogfood run typecheck exits 0. With --listFiles the program holds 409 files under packages/qa/dogfood/test/, one of them authz-conformance.matrix.ts.

Acceptance notes

Verification

Gate families come from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at head 2ba6263244 (3 paths). Each exit code was recorded before any pipe and reconciled with --ran.

Local derived set: 71 derived, 70 run exit 0, 1 NOT MEASURED
  • --ran verdict: 71 derived famil(ies) accounted for — 70 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).
  • NOT MEASURED: pnpm check:dual-build-cjs-loads exits 3 with PREREQUISITE NOT MET, because six packages outside this diff's build closure have no dist/ (studio, embedder-openai, knowledge-memory, knowledge-ragflow, service-cluster-redis, service-knowledge). CI's Lint & Repo Gates runs it on a full build.
  • Build closure: pnpm --filter '@objectstack/dogfood^...' build exits 0. @objectstack/client-react... was also built, because check:skill-examples reads its dist. That gate exited 3 before the build and 0 after it.
  • --ran flagged the tree as 11 commits behind origin/main. The one derivation input that changed in that range is scripts/pm/check-skill-line-ratchet.mjs, which none of the 71 derived families runs.

Generated by Claude Code

… only for a signed-in session

Adds the `keys-mint-session-required` row to the authz-conformance matrix
beside `anonymous-deny-keys` (enforcement read from runtime/domains/keys.ts,
note naming the 14-case pin), aligns `anonymous-deny-keys` to say a
session-admitted caller reaches the mint, refreshes the docblock's covers
census figures, and adds one sentence naming the rule to the API-key
bullet in content/docs/api/index.mdx and under "Or mint one over REST" in
content/docs/ai/connect-mcp.mdx. No runtime change.

Claude-Session: https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 29, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2feee8371a2ca6beacdb1d3ca68c2bb997d323a2 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#2 · os-justin · session_01QSeKYmTYfaPbLQD4BJfomW

席位核对 — ACCEPT · domain:devx#2 · head 2ba6263244 · 2026-10-29T20:52Z

维护者速读

  • 改了什么:授权一致性矩阵加了一行 keys-mint-session-required,记下「只有已登录会话才能创建 API key」这条规则;api/index.mdx 和 connect-mcp.mdx 各加一句话说明它。
  • 为什么改:harden(keys): minting an API key requires an interactive session #27536 已经让「用 API key 调 POST /api/v1/keys」返回 403 PERMISSION_DENIED,但矩阵和文档都没写。写脚本的人要到收到 403 才知道有这条规则。
  • 风险与回滚:不改运行时,只动私有测试包里的一个数据文件和两页文档(3 个文件,+11/−5)。回滚 = 还原本 PR。
  • 先看:矩阵新行的 enforcement,对照 runtime/domains/keys.ts 第 146 行;然后看两页文档各新增的那一句。
  • 两分钟验证:pnpm --filter @objectstack/dogfood exec vitest run test/authz-conformance.test.ts test/authz-probe-blind-spot.test.ts,应为 2 个文件、70 个用例全绿。
  • 需要你做的:无。席位落地。

席位意见:合入。新行是照着代码写的:只放行 credential === 'session',用的是「点名允许的那一种凭证」,不是去列举要拒绝的。两句文档席位逐句对过 keys.ts,都成立。

Shape: draft → main, Fixes #27708. 3 files, +11/−5: packages/qa/dogfood/test/authz-conformance.matrix.ts, content/docs/api/index.mdx and content/docs/ai/connect-mcp.mdx. Labels documentation, size/s, skip-changeset. The dogfood package is private and content/docs ships in no package. No assignees or reviewers: the PR is non-governed, so the seat lands it. The dev opened it through the relay. Report 6507737667, claim 6507004606.

Premise measured: keys.ts:146, if (context.admission?.credential !== 'session'), answers 403 PERMISSION_DENIED. It sits below the anonymous decision and ahead of the body read. The pin keys-mint-interactive-session.pin.test.ts has 14 cases, and the dev saw 14 of 14 pass.

The two docs sentences, checked against the diff (content/docs is a prose face, so the seat checks them at ACCEPT):

Seat's own read of the matrix:

  • The new row is state: 'enforced' and carries no covers, which is the matrix's convention for a predicate inside an already-classified domain. A covers key that no discover() probe mints would fail as STALE.
  • The anonymous-deny-keys row now says "session-admitted caller" and points to the new row.
  • The docblock's unpinned covers census was already one row stale on main (68/50/61/44). It is corrected to the measured 69/51/62/45, and a ruling forbids pinning row counts there.

Gates:

  • On head 2ba6263244: 54 check-runs, 36 success, 18 skipped, 0 failure; mergeable_state: clean.
  • check-expected-skips --pr 27718: OK, all 18 skips are in main's roster (exit 0, roster re-pinned to main's tip).
  • check-governed-merges --pr 27718: NOT governed, 16 changed lines.
  • The dev's union: 71 derived, 70 run with exit 0, and 1 NOT MEASURED by prerequisite (check:dual-build-cjs-loads, packages outside this diff's closure have no dist).
  • Matrix tests 70/70.
  • This head's CI predates ci(live-e2e-ui): run on server-only diffs too (|| core), and pin the 2-minute spec budget #27619, so Live E2E UI skipped here. The merge-queue build runs it, because core is always true on merge_group.

Accepted deviations:

  • The docblock census was corrected as described above (same file).
  • No assignee, per the relay's pr_create rule.
  • The commit uses the repo's model-free trailer pair.

Out of scope: the matrix docblock's census is hand-typed and unpinned, so it can drift again. That goes to Acceptance notes; it is not filed, because pinning a row count is ruled out by the existing ruling. Skills-docs: n/a (Metadata-protocol: none).

Landing: the PR is non-governed with 16 changed lines. In this act the seat readies it and arms auto-merge. On merge, the seat verifies the merged diff and posts the landing note on #27708. There is no cloud session to archive (mode:subagent).


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 20:53
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 20:53
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit d93d9be Oct 29, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-27708-keys-session-mint-docs branch October 29, 2026 21:12
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/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant