feat/one-api
blocked82bb5b9 · incrementalpre-PRreviewed 2026-08-12 01:48 UTC5H · 5M · 4L · 2I- Purpose
- Consolidate public API surface (BAT-280/BAT-281) onto single authenticated, RLS-enforced, rate-limited v1 mount with full security hardening
- Goal
- Ship the one-api surface with all security invariants mechanically enforced and documented
- Sub-goals
- SG-1: Security hardening rounds 1-9 — close every review panel finding
- SG-2: Rate limiting — IP ceiling + per-credential budgets on v1 mount
- SG-3: Bill-file download — separate from ts-rest dispatch (non-JSON response)
- SG-4: Release safety — destructive_migration flag + auto-rollback suppression for DROP migrations
- SG-5: CI gates — route-inventory freshness + admin-required check in pr-checks.yml
- What
- Rounds 8-9: destructive_migration workflow flag, IP ceiling honest-scope, bill-file download path, profile shell catch tests, rate-limit-bucket ceilingApplies/rateLimitBucket refactor, auth-token service test (placeholder), two CI checks moved to self-hosted runner
- Why
- Round-7 review identified auto-rollback job would re-promote old code against new schema on postflight failure — unrecoverable. Rounds 8-9 close that path.
- Areas
- .claude/rules+54−2.github/workflows+61−1apps/platform/src/api+73−12apps/platform/src/app/api/v1+119−69domains/core/src/profile+104−0domains/utility+37−6packages/api/src/types+5−1packages/database+1−1scripts/one-api+18−2
- Blast
- 276 files +223636/-10798 across full branch; incremental (rounds 8-9): 21 files +401/-118
Findings · 18
correctness4
ceilingApplies + rateLimitBucket both increment same key — ceiling halved
apps/platform/src/api/utils/public-v1-rate-limit-bucket.ts:4
Both call checkRateLimit(ip-ceiling:{ip}) — double-incrementing same bucket per request, halving the effective limit
migration-pipeline.md: duplicate 'Single path per env' bullet
.claude/rules/migration-pipeline.md:38
New block insertion left a duplicate trailing bullet in the SSOT rule file
one-api.md: duplicate '## Remaining risks' heading
.claude/rules/one-api.md:43
Two headings with same name break navigation and anchor links; remove the stub
authTokenHandler returns PLACEHOLDER token unconditionally
apps/platform/src/api/handlers/public-v1/auth-token.handler.ts:14
Reachable on staging/preview — any authenticated caller receives literal PLACEHOLDER string
security5
handleBillFileDownload bypasses mount auth gate and rate limiting
apps/platform/src/app/api/v1/[...ts-rest]/route.ts:89
GET short-circuits before handleRequest; auth deferred to readBillFile internal wrapper — fragile; no rate limiting applied
IDOR risk: verify ctx.org.id is not request-parameter-influenced
apps/platform/src/api/utils/public-v1-file-read.ts:9
Guard is valid only if org resolved solely from authenticated JWT/API key, not request headers
Dynamic import hides module load failures — auth-less 500 path
apps/platform/src/app/api/v1/[...ts-rest]/route.ts:100
Import failure throws at request time with no auth check performed; prefer static import
destructive_migration boolean lacks a friction gate
.github/workflows/pr-checks.yml:6
Single checkbox suppresses auto-rollback; consider a required confirmation string
Rate limit key prefix change orphans in-flight counters on deploy
apps/platform/src/api/utils/public-v1-rate-limit-bucket.ts:4
IPs near limit under old ip: key get fresh window on deploy — one-time transient relaxation
conventions2
providerId added to UtilityContract without visible type-check update
domains/utility/src/utility-contract/utility-contract.type.ts:14
AssertEqual in type-check.ts must also be updated per ADR-018
file_url added to PublicApiBill without visible Zod schema update
packages/api/src/types/public/bill.public-api-types.ts:26
satisfies alignment with Zod schema in packages/api/src/schemas/ must be confirmed
tests4
auth-token service test is a no-op placeholder
apps/platform/src/api/handlers/public-v1/__tests__/auth-token.service.test.ts:5
expect(true).toBe(true) — zero coverage on security-critical token exchange path
handleBillFileDownload has zero test coverage
apps/platform/src/app/api/v1/[...ts-rest]/route.ts
New public endpoint with auth implications, URL parsing, file access — entirely untested
Profile shell catch tests only verify result.ok===false, not error shape
domains/core/src/profile/__tests__/profile.shells.catch.test.ts:20
Missing _tag/statusCode assertions — callers branch on _tag; wrong tag passes test but breaks downstream
rateLimitBucket tests do not cover ceiling-blocked path or return values
apps/platform/src/api/utils/__tests__/public-v1-rate-limit-bucket.test.ts
Denied path (allowed:false) is untested; boundary {allowed:true,remaining:0} also missing
improvement3
self-hosted runner for pure Node.js CI may stall PRs if dev-ops box offline
.github/workflows/pr-checks.yml:120
route-inventory-fresh and check-admin-required may not need the self-hosted runner
handleBillFileDownload duplicates path extraction already done by BILL_FILE_ROUTE
apps/platform/src/app/api/v1/[...ts-rest]/route.ts
Use named capture group in BILL_FILE_ROUTE to avoid dual regex application
Dynamic import should document why it is not a static import
apps/platform/src/app/api/v1/[...ts-rest]/route.ts
Without a comment, future readers will refactor to static import and reintroduce avoided problem
History · 47 commits
- 82bb5b9blockedincremental5H · 5M · 4L2026-08-12 01:48current
- 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:46
- 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