Storage docs

MeterCall's storage layer is decentralized by default. IPFS for hot content, Arweave for permanence, Filecoin for cold long-tail. One API, three tiers, five public gateways with automatic fallback.

Overview

Every module page, every bridge attestation, every receipt can be pinned to decentralized storage. In dev mode the server returns deterministic fake CIDs so you can wire up your UI without real keys. When you set WEB3_STORAGE_TOKEN, PINATA_JWT, or BUNDLR_KEY, the same endpoints start issuing real CIDs.

Retrieval is always live: the /v1/storage/resolve/:cid endpoint races 5 public gateways in parallel and returns the fastest responder plus the full fallback list.

Tiers

TierPricePersistenceBest for
ipfs~$0.0001/MB/mo180 days typicalModules, HTML, hot pages
arweave~$2/GB one-timeForever (200y horizon)Bridge attestations, receipts
filecoin~$0.003/GB/moYears (provable deals)Long-tail catalog, analytics

Endpoints

Method & pathPurpose
POST /v1/storage/pinPin arbitrary content
POST /v1/storage/pin-urlFetch URL, then pin
GET  /v1/storage/resolve/:cidGateway race & fallback
GET  /v1/storage/gateway/statusLive gateway health, 60s cache
POST /v1/storage/pin-module/:slugPin a module's built HTML
POST /v1/storage/arweave/attestQueue attestation for Arweave
GET  /v1/storage/statsAggregate metrics
POST /v1/storage/mirror-catalogPin all modules (batched)
GET  /v1/storage/mirror/:jobIdMirror job progress

Pin content

# POST /v1/storage/pin
curl -sX POST https://metercall.ai/v1/storage/pin \
  -H "Content-Type: application/json" \
  -d '{"content":"hello, decentralized world","tier":"ipfs"}'

# → { cid, gateway_urls, pin_id, size_bytes, estimated_persistence_days, cost }

Pin a URL

curl -sX POST https://metercall.ai/v1/storage/pin-url \
  -H "Content-Type: application/json" \
  -d '{"url":"https://metercall.ai/l4.html","tier":"ipfs"}'

Resolve a CID

curl -s https://metercall.ai/v1/storage/resolve/bafkreiabc123…
# → races 5 public gateways, returns fastest + full fallback list

Mirror the whole catalog

curl -sX POST https://metercall.ai/v1/storage/mirror-catalog
# → { job_id, total, poll: "/v1/storage/mirror/{id}" }

curl -s https://metercall.ai/v1/storage/mirror/mir_…
# → { done, total, failed, progress_pct, status }

Archive an attestation on Arweave

curl -sX POST https://metercall.ai/v1/storage/arweave/attest \
  -H "Content-Type: application/json" \
  -d '{"attestation_payload":{"bridge":"usdc-base-to-sol","tx":"0xabc","amount":"100"}}'
# → { queued: true, arweave_tx_id, viewer_url: "https://arweave.net/{id}" }

CID format

Real CIDs are multibase-encoded multihashes. MeterCall uses CIDv1 with base32 lowercase (prefix bafkrei…) so links work with every major public gateway. In stub mode we synthesize a deterministic lookalike by base32-encoding a SHA-256 of the input. Same content → same CID, every time.

Gateway fallback

Every pin response includes gateway_urls — five public gateways in preferred order. Always try the first; if you get a 404 or >3s timeout, rotate to the next. /v1/storage/resolve/:cid does this race for you.

BYO keys

To flip from stub to live, set one or more of these env vars before boot:

Env varSourcePurpose
WEB3_STORAGE_TOKENweb3.storage (free 5GB)IPFS + Filecoin cold backup
PINATA_JWTPinata (free 1GB)IPFS pinning
BUNDLR_KEYBundlr / TurboArweave uploads

No keys required for reads — public gateways are always free.

Pinning patterns

Pattern 1 — module-first

When a module is built, immediately POST /v1/storage/pin-module/:slug. The returned CID is recorded in global._moduleCids so subsequent page loads can prefer the IPFS gateway.

Pattern 2 — attestation-first

When the bridge emits a new attestation, pipe it through /v1/storage/arweave/attest. Permanence is guaranteed by the one-time fee.

Pattern 3 — cold tail

Nightly job: anything older than 90 days and rarely accessed → re-pin with tier:"filecoin" and drop the IPFS pin.

Arweave permanence guarantees

Arweave's storage endowment model charges a one-time fee sized to cover storage for 200+ years at declining hardware costs. Each attestation we archive gets a 43-character base64url tx id viewable at https://arweave.net/{id}.

Filecoin cold paths

We cold-store via web3.storage's Filecoin backups; retrieval uses the w3s.link gateway. For programmatic deals, set WEB3_STORAGE_TOKEN.

Billing

Each storage call is metered the same way as any other MeterCall API call — a small per-call toll. Actual pin costs passthrough from the underlying provider. See the cost calculator.

Migration from centralized storage

  1. Deploy with all three env vars unset — stubs everywhere, nothing breaks.
  2. Get a free Pinata JWT; set PINATA_JWT. Stubs become real CIDs for new pins; old CIDs still resolve from the deterministic stub map.
  3. Run POST /v1/storage/mirror-catalog once. All 2,866 modules pinned.
  4. Add BUNDLR_KEY when you're ready to archive bridge attestations.

No client code changes required at any step — every endpoint returns the same shape.