> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blobrouter.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture decisions

> Why BlobRouter is built this way — Workers, direct uploads, STS, queues, and more.

<Info>
  **BlobRouter v0.1** · Last updated: August 2026 · Architecture version: **2.0**
</Info>

These are the decisions we get asked about most. They are locked for v1 unless noted otherwise in [ARCHITECTURE.md](https://github.com/blobrouter).

## 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

Kubernetes would optimize for long-lived services we do not need yet. Revisit if we outgrow Worker CPU/memory for migrations.

## 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

Proxying would make us a data processor and a single point of failure for every upload.

## 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

STS does not work the same way across R2/B2; encrypted stored keys are the practical Model B.

## 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](/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 a `scanId` 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](/concepts/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

| Choice                     | Why                                                     |
| -------------------------- | ------------------------------------------------------- |
| Server-only SDK v1         | Avoid browser CORS + key leakage                        |
| 5GB single PUT             | Covers common AI artifacts without multipart complexity |
| No auto-migration          | Explicit approval only                                  |
| HeadObject on complete     | Protects savings accuracy                               |
| One bucket per provider v1 | Simple schema                                           |

More detail lives in the monorepo `ARCHITECTURE.md` decision log.
