Skip to content

Make Audience ingest keys revealable instead of show-once - #54

Merged
ralyodio merged 1 commit into
masterfrom
feat/audience-key-reveal
Jun 12, 2026
Merged

ralyodio merged 1 commit into
masterfrom
feat/audience-key-reveal

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Why

The Audience server API keys were show-once ("Copy now — it won't be shown again"), which is hostile UX: lose the value and your only option is revoke + re-mint + redeploy.

What

  • New migration 20260612160000_project_api_keys_ciphertext.sql: adds key_ciphertext to project_api_keys.
  • Mint (lib/audience/projectKeys.ts): alongside the existing peppered-SHA-256 verification hash, the plaintext is now also stored AES-256-GCM-encrypted under the existing vault key (lib/sp/vault.ts / SOCIAL_VAULT_KEY) — same scheme already used for OAuth tokens. Verification on /api/events is unchanged (still hash lookup).
  • New server action revealProjectApiKey: decrypts on demand. Gated on requireProjectAccess, viewers excluded, revoked keys refused.
  • UI: each active key gets Reveal/Hide + Copy; the mint banner loses the scare copy and gains a Copy button. Keys minted before this change have no ciphertext, so they get no Reveal button, and revealing one returns a clear "generate a new one" error.
  • The ciphertext never reaches the browser — the page maps it to a can_reveal boolean server-side.

Security notes

  • DB leak alone still exposes nothing: reveal requires SOCIAL_VAULT_KEY (Railway env), hash requires SP_TOKEN_PEPPER.
  • Trade-off vs before: a compromise of both the DB and the vault key now recovers key plaintexts. That's the same trust model already accepted for social OAuth tokens.

Test plan

  • vitest run — 345 passed (new test: minted ciphertext round-trips through decryptSecret, and doesn't contain the plaintext)
  • tsc --noEmit clean, oxlint clean
  • Apply migration to live DB

🤖 Generated with Claude Code

Store the cpk_ plaintext AES-256-GCM-encrypted (lib/sp/vault.ts,
SOCIAL_VAULT_KEY) alongside the verification hash, add a
revealProjectApiKey server action (project access required, viewers
excluded, revoked keys refused), and give each key a Reveal/Hide +
Copy control in the Audience UI. Keys minted before the migration
have no ciphertext and simply don't get a Reveal button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit 2eecf36 into master Jun 12, 2026
8 checks passed
@ralyodio
ralyodio deleted the feat/audience-key-reveal branch June 12, 2026 19:05
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.

1 participant