← all branches

feat/one-api

needs attentionviewing older commit
215cd2d · incrementalpre-PRreviewed 2026-07-27 17:04 UTC3H · 6M · 5L
The branch
Purpose
Ship machine and service API credentials for the Batu public API, establishing the auth foundation required for SOC 2 readiness and external integrations.
Goal
Complete one-table API key system (customer bk_ + Batu sk_ credentials), JWT token exchange with ES256, scope enforcement gate, and the first compliance artifact documenting the implemented controls.
Sub-goals
  • SG-1: ApiKey entity — one table, kind discriminator, scrypt hashing
  • SG-2: Token exchange — POST /v1/auth/token, ES256 JWT, timing equalization
  • SG-3: Service key variant — platform-scoped, no org, same table
  • SG-4: Meta gate — one enforcement point for credential class + scope on every route
  • SG-5: Compliance narrative — first SOC 2 / ISO 27001 artifact
The changes (whole branch)
What
This commit adds docs/security/access-control-narrative.md (294 lines) — the first compliance document for the branch. Prior commits implemented the full API key + service key stack.
Why
SOC 2 CC6 and ISO 27001 A.5/A.8 require documented access control controls. The narrative captures the as-implemented state including known gaps (§9 L1-L7), establishing an auditable baseline.
Areas
domains/core/src/api-key/+72733apps/platform/src/api/+38328packages/api/src/+40737docs/security/+2940.claude/rules/+816packages/database/+535.github/workflows/+302
Blast
~35 files across 7 areas, +1975/-111 lines (excluding the 13k-line Drizzle snapshot)
typecheck· no CI — pre-PR branchtests· no CI — pre-PR branchcoderabbit· no .coderabbit.yaml

Findings · 14

correctness3

high

Display prefix described as 8 chars; stored prefix is 11 (bk_+8)

docs/security/access-control-narrative.md

§4.2 'Non-disclosure in logs' says 'non-secret 8-character display prefix are bound as database parameters.' PREFIX_DISPLAY_LENGTH=11 in api-key.lib.ts — prefix is `bk_` (3) + 8 random chars = 11 total.

medium

CHECK constraint description captures only half the invariant

docs/security/access-control-narrative.md

§3 writes kind='service' ⇒ org_id IS NULL. Actual constraint is biconditional; omits that non-service keys MUST have an org.

low

scrypt params cited as explicit config; they are Node.js defaults

docs/security/access-control-narrative.md

security3

high

§4.3 JWKS claim misrepresents the verification trust path

docs/security/access-control-narrative.md

The JWKS endpoint exists for external consumers. Internal verification (verifyBatuJwt) reads BATU_JWT_PUBLIC_KEY_PEM from an env var. Rotation boundary is a redeploy, not a JWKS TTL.

low

§3 'by construction' framing not scoped to public surface

docs/security/access-control-narrative.md

low

Combined rotation + revocation exposure window (25h) not stated

docs/security/access-control-narrative.md

conventions1

medium

§6 event-type list uses leading-dot shorthand

docs/security/access-control-narrative.md

`.rotated`, `.revoked`, `.deleted` look like sub-paths; should be full standalone identifiers.

tests1

high

§5.3 & §8 overstate IDOR test: not in CI, probe is one-directional

docs/security/access-control-narrative.md

Test requires RUN_INTEGRATION=true + DB env; no CI workflow sets this. Also probes only from org-A, not symmetrically from each org.

improvement6

medium

Stale argon2id comments in schema contradict the doc's scrypt claim

docs/security/access-control-narrative.md

packages/database/src/schema/api-keys.ts lines 9 and 94 say 'argon2id hash' — the implementation and this doc correctly say scrypt. Must be fixed in same PR.

medium

§6 Request-stage RLS gate doesn't apply to machine credentials

docs/security/access-control-narrative.md

Machine credentials use service-role DB (no RLS). Exclusion from key-creation is application-layer authz, not RLS.

medium

§8 'Authentication outcomes' evidence claim underspecified

docs/security/access-control-narrative.md

Failed-auth paths return silent 401 with no visible structured log entry in the handler code. Auditor will request log schema.

low

L2/L3 retirement has no owner, date, or tracking reference

docs/security/access-control-narrative.md

low

§2 system boundary omits EventBridge/SQS outbox delivery path

docs/security/access-control-narrative.md

low

§3 automated test claim lacks file reference

docs/security/access-control-narrative.md

History · 47 commits

  1. 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48
  2. 90aa3d5needs attentionincremental1H · 5M · 3L2026-08-11 19:37
  3. 29d19a0needs attentionincremental1H · 5M · 9L2026-08-11 17:41
  4. 9bd8a0cneeds attentionfull0H · 5M · 9L2026-08-11 02:14
  5. 62ec3f7needs attentionincremental2H · 5M · 6L2026-08-10 22:51
  6. f93bca9needs attentionincremental2H · 5M · 8L2026-08-10 17:51
  7. 052db6fneeds attentionincremental1H · 3M · 4L2026-08-09 21:13
  8. 45699caneeds attentionincremental0H · 7M · 11L2026-08-09 17:44
  9. b843d8aneeds attentionincremental1H · 7M · 9L2026-08-09 04:05
  10. e1757b8needs attentionincremental0H · 3M · 6L2026-08-05 02:11
  11. 7a762faneeds attentionincremental2H · 5M · 5L2026-08-05 01:25
  12. 3300a60needs attentionincremental2H · 4M · 7L2026-08-04 19:06
  13. 0c8a7f5needs attentionincremental0H · 4M · 9L2026-08-04 18:15
  14. 345f42eneeds attentionincremental2H · 6M · 9L2026-08-04 17:28
  15. 8338a9aneeds attentionincremental5H · 14M · 14L2026-08-04 00:33
  16. 41be4c3needs attentionincremental0H · 5M · 7L2026-08-03 23:49
  17. 5ed593dneeds attentionincremental1H · 6M · 6L2026-08-03 21:32
  18. b333e25needs attentionincremental4H · 9M · 8L2026-08-03 21:00
  19. 5642cccneeds attentionincremental2H · 3M · 2L2026-08-03 20:17
  20. 73b0b39needs attentionincremental3H · 10M · 13L2026-07-31 18:29
  21. b19852eneeds attentionincremental0H · 1M · 5L2026-07-29 05:04
  22. 3845205needs attentionincremental3H · 6M · 4L2026-07-29 04:47
  23. eb8eb50needs attentionincremental0H · 1M · 2L2026-07-29 03:03
  24. f4720a3needs attentionincremental6H · 8M · 7L2026-07-29 02:54
  25. f8d341ablockedincremental2H · 2M · 5L2026-07-29 00:00
  26. a7f1a64needs attentionincremental2H · 8M · 8L2026-07-28 18:41
  27. 738b60bblockedincremental3H · 6M · 5L2026-07-28 00:46
  28. 2c248b6needs attentionincremental8H · 12M · 8L2026-07-27 23:23
  29. 1346cc0needs attentionincremental2H · 8M · 6L2026-07-27 20:15
  30. 0716018needs attentionincremental2H · 11M · 12L2026-07-27 19:22
  31. 215cd2dneeds attentionincremental3H · 6M · 5L2026-07-27 17:04current
  32. ec46958needs attentionincremental0H · 3M · 5L2026-07-27 16:51
  33. de7b337blockedincremental4H · 9M · 14L2026-07-27 06:36
  34. b1bb9c0needs attentionincremental1H · 2M · 4L2026-07-27 05:09
  35. 4701d11needs attentionincremental0H · 4M · 3L2026-07-27 04:44
  36. e1626c4needs attentionincremental3H · 9M · 10L2026-07-27 03:21
  37. 195f198needs attentionincremental3H · 3M · 3L2026-07-25 01:22
  38. 42c7358safeincremental0H · 0M · 0L2026-07-22 20:46
  39. 85b9018needs attentionincremental0H · 1M · 6L2026-07-21 23:51
  40. a7b2a9aneeds attentionincremental0H · 9M · 12L2026-07-21 18:49
  41. c2ee0daneeds attentionincremental4H · 7M · 7L2026-07-21 02:17
  42. e8ffa5eneeds attentionincremental4H · 7M · 5L2026-07-21 01:33
  43. a2d2a54needs attentionincremental2H · 7M · 3L2026-07-21 00:51
  44. 576fbd6needs attentionfull1H · 6M · 7L2026-07-21 00:35
  45. d3465e8needs attentionincremental1H · 7M · 10L2026-07-21 00:23
  46. dc794a7needs attentionincremental0H · 5M · 5L2026-07-20 23:46
  47. 9082773needs attentionfull1H · 3M · 3L2026-07-20 23:13