# Decentralized storage keys — provider matrix

Status: **scaffolded**. `/storage.html` runs in stub mode by default. When provider env vars are present, real pins happen and the stub banner is hidden.

Last updated: 2026-04-17.

---

## Env var map

| Env var                 | Provider        | Target network       | Free tier                          | Where to sign up                  |
| ----------------------- | --------------- | -------------------- | ---------------------------------- | --------------------------------- |
| `WEB3_STORAGE_TOKEN`    | web3.storage    | IPFS + Filecoin      | 5 GB free, 100 GB soft-cap / mo    | https://web3.storage              |
| `PINATA_JWT`            | Pinata          | IPFS                 | 1 GB free, 100 pins, public gateway| https://pinata.cloud              |
| `BUNDLR_PRIVATE_KEY`    | Bundlr (Irys)   | Arweave              | Pay-as-you-go in SOL/ETH/MATIC     | https://irys.xyz                  |
| `NFT_STORAGE_TOKEN`     | NFT.Storage     | IPFS + Filecoin      | Free for NFT-sized files           | https://nft.storage               |
| `ARWEAVE_JWK`           | Arweave direct  | Arweave              | No free tier; pay upfront per GB   | https://www.arweave.org           |
| `ESTUARY_TOKEN`         | Estuary (legacy)| IPFS + Filecoin      | Free research tier                 | https://estuary.tech               |
| `LIGHTHOUSE_KEY`        | Lighthouse      | IPFS + Filecoin      | 5 GB free                          | https://lighthouse.storage        |
| `FILEBASE_ACCESS_KEY`   | Filebase        | S3-compat IPFS/Sia   | 5 GB free                          | https://filebase.com              |

---

## Tier routing logic

`/storage.html` offers three tiers in the UI:

- **IPFS (cheap)** → web3.storage / Pinata / Lighthouse, content-addressed, gateway-served.
- **Arweave (permanent)** → Bundlr or direct Arweave, one-time payment, permanent by protocol design.
- **Filecoin (cold)** → web3.storage's Filecoin deals or Filebase, long-term cold storage, gateway-backed retrieval.

In stub mode, CIDs are deterministic hashes of the uploaded content — they *look* real but are not actually pinned anywhere. Clients should treat `pinned: false` or the stub banner as a signal to not rely on retrieval.

---

## Stub-mode banner

`/storage.html` shows an explicit yellow banner when no provider keys are set:

> **Stub mode.** CIDs are deterministic placeholders, not real pins. Set `WEB3_STORAGE_TOKEN`, `PINATA_JWT`, or `BUNDLR_PRIVATE_KEY` to pin to real networks.

The banner is wired to an env-var check at page render time.

---

## Free-tier limits worth knowing

- **web3.storage** — 5 GB free, polite rate limits, no manual review for small apps. Best starting point.
- **Pinata** — 1 GB free, 100 pins, dedicated gateways only on paid tiers.
- **NFT.Storage** — unlimited free for NFT use case, but they review your app if you go over ~50 GB/mo.
- **Bundlr / Irys** — no free tier but very cheap at $0.02–$0.05 per MB for Arweave-permanent.
- **Lighthouse** — 5 GB free, clean API, new kid, growing fast.

---

## Compliance notes

- **Content policy** — every provider will remove content that violates their ToS (CSAM, doxxing, copyright claims). "Decentralized" does not mean "unremovable" at the pinning layer.
- **GDPR "right to be forgotten"** — IPFS is content-addressed and public. Do not pin PII. Use encryption-at-rest before upload if you must store regulated data.
- **Arweave** — truly permanent. Once paid, the network is economically motivated to preserve forever. Pin only data you are sure should be permanent.

---

## Minimum setup to flip to "live" mode

```bash
# .env
WEB3_STORAGE_TOKEN=eyJhbGc...
PINATA_JWT=eyJhbGc...
# Arweave tier stays stub until:
BUNDLR_PRIVATE_KEY=0x...
```

`server.js` should read these at startup and wire real handlers for `/api/storage/pin` instead of the stub. The response contract stays the same — just `pinned: true` flips when it's real.
