BlobRouter v0.1 · Last updated: August 2026 · Architecture version: 2.0
Decision 1 — Why Cloudflare Workers instead of Kubernetes?
The control plane mints URLs and runs short jobs. It does not stream gigabytes. Workers give:- Global edge latency for
upload/init - No cluster ops tax for a small team
- Native Queues, KV, and rate-limit bindings
Decision 2 — Why direct uploads instead of proxying?
Invariant: BlobRouter never sees object bytes. Presigned PUT/GET means:- No bandwidth bill or bottleneck on our side
- Smaller security blast radius
- Customers keep data residency with their providers
Decision 3 — Why STS for the scanner?
Audits need temporary, revocable access. STS AssumeRole + External ID is the industry pattern for cross-account read access:- No long-lived AWS keys stored for scanning
- Customer can revoke by deleting the role
- Confused-deputy protection via External ID
Decision 4 — Why encrypted keys for the router?
Routing needs to mint presigned URLs with the customer’s provider credentials. Those keys are:- Encrypted with AES-256-GCM before Supabase insert
- Decrypted only in Worker memory at URL mint time
- Never logged or returned to clients
Decision 5 — Why storage first?
Storage access patterns are objective, trust required is low (read-only audit), and waste is easy to show in dollars. Compute and “AI Cloud Engineer” stay on the roadmap — we do not pretend they ship today.Decision 6 — Why queues instead of synchronous scans?
Full-account S3 scans take 2–5 minutes. Holding an HTTP request open that long is fragile (timeouts, retries, duplicate work). Cloudflare Queues let us return ascanId immediately and update results asynchronously.
Decision 7 — Why Hono?
Small, typed, works on Workers, familiar Express-like routing. We do not need a heavier framework for a focused API surface.Decision 8 — Why Supabase?
Postgres + Auth + Row policies without running our own auth service. Dashboard uses Supabase Auth JWTs; the API uses the service role for control-plane writes. Good enough pre-SOC2; Vault migration is a later option.Decision 9 — Why pricing assumptions?
True future access is unknown at upload time. We use explicit, published defaults per priority (reads/month, months stored), run a deterministic cost formula, and return those assumptions on the decision. Honesty beats a black-box ML claim. See Routing logic.Decision 10 — Why developer-first?
The people who can connect IAM and install an SDK are the same people who feel storage pain. Docs, API keys, and SDKs come before enterprise SSO theater. Enterprise packaging can layer on later; trust and clarity cannot.Related locked choices
More detail lives in the monorepo
ARCHITECTURE.md decision log.