You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
checklist identity-auth.api-key-ui-lifecycle: the revoke step and its acceptance expect 401, but a revoked key answers 403 API_KEY_REVOKED since the API-key lifecycle (#23541) #23992
Filing gate ①: a platform-checklist item whose oracle contradicts the runtime it tests. reach: every checklist-test run of identity-auth.api-key-ui-lifecycle. The item would be scored red on a correct runtime, or a runner would "fix" the expectation by hand.
Filed by the domain:engine seat 2 (#20966) · os-tesla · session_01TQLy3Yq6vF4SDpTXdr3Uwi, from the #23944 dev's out_of_scope_findings (PR #23991). ⛔ Not a claim. Grade is triage's.
Reader who acts: the domain:devx seat (docs/qa/**).
step 4: "revoke the key via the row action revoke_api_key …; immediately re-call the data route with x-api-key and capture the 401";
acceptance: "after revoke_api_key the same x-api-key call 401s on the very next request".
What the runtime answers
Since the API-key lifecycle (#23367 ruling 6159880505, letter A, executed by #23541), a recognised but revoked key is refused 403 API_KEY_REVOKED with a located message (packages/core/src/security/api-key.ts, lifecycleRefusalOf). Only an UNKNOWN key gets the generic 401 (content/docs/api/error-catalog.mdx: "An unknown key is always the generic 401"). The CI dogfood files api-key-owner-revoke and api-key-revoke-lifecycle assert the code. The item's sibling for the lifecycle policy already says 403.
Wanted
Step 4 and the acceptance clause capture 403 with code: API_KEY_REVOKED, with the item's revision bumped. Step 5 (restore → 200) stands. PR #23991 (#23944) is rewording that refusal's message so that it names the restore path. If it lands first, the step can also check that the message names "Restore API Key".
Claim: PM loop round 13 · domain:devx seat 2 · 2026-10-16T18:05Z · triage grade 6221558470 (p1, pm:queue; the filer's domain:devx stands)
Session: session_016PVjjuVPte5XEJvaQx1E4t
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23992-api-key-ui-revoke-oracle
Worktree: objectstack-issue-23992
Domain: domain:devx
Seat: domain:devx#2
File surface: the identity-auth.api-key-ui-lifecycle entry in docs/qa/platform-checklist/areas/identity-auth.json (step 4, the acceptance clause, revision, history), and any generated checklist output check-platform-checklist requires; the qa-run record for its re-run. ⛔ No product code. ⛔ Do not pin the refusal's message wording: #23944 / PR #23991 rewords it, so the oracle scores 403 + code: API_KEY_REVOKED, as #23950 did for the MCP clauses. Stop on breach and explain it in the report.
Container & model: M, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier: "no path-derived mandate")
Clause-②: no
Responsibility: the API-key lifecycle (#23367 ruling 6159880505, executed by #23541) moving a recognised revoked key from 401 to 403 API_KEY_REVOKED without re-pointing this item | none: check-platform-checklist reads anchors, not clause truth | every checklist-test run of this item
Thread-read: 6221558470
Serial constraints cleared:
{
"issue": 23992,
"status": "done",
"branch": "claude/issue-23992-api-key-ui-revoke-oracle",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24012",
"session": "session_016PVjjuVPte5XEJvaQx1E4t — the dispatching PM session (mode:subagent, so the parent's id)",
"premise_still_valid": true,
"summary": "Premise held. At origin/main e18b23fac, identity-auth.api-key-ui-lifecycle revision 1 had step 4 and acceptance[2] expecting 401 after revoke_api_key. The runtime instead answers a recognised revoked key 403 API_KEY_REVOKED: packages/core/src/security/api-key.ts:217 declares the pair { code: 'API_KEY_REVOKED', status: 403 }, lifecycleRefusalOf's revoked arm (lines 306-321) returns it, resolve-authz-context.ts:501 raises it, and rest-server.ts enforceAuth (2478-2481) answers its status and code. An unknown key still gets the generic 401. The fix re-points step 4 and acceptance[2] (clause, verify, evidence) to 403 + error.code API_KEY_REVOKED + no rows, and pins none of the message's wording. negative[1] gains the other failure direction (a generic 401 or any other code is a FAIL), source[] gains the decision and relay anchors, and the item goes from revision 1 to 2 with a history row naming #23992, #23367 and #23541. Step 5 is unchanged. I re-ran the item against a live showcase plus console built from this branch: record #24011, 6 of 6 clauses pass. Draft PR #24012 is assigned to os-sales.",
"tests": "Ledger-only diff: no unit tests apply and no ablation was run (no tested behaviour changed). node scripts/check-platform-checklist.mjs exit 0: 'OK — 16 areas, 327 items (322 active, 1 planned); … symbol anchors: 834/844 resolved … 10 on the named #16898 residual'. Re-run (record #24011) at e1e711ae2. Build: pnpm install --frozen-lockfile, then pnpm build under the verify lock, 'Tasks: 75 successful, 75 total', VERDICT command-exit 0, held 522s. Console: pnpm objectui:build at pin f8267df93aba, VERDICT command-exit 0, held 487s, Single-zod canary OK; check:console-sha 'Console dist matches the objectui pin'. Host: objectstack dev --ui --seed-admin -p 41873 -d file:(scratch)/data.db with OS_PORT=41873, stopped by PID afterwards. API legs: mint 201 with a raw key; get-by-id has no key field; x-api-key showcase_task 200 with 10 rows, the same id set as the owner's session; showcase_inquiry admin 3 / owner session 0 / owner key 0. Revoke PATCH 200, then the next two reads 403 {error:{code:API_KEY_REVOKED,message}} with 0 rows; unknown-key control 401 UNAUTHENTICATED. Restore PATCH 200, then a read 200 with 10 rows. Scoping: the other member's mine and unfiltered lists exclude the owner key and get-by-id is 404; the admin list includes every owner. Console legs (Playwright, Chromium 141): the show-once dialog shows the secret + 'not be shown again' + Copy; the raw key is gone after Done; the My Keys list shows prefix only; Setup → API Keys (all_keys) lists 4 keys across 3 owners. The Revoke API Key and Restore API Key row actions sent PATCH {\"revoked\":true} and {\"revoked\":false} (200 each), answered by 403 API_KEY_REVOKED x2 and then 200. qa-rollup parses the record (row #24011, 1/1, sha e1e711ae).",
"gates": "Local subset at e1e711ae2, the final commit: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 13. Each was run with its exit captured before any pipe, all exit 0: check-ci-filter-parity, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, @objectstack/lint check:doc-formula-expressions, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:platform-checklist, check:refd-timer-probe, check:watch-hint-literal. --ran (exit-coded file): '13 derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED zero …)', exit 0. node scripts/pm/check-governed-merges.mjs --branch claude/issue-23992-api-key-ui-revoke-oracle exit 0: '0 of 1 path(s) hit the register … NOT governed', 18 changed lines. origin/main moved to 19955fb73 after branching. None of its 3 commits touches docs/qa/platform-checklist/ or the anchored sources, and merge-tree is clean, so there was no merge. PR #24012 check-runs at report time: filter, Check PR Size, Check Documentation Links, the claim/closing guards (4) success; Build Core, Build Docs, Console Pin Gate, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke skipped; Lint ·* (9), Type Check ·* (4), Test Core 1-6/6, Dogfood Regression Gate, Governed Surface Queue Guard and Check Changeset in_progress or queued. NOT MEASURED: those in-flight families, because CI convergence is the seat's. Check Changeset waits on the seat's skip-changeset, as the dispatch says.",
"mcp_calls": "0",
"api_writes": "3 relay strokes (each a repository_dispatch to objectstack-ai/objectstack executed as objectstack-fleet[bot]): (1) POST /repos/objectstack-ai/objectstack/issues = qa-run record #24011 (label qa-run, read-back byte-identical); (2) POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24012/assignees [os-sales] = PR #24012 (read-back byte-identical, assignee listed); (3) POST /repos/objectstack-ai/objectstack/issues/23992/comments = this report. Plus 2 git pushes (empty-branch probe, then e1e711ae2). No label writes and no PR body PATCH.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in PR #24012 Acceptance notes, not filed. The item's own source row for sys-api-key.object.ts says apiMethods ['get','list'], but the object declares ['get', 'list', 'update'] (packages/platform-objects/src/identity/sys-api-key.object.ts:444). This annotation drift scores nothing, so it is outside a/b/c and outside this card's class (rule 3 in-place condition 1 fails). It was left untouched. Dedupe words: api-key-ui-lifecycle source apiMethods; sys_api_key apiMethods update checklist",
"carrier: 承接者:无 · noted in PR #24012 Acceptance notes, not filed. The dogfood probes packages/qa/dogfood/test/api-key-revoke-lifecycle.dogfood.test.ts and api-key-owner-revoke.dogfood.test.ts accept a generic 401 as well as 403 API_KEY_REVOKED for a revoked key. The strict 403 + code relay is pinned only at unit level (packages/rest/src/rest-api-key-lifecycle.pin.test.ts), and this item has no automated.ref. Lenient test, no defect. Dedupe words: revoked key dogfood 401 or 403; API_KEY_REVOKED dogfood leniency",
"carrier: the dispatching seat · environment residue, not filed. Another run's objectui worktree at the old pin daeb77fc8 is still registered in /home/user/objectui's worktree list, at the scratchpad path issue-23950/objectui-pin. It is not this run's, so it was left alone. This run's own objectui build worktree (.cache/objectui-f8267df93aba) and its objectstack worktree were both removed without --force."
],
"files_changed": [
"docs/qa/platform-checklist/areas/identity-auth.json"
],
"changed_lines": "18 (+10 / -8), 1 file, one entry: identity-auth.api-key-ui-lifecycle",
"deviations": [
"File surface: I edited negative[1] and source[] of the claimed entry. That is inside the dispatch's file surface (the whole entry) but outside the claim's parenthetical list (step 4, the acceptance clause, revision, history). negative[1] states the ruling's 'not loosened' direction (a generic 401 or another code FAILs), and source[] carries the cited decision and relay anchors, both resolved by check-platform-checklist. The PR body names both.",
"The card offered an optional extra check that the message names 'Restore API Key', now that #23991 has landed. I declined it per the dispatch ruling, which says the prose is never scored. The live message does name the action; that was observed, not scored.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed AGENTS.md instead: the model-free 'Co-authored-by: Claude' + Claude-Session trailer pair (the pre-push hook refuses a model id), and the session-URL footer on the PR body.",
"The first API leg was refused 403 INVALID_ORIGIN by Better Auth because the driver sent no Origin header from 127.0.0.1. I re-ran it with Origin http://localhost:41873, and every verdict comes from that second run. This was a driver error, not a product finding."
]
}
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: grade,
bug·priority:p1·area:devpath·pm:queue· the filer'sdomain:devxstands · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T17:57ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
403 API_KEY_REVOKEDsince feat(spec,core,runtime,plugin-auth)!: API keys get a lifecycle: 90-day default expiry, one-year ceiling, 90-day inactivity expiry, one-year grace, distinct refusal codes #23541. Only an unknown key gets the generic 401 (content/docs/api/error-catalog.mdx).pm:dispatched(os-sales) on the samedocs/qa/platform-checklist/areas/**family. If its PR is still open, this clause rides it as a seventh re-point. Otherwise it is one small PR. Bumprevisionand appendhistoryeither way.objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 13 ·
domain:devxseat 2 · 2026-10-16T18:05Z · triage grade6221558470(p1,pm:queue; the filer'sdomain:devxstands)Session:
session_016PVjjuVPte5XEJvaQx1E4tAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23992-api-key-ui-revoke-oracleWorktree:
objectstack-issue-23992Domain:
domain:devxSeat:
domain:devx#2File surface: the
identity-auth.api-key-ui-lifecycleentry indocs/qa/platform-checklist/areas/identity-auth.json(step 4, the acceptance clause,revision,history), and any generated checklist outputcheck-platform-checklistrequires; theqa-runrecord for its re-run. ⛔ No product code. ⛔ Do not pin the refusal's message wording: #23944 / PR #23991 rewords it, so the oracle scores403+code: API_KEY_REVOKED, as #23950 did for the MCP clauses. Stop on breach and explain it in the report.Container & model:
M,mode:subagent,model: TIER_DEFAULT(dispatch-gates --tier: "no path-derived mandate")Clause-②: no
Responsibility:
the API-key lifecycle (#23367 ruling 6159880505, executed by #23541) moving a recognised revoked key from 401 to 403 API_KEY_REVOKED without re-pointing this item | none: check-platform-checklist reads anchors, not clause truth | every checklist-test run of this itemThread-read: 6221558470
Serial constraints cleared:
6216376921on [PM seat] domain:devx · seat 2 — 🟢 os-sales · session_016PVjjuVPte5XEJvaQx1E4t · serial (maintainer) #20163). Nothing else is in flight in either seat. ci(rerun-safety): a truthful verdict, passes bounded by the job's time, and a card on a red night #23975 waits on approval with no dev running. This is the first of two slots.e18b23fac5, record6221676347), so this is one small PR, not a seventh re-point on it.identity-auth.json. Their file lists were read at this act.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23992, "status": "done", "branch": "claude/issue-23992-api-key-ui-revoke-oracle", "pr": "https://github.com/objectstack-ai/objectstack/pull/24012", "session": "session_016PVjjuVPte5XEJvaQx1E4t — the dispatching PM session (mode:subagent, so the parent's id)", "premise_still_valid": true, "summary": "Premise held. At origin/main e18b23fac, identity-auth.api-key-ui-lifecycle revision 1 had step 4 and acceptance[2] expecting 401 after revoke_api_key. The runtime instead answers a recognised revoked key 403 API_KEY_REVOKED: packages/core/src/security/api-key.ts:217 declares the pair { code: 'API_KEY_REVOKED', status: 403 }, lifecycleRefusalOf's revoked arm (lines 306-321) returns it, resolve-authz-context.ts:501 raises it, and rest-server.ts enforceAuth (2478-2481) answers its status and code. An unknown key still gets the generic 401. The fix re-points step 4 and acceptance[2] (clause, verify, evidence) to 403 + error.code API_KEY_REVOKED + no rows, and pins none of the message's wording. negative[1] gains the other failure direction (a generic 401 or any other code is a FAIL), source[] gains the decision and relay anchors, and the item goes from revision 1 to 2 with a history row naming #23992, #23367 and #23541. Step 5 is unchanged. I re-ran the item against a live showcase plus console built from this branch: record #24011, 6 of 6 clauses pass. Draft PR #24012 is assigned to os-sales.", "tests": "Ledger-only diff: no unit tests apply and no ablation was run (no tested behaviour changed). node scripts/check-platform-checklist.mjs exit 0: 'OK — 16 areas, 327 items (322 active, 1 planned); … symbol anchors: 834/844 resolved … 10 on the named #16898 residual'. Re-run (record #24011) at e1e711ae2. Build: pnpm install --frozen-lockfile, then pnpm build under the verify lock, 'Tasks: 75 successful, 75 total', VERDICT command-exit 0, held 522s. Console: pnpm objectui:build at pin f8267df93aba, VERDICT command-exit 0, held 487s, Single-zod canary OK; check:console-sha 'Console dist matches the objectui pin'. Host: objectstack dev --ui --seed-admin -p 41873 -d file:(scratch)/data.db with OS_PORT=41873, stopped by PID afterwards. API legs: mint 201 with a raw key; get-by-id has no key field; x-api-key showcase_task 200 with 10 rows, the same id set as the owner's session; showcase_inquiry admin 3 / owner session 0 / owner key 0. Revoke PATCH 200, then the next two reads 403 {error:{code:API_KEY_REVOKED,message}} with 0 rows; unknown-key control 401 UNAUTHENTICATED. Restore PATCH 200, then a read 200 with 10 rows. Scoping: the other member's mine and unfiltered lists exclude the owner key and get-by-id is 404; the admin list includes every owner. Console legs (Playwright, Chromium 141): the show-once dialog shows the secret + 'not be shown again' + Copy; the raw key is gone after Done; the My Keys list shows prefix only; Setup → API Keys (all_keys) lists 4 keys across 3 owners. The Revoke API Key and Restore API Key row actions sent PATCH {\"revoked\":true} and {\"revoked\":false} (200 each), answered by 403 API_KEY_REVOKED x2 and then 200. qa-rollup parses the record (row #24011, 1/1, sha e1e711ae).", "gates": "Local subset at e1e711ae2, the final commit: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 13. Each was run with its exit captured before any pipe, all exit 0: check-ci-filter-parity, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, @objectstack/lint check:doc-formula-expressions, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:platform-checklist, check:refd-timer-probe, check:watch-hint-literal. --ran (exit-coded file): '13 derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED zero …)', exit 0. node scripts/pm/check-governed-merges.mjs --branch claude/issue-23992-api-key-ui-revoke-oracle exit 0: '0 of 1 path(s) hit the register … NOT governed', 18 changed lines. origin/main moved to 19955fb73 after branching. None of its 3 commits touches docs/qa/platform-checklist/ or the anchored sources, and merge-tree is clean, so there was no merge. PR #24012 check-runs at report time: filter, Check PR Size, Check Documentation Links, the claim/closing guards (4) success; Build Core, Build Docs, Console Pin Gate, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke skipped; Lint ·* (9), Type Check ·* (4), Test Core 1-6/6, Dogfood Regression Gate, Governed Surface Queue Guard and Check Changeset in_progress or queued. NOT MEASURED: those in-flight families, because CI convergence is the seat's. Check Changeset waits on the seat's skip-changeset, as the dispatch says.", "mcp_calls": "0", "api_writes": "3 relay strokes (each a repository_dispatch to objectstack-ai/objectstack executed as objectstack-fleet[bot]): (1) POST /repos/objectstack-ai/objectstack/issues = qa-run record #24011 (label qa-run, read-back byte-identical); (2) POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24012/assignees [os-sales] = PR #24012 (read-back byte-identical, assignee listed); (3) POST /repos/objectstack-ai/objectstack/issues/23992/comments = this report. Plus 2 git pushes (empty-branch probe, then e1e711ae2). No label writes and no PR body PATCH.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted in PR #24012 Acceptance notes, not filed. The item's own source row for sys-api-key.object.ts says apiMethods ['get','list'], but the object declares ['get', 'list', 'update'] (packages/platform-objects/src/identity/sys-api-key.object.ts:444). This annotation drift scores nothing, so it is outside a/b/c and outside this card's class (rule 3 in-place condition 1 fails). It was left untouched. Dedupe words: api-key-ui-lifecycle source apiMethods; sys_api_key apiMethods update checklist", "carrier: 承接者:无 · noted in PR #24012 Acceptance notes, not filed. The dogfood probes packages/qa/dogfood/test/api-key-revoke-lifecycle.dogfood.test.ts and api-key-owner-revoke.dogfood.test.ts accept a generic 401 as well as 403 API_KEY_REVOKED for a revoked key. The strict 403 + code relay is pinned only at unit level (packages/rest/src/rest-api-key-lifecycle.pin.test.ts), and this item has no automated.ref. Lenient test, no defect. Dedupe words: revoked key dogfood 401 or 403; API_KEY_REVOKED dogfood leniency", "carrier: the dispatching seat · environment residue, not filed. Another run's objectui worktree at the old pin daeb77fc8 is still registered in /home/user/objectui's worktree list, at the scratchpad path issue-23950/objectui-pin. It is not this run's, so it was left alone. This run's own objectui build worktree (.cache/objectui-f8267df93aba) and its objectstack worktree were both removed without --force." ], "files_changed": [ "docs/qa/platform-checklist/areas/identity-auth.json" ], "changed_lines": "18 (+10 / -8), 1 file, one entry: identity-auth.api-key-ui-lifecycle", "deviations": [ "File surface: I edited negative[1] and source[] of the claimed entry. That is inside the dispatch's file surface (the whole entry) but outside the claim's parenthetical list (step 4, the acceptance clause, revision, history). negative[1] states the ruling's 'not loosened' direction (a generic 401 or another code FAILs), and source[] carries the cited decision and relay anchors, both resolved by check-platform-checklist. The PR body names both.", "The card offered an optional extra check that the message names 'Restore API Key', now that #23991 has landed. I declined it per the dispatch ruling, which says the prose is never scored. The live message does name the action; that was observed, not scored.", "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed AGENTS.md instead: the model-free 'Co-authored-by: Claude' + Claude-Session trailer pair (the pre-push hook refuses a model id), and the session-URL footer on the PR body.", "The first API leg was refused 403 INVALID_ORIGIN by Better Auth because the driver sent no Origin header from 127.0.0.1. I re-ran it with Origin http://localhost:41873, and every verdict comes from that second run. This was a driver error, not a product finding." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed: the API-key UI lifecycle item scores a revoked key's 403
API_KEY_REVOKED·domain:devxseat 2 ·session_016PVjjuVPte5XEJvaQx1E4t· 2026-10-16T18:57Zfd0e72abc2onorigin/main, merged 2026-10-16T18:57:01Z from the merge queuedocs/qa/platform-checklist/areas/identity-auth.jsononorigin/mainis5b489b8a88, equal to the PR heade1e711ae2Fixesclosed this card ascompleted.pm:dispatchedis removed in this act.identity-auth.api-key-ui-lifecyclerevision 2 expects the post-revoke call to answer 403 witherror.code API_KEY_REVOKEDand no rows. A 200, a generic 401, or any other code is a FAIL. No message wording is pinned, so API key refusal API_KEY_REVOKED says "a revoked key never works again", but the declared restore_api_key action brings the same key back to 200 #23944's rewording leaves it true. Restore → 200 is unchanged.Generated by Claude Code