feat/one-api
blockedviewing older commit738b60b · incrementalpre-PRreviewed 2026-07-28 00:46 UTC3H · 6M · 5L · 2I- Purpose
- Implement machine-to-machine auth — JWT-based public API with Bearer token, per-route capability gating, and full API key lifecycle. 'One key per org' product shape: master minted atomically with org, owner can retrieve/rotate/revoke it.
- Goal
- Ship a first-party API credential that an org owner can use immediately — no transcription at creation, always retrievable via dashboard.
- Sub-goals
- SG-1: Master API key minted at org creation, stored in Secrets Manager for owner retrieval
- SG-2: POST /reveal — owner-only, session-only, audited
- SG-3: Remove POST /v1/api-keys from public surface; update UI to one-key model
- SG-4: ARN moves with rotation so revealed secret is always current
- SG-5: Scope machinery live but dormant
- What
- Added secret_arn column (migration 0068), prepareMasterApiKey now stores to Secrets Manager, rotation moves ARN to replacement, new recordApiKeyRevealed audit event, new reveal handler (dashboard-only, owner-gated), apiKeysCreate removed from publicV1Contract, UI replaced Create/Delete dialogs with RevealApiKeyDialog, IAM extended to batu/{env}/api-keys/*.
- Why
- Customers should not transcribe a credential at creation — same trade already accepted for CFE credentials. One-key-per-org mirrors how API-first companies launch.
- Areas
- domains/core+2489−148apps/platform+2141−371packages/api+679−61docs/security+334−0packages/database+169−5.claude/rules+143−8.github+74−2packages/shared-kernel+63−0infra/cdk+4−0
- Blast
- 83 files (+6068/-595); 9 areas; security-critical path: credential storage, rotation, retrieval for every org
Findings · 17
correctness4
Rotation without provider leaves new key holding stale ARN
domains/core/src/api-key/api-key.shells.ts:263
Audit write for reveal not atomic — silent gap if DB write fails
apps/platform/src/api/handlers/api-keys.handler.ts:159
revealApiKeyHandler uses non-RLS database for audit write
apps/platform/src/api/handlers/api-keys.handler.ts:159
createApiKeyHandler still live on dashboard — one-key invariant unenforced
apps/platform/src/app/api/[...ts-rest]/route.ts:578
security2
No test verifying org admin is rejected by reveal
apps/platform/src/api/handlers/api-keys.handler.ts:134
secret_arn stored as plaintext in PostgreSQL
packages/database/drizzle/0068_purple_stature.sql
conventions3
AWS call inside db.transaction() violates FCIS shell contract
domains/core/src/api-key/api-key.shells.ts:265
API_KEY_REVEALED_EVENT_TYPE has no TypeScript interface or Zod schema
domains/core/src/events/api-key.events.ts:127
useRevealApiKey(orgPublicId) parameter is dead — never used
apps/platform/src/lib/hooks/api/useApiKeys.ts:68
tests4
POST /reveal endpoint has zero automated tests
apps/platform/src/api/handlers/api-keys.handler.ts
secretArn ARN-move assertion is vacuous truth in CI
apps/platform/src/__tests__/integration/public-v1-api-keys.test.ts:258
discardMasterApiKeySecret compensating cleanup has no test
domains/core/src/api-key/api-key.master.ts:89
Childless-child revoke empty-cascade test removed with no replacement
apps/platform/src/__tests__/integration/public-v1-api-keys.test.ts
improvement4
useCreateApiKey and useDeleteApiKey are orphaned dead exports
apps/platform/src/lib/hooks/api/useApiKeys.ts
discardMasterApiKeySecret swallows failures silently
domains/core/src/api-key/api-key.master.ts:93
Reveal button missing loading spinner
apps/platform/src/app/[locale]/(dashboard)/credentials/api/_components/RevealApiKeyDialog.tsx:70
readMasterKeySecret constructs new client per call + split imports
apps/platform/src/lib/secrets/api-key-secret.ts:40
History · 47 commits
- 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48
- 90aa3d5needs attentionincremental1H · 5M · 3L2026-08-11 19:37
- 29d19a0needs attentionincremental1H · 5M · 9L2026-08-11 17:41
- 9bd8a0cneeds attentionfull0H · 5M · 9L2026-08-11 02:14
- 62ec3f7needs attentionincremental2H · 5M · 6L2026-08-10 22:51
- f93bca9needs attentionincremental2H · 5M · 8L2026-08-10 17:51
- 052db6fneeds attentionincremental1H · 3M · 4L2026-08-09 21:13
- 45699caneeds attentionincremental0H · 7M · 11L2026-08-09 17:44
- b843d8aneeds attentionincremental1H · 7M · 9L2026-08-09 04:05
- e1757b8needs attentionincremental0H · 3M · 6L2026-08-05 02:11
- 7a762faneeds attentionincremental2H · 5M · 5L2026-08-05 01:25
- 3300a60needs attentionincremental2H · 4M · 7L2026-08-04 19:06
- 0c8a7f5needs attentionincremental0H · 4M · 9L2026-08-04 18:15
- 345f42eneeds attentionincremental2H · 6M · 9L2026-08-04 17:28
- 8338a9aneeds attentionincremental5H · 14M · 14L2026-08-04 00:33
- 41be4c3needs attentionincremental0H · 5M · 7L2026-08-03 23:49
- 5ed593dneeds attentionincremental1H · 6M · 6L2026-08-03 21:32
- b333e25needs attentionincremental4H · 9M · 8L2026-08-03 21:00
- 5642cccneeds attentionincremental2H · 3M · 2L2026-08-03 20:17
- 73b0b39needs attentionincremental3H · 10M · 13L2026-07-31 18:29
- b19852eneeds attentionincremental0H · 1M · 5L2026-07-29 05:04
- 3845205needs attentionincremental3H · 6M · 4L2026-07-29 04:47
- eb8eb50needs attentionincremental0H · 1M · 2L2026-07-29 03:03
- f4720a3needs attentionincremental6H · 8M · 7L2026-07-29 02:54
- f8d341ablockedincremental2H · 2M · 5L2026-07-29 00:00
- a7f1a64needs attentionincremental2H · 8M · 8L2026-07-28 18:41
- 738b60bblockedincremental3H · 6M · 5L2026-07-28 00:46current
- 2c248b6needs attentionincremental8H · 12M · 8L2026-07-27 23:23
- 1346cc0needs attentionincremental2H · 8M · 6L2026-07-27 20:15
- 0716018needs attentionincremental2H · 11M · 12L2026-07-27 19:22
- 215cd2dneeds attentionincremental3H · 6M · 5L2026-07-27 17:04
- ec46958needs attentionincremental0H · 3M · 5L2026-07-27 16:51
- de7b337blockedincremental4H · 9M · 14L2026-07-27 06:36
- b1bb9c0needs attentionincremental1H · 2M · 4L2026-07-27 05:09
- 4701d11needs attentionincremental0H · 4M · 3L2026-07-27 04:44
- e1626c4needs attentionincremental3H · 9M · 10L2026-07-27 03:21
- 195f198needs attentionincremental3H · 3M · 3L2026-07-25 01:22
- 42c7358safeincremental0H · 0M · 0L2026-07-22 20:46
- 85b9018needs attentionincremental0H · 1M · 6L2026-07-21 23:51
- a7b2a9aneeds attentionincremental0H · 9M · 12L2026-07-21 18:49
- c2ee0daneeds attentionincremental4H · 7M · 7L2026-07-21 02:17
- e8ffa5eneeds attentionincremental4H · 7M · 5L2026-07-21 01:33
- a2d2a54needs attentionincremental2H · 7M · 3L2026-07-21 00:51
- 576fbd6needs attentionfull1H · 6M · 7L2026-07-21 00:35
- d3465e8needs attentionincremental1H · 7M · 10L2026-07-21 00:23
- dc794a7needs attentionincremental0H · 5M · 5L2026-07-20 23:46
- 9082773needs attentionfull1H · 3M · 3L2026-07-20 23:13