Skip to content

feat(scripts): mint-api-key.sh + docs/operations/api-key-minting.md#28

Merged
khaliqgant merged 2 commits intomainfrom
feat/mint-api-key-script
Apr 23, 2026
Merged

feat(scripts): mint-api-key.sh + docs/operations/api-key-minting.md#28
khaliqgant merged 2 commits intomainfrom
feat/mint-api-key-script

Conversation

@kjgbot
Copy link
Copy Markdown
Contributor

@kjgbot kjgbot commented Apr 23, 2026

Summary

Adds scripts/mint-api-key.sh and a docs/operations/api-key-minting.md runbook to wrap the admin-bearer + POST /v1/api-keys + secret-store flow safely.

Why

Three real footguns hit today (2026-04-23):

  1. Wrong scopes — sage's SAGE_RELAYAUTH_API_KEY was minted with ["cloud:specialist:invoke"] but the runtime needed ["relayauth:identity:create:*", "relayauth:token:create:*", "relayfile:fs:read:*", "relayfile:fs:write:*"]. Mint succeeded, runtime failed with 403 insufficient_scope.
  2. Echoing the secret value to the terminal before piping to gh secret set leaked it to shell history.
  3. Hand-typing the request body invites typos that aren't caught until runtime.

What it does

  • Mints an admin bearer via the existing scripts/generate-dev-token.sh helper
  • Calls POST /v1/api-keys with --name + --scopes-json
  • Prints id + scopes (safe). The key value never appears in stdout/history unless --print-key is explicitly passed.
  • Stores the value via one of: --to-gh-secret REPO:NAME (piped to gh secret set), --to-file PATH (mode 600), or a fallback mktemp tempfile
  • --revoke-prior <id> rotates cleanly after the new key is in place
  • Scrubs the bearer + key value from env on exit

Doc

docs/operations/api-key-minting.md covers:

  • Canonical sage rotation example
  • Per-service required scopes (sage / specialist-worker / operator emergency)
  • Why scope-subset matters
  • Recovery when the value was lost

kjgbot and others added 2 commits April 23, 2026 17:27
Adds a wrapper around the admin-bearer + POST /v1/api-keys + secret-store
flow that's been bitten three times today:

1. Picking the wrong scope set silently succeeds at mint, fails at
   runtime — sage's SAGE_RELAYAUTH_API_KEY had scopes
   ["cloud:specialist:invoke"] but actually needed
   ["relayauth:identity:create:*", "relayauth:token:create:*",
    "relayfile:fs:read:*", "relayfile:fs:write:*"] for its
   mintRelayfileToken path. Surfaced as repeated 403 insufficient_scope
   from production once the apiKeyAuth Workers fix landed.
2. Echoing the new key value to terminal/shell history before piping
   into gh secret set leaks the credential.
3. Hand-typing the request body shape per service caller invites
   typos that aren't caught until runtime.

The script:
- generates the admin bearer using the existing generate-dev-token.sh
  helper (HS256, signed with SIGNING_KEY)
- POSTs to /v1/api-keys with the operator's name + scopes
- prints the new api-key id + scopes (safe), never the value
- pipes the value directly into one of {gh secret set | file with mode
  600 | --print-key for explicit piping | mktemp fallback}
- optionally revokes a prior api-key id for clean rotation
- scrubs ADMIN_BEARER + the key value from env on exit

Doc at docs/operations/api-key-minting.md captures:
- canonical sage rotation example (the actual scope set that works)
- per-service required scopes (sage, specialist-worker, operator admin)
- operator emergency admin key recipe (provision before phase-122 step 3
  sunsets HS256 admin bearers)
- explanation of why scope-subset enforcement matters
- recovery procedure when the value was lost mid-mint

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ot identity:create:*

POST /v1/identities requires relayauth:identity:manage:*. The scope
matcher treats 'manage' as implying create/read/write/delete, but
not the reverse — so an api-key with identity:create:* cannot call
that route. The previous example would succeed at mint and fail at
runtime with 403 insufficient_scope.

Caught while rotating sage's key during phase 122 wrap-up, 2026-04-23.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@khaliqgant khaliqgant merged commit 8c17c86 into main Apr 23, 2026
2 checks passed
@khaliqgant khaliqgant deleted the feat/mint-api-key-script branch April 23, 2026 17:39
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.

2 participants