Symptom
There is no product route that revokes an API key.
- Observed:
PATCH /api/v1/data/sys_api_key/{id} {revoked:true} (as admin) → 405OBJECT_API_METHOD_NOT_ALLOWED, allowed:[get,list,aggregate,history,export]. Same 405 for {revoked:false} (restore). Reproduced 3×, including through the real UI: Setup → API Keys → Open menu → Revoke API Key → Continue produces an error toast, the key keeps authenticating, and the row still reads revoked=false. - Expected: 200, and the key stops authenticating.
No alternative route exists — route-ledger.ts carries only POST /keys; /api/v1/auth/api-key/* and DELETE /api/v1/keys/{id} all 404.
Root cause
packages/platform-objects/src/identity/sys-api-key.object.tscontradicts itself:
So the two halves cancel out: the declared action fires a PATCH the object refuses at the method gate. Enforcement of the flag is fine — setting revoked=1 out-of-band makes the very next x-api-key call 401 UNAUTHENTICATED — so the missing piece is purely the write path. Fix is either to allow update scoped to the revoked column, or to give revoke/restore a dedicated auth route.
Route: domain:metadata (the contradiction lives entirely in the platform-objects object definition — both the action declarations and the apiMethods gate are there). If the maintainer instead chooses the dedicated-auth-route fix, the work moves to a route and would be domain:cli — but as located the defect and its fix sit in the platform object, so domain:metadata.
Security consequence
⚠️A leaked API key cannot be revoked without direct database access. The QA run filed this publicly deliberately: it is a dead feature rather than a bypassable gate — knowing about it grants an attacker nothing they don't already have. Adding security for triage visibility.
Nothing pins this today: the unit tests (http-dispatcher.keys.test.ts, resolve-execution-context.test.ts) exercise key resolution with a pre-revoked row and never call the PATCH route the action declares.
Reproduction
POST /api/v1/keys {name} → 201.PATCH /api/v1/data/sys_api_key/{id} {revoked:true} as admin → 405OBJECT_API_METHOD_NOT_ALLOWED.- The key still authenticates;
revoked still reads false. Same via the Setup → API Keys → Revoke UI (error toast).
Source
Extracted from the QA run #7663 (framework 92f26f7, console 09987b680).
Symptom
There is no product route that revokes an API key.
PATCH /api/v1/data/sys_api_key/{id} {revoked:true}(as admin) → 405OBJECT_API_METHOD_NOT_ALLOWED,allowed:[get,list,aggregate,history,export]. Same 405 for{revoked:false}(restore). Reproduced 3×, including through the real UI: Setup → API Keys → Open menu → Revoke API Key → Continue produces an error toast, the key keeps authenticating, and the row still readsrevoked=false.No alternative route exists —
route-ledger.tscarries onlyPOST /keys;/api/v1/auth/api-key/*andDELETE /api/v1/keys/{id}all 404.Root cause
packages/platform-objects/src/identity/sys-api-key.object.tscontradicts itself:revoke_api_key/restore_api_keyasmethod:'PATCH',target:'/api/v1/data/sys_api_key/{id}',bodyExtra:{revoked:true|false}(theactionsblock, ~lines 42–72 onorigin/main).enable.apiMethods = ['get', 'list'](line 219), with the comment "managedBy is not enforced: generic CRUD bypasses better-auth on sys_team (data-integrity / security) #1591 — reads only: writes are refused by the identity write guard (ADR-0092 D2)… HTTP answers 405 before the 403".So the two halves cancel out: the declared action fires a PATCH the object refuses at the method gate. Enforcement of the flag is fine — setting
revoked=1out-of-band makes the very nextx-api-keycall 401UNAUTHENTICATED— so the missing piece is purely the write path. Fix is either to allowupdatescoped to therevokedcolumn, or to give revoke/restore a dedicated auth route.Route:
domain:metadata(the contradiction lives entirely in theplatform-objectsobject definition — both the action declarations and theapiMethodsgate are there). If the maintainer instead chooses the dedicated-auth-route fix, the work moves to a route and would bedomain:cli— but as located the defect and its fix sit in the platform object, sodomain:metadata.Security consequence
securityfor triage visibility.Nothing pins this today: the unit tests (
http-dispatcher.keys.test.ts,resolve-execution-context.test.ts) exercise key resolution with a pre-revoked row and never call the PATCH route the action declares.Reproduction
POST /api/v1/keys {name}→ 201.PATCH /api/v1/data/sys_api_key/{id} {revoked:true}as admin → 405OBJECT_API_METHOD_NOT_ALLOWED.revokedstill readsfalse. Same via the Setup → API Keys → Revoke UI (error toast).Source
Extracted from the QA run #7663 (framework 92f26f7, console 09987b680).