Skip to content

Show operators how to use an API key, and let them pause one (#142) - #143

Merged
CaYatur merged 2 commits into
mainfrom
feat/api-key-usage-toggle
Jul 29, 2026
Merged

Show operators how to use an API key, and let them pause one (#142)#143
CaYatur merged 2 commits into
mainfrom
feat/api-key-usage-toggle

Conversation

@CaYatur

@CaYatur CaYatur commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Closes #142.

The samples

src/shared/apiUsage.ts holds curl, JavaScript and Python samples built against this install's own base URL. One pure source, so the desktop app, the web panel and the docs page cannot drift apart. They carry PASTE_YOUR_KEY_HERE rather than a plausible-looking fake, because the real secret is unrecoverable the moment the creation dialog closes.

usageSamples is deliberately self-contained — it spells /api/v1 and X-API-Key out instead of naming the shared constants. The panel serves it through .toString(), and a reference to a module-level binding becomes a ReferenceError in the page as soon as the bundler renames it. This codebase has walked into that trap before.

The smoke re-parses the function with no scope around it and calls it. Reading the source for a forbidden identifier would not work — the bundler renames it, so the dead reference is no longer called API_PREFIX by the time it reaches the page. Only running it detached catches this.

Disable is not revoke

Revoking is permanent, and is what you do to a credential you think has leaked. Disabling is what you do to an integration you are pausing. Collapsing them into one button loses that distinction, so they are two buttons. Enabling a revoked key answers 409 rather than quietly resurrecting it.

The switch is honoured inside isKeyUsable, the single answer to "may this key be used". A switch obeyed in some places and not others is worse than no switch.

What the self-review found

The new route had no HTTP coverage — the smoke called setKeyDisabled in-process, so "the route works" and "owner only" were claims, not tests. Adding the request surfaced the real defect:

All three key-mutation routes documented their body field as id. The server has always read keyId. Anyone following the reference exactly got 404 from revoke, 404 from disable, and 200 from a delete that deleted nothing — the exact failure this issue is about, living inside the document meant to prevent it.

The test now takes the field name from the route table rather than spelling it itself, so a document that lies cannot sit beside a test that passes.

Verification

12/12 gates on a fresh build. Three checks proved failable by reintroducing the bug:

Reintroduced Gate says
isKeyUsable ignores disabled a disabled key still worked: 200
a sample points back at API_PREFIX ReferenceError: API_PREFIX is not defined
the table documents id again owner disable over HTTP: 404 {"error":"key-not-found"}

CaYatur added 2 commits July 29, 2026 15:36
A key an operator cannot work out how to send is a key that does nothing.
The docs page lists every route and never says which header carries the
credential, so `src/shared/apiUsage.ts` now holds curl, JavaScript and
Python samples built against this install's own base URL - one pure
source, so the desktop app, the web panel and the docs cannot disagree.

`usageSamples` is deliberately self-contained. The panel serves it via
`.toString()`, and a reference to a module-level binding becomes a
ReferenceError in the page the moment the bundler renames it - a trap
this codebase has fallen into before. The smoke now re-parses the
function with no scope around it and calls it, which is the only check
that turns that into a failure here; reading the source for the
identifier would not, because the bundler renames it.

Disabling is new and is not revoking. Revoking is permanent and is what
you do to a credential you believe has leaked; disabling is what you do
to an integration you are pausing, and it can be undone. Enabling a
revoked key is refused (409) rather than quietly resurrecting it.

The switch is honoured in `isKeyUsable`, the single answer to "may this
key be used" - a switch obeyed in some places and not others is worse
than no switch at all.

Verified: 12/12 gates. Both new checks proved failable by
reintroducing the bug - ignoring `disabled` gives "a disabled key still
worked: 200", and pointing a sample back at `API_PREFIX` gives the real
"ReferenceError: API_PREFIX is not defined" from the detached call.
…#142)

Two gaps in the first commit.

The new route had no HTTP coverage at all - the smoke called
`setKeyDisabled` in-process and never issued a request, so "owner only"
and "the route works" were both claims rather than tests. Adding the
request found the second, larger problem.

All three key-mutation routes documented their body field as `id`. The
server has always read `keyId`. An integrator following the reference
exactly got 404 from revoke, 404 from disable, and 200 from a delete
that deleted nothing - the precise failure this issue is about, sitting
inside the document meant to prevent it.

So the test now takes the field name *from the route table* instead of
spelling it itself. A test that hardcoded `keyId` would have passed
happily beside a document that said `id`; this one cannot. Restoring
`id` in the table turns the gate red with "owner disable over HTTP: 404
{"error":"key-not-found"}", which is exactly what the operator saw.

Also asserts what was only asserted in a comment: an API key, even one
holding every scope, gets 403 from the disable route. A key that can
switch keys off is the same escalation that keeps key minting off the
API.

Verified: 12/12 gates, on a fresh build.
Copilot AI review requested due to automatic review settings July 29, 2026 12:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@CaYatur
CaYatur merged commit da1dd6b into main Jul 29, 2026
1 check passed
@CaYatur
CaYatur deleted the feat/api-key-usage-toggle branch July 29, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API keys: no one can tell how to use one, and there is no way to pause one

2 participants