Skip to main content

Overview

Pinata provides IPFS pinning services that zkStorage uses to store encrypted files. Files are encrypted client-side before upload, then pinned to IPFS via Pinata’s API.

How It Works

  1. Encrypt: Files encrypted client-side with AES-256-GCM
  2. Upload: Encrypted blob uploaded to Pinata API
  3. Pin: Pinata pins the file to IPFS network
  4. CID: Returns Content Identifier (CID) for retrieval
  5. Download: Fetch via Pinata gateway, decrypt client-side

Features

  1. Fast Uploads - Direct upload to Pinata with retry logic
  2. Gateway Access - Downloads via gateway.pinata.cloud
  3. Metadata Storage - Separate CID for file metadata
  4. Multi-Gateway Fallback - Cloudflare and ipfs.io as backup

Integration with zkStorage

zkStorage uses Pinata for all IPFS operations:
┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Client    │      │   zkTerm    │      │   Pinata    │
│  (Browser)  │      │   Server    │      │    IPFS     │
├─────────────┤      ├─────────────┤      ├─────────────┤
│ 1. Encrypt  │ ───> │ 2. Upload   │ ───> │ 3. Pin      │
│    file     │      │    to API   │      │    to IPFS  │
│             │ <─── │ 4. Return   │ <─── │ 5. Return   │
│             │      │    CID      │      │    CID      │
└─────────────┘      └─────────────┘      └─────────────┘

Terminal Commands

zkStorage commands that use Pinata:
zk storage upload              # Upload → Pinata → IPFS
zk storage download <file-id>  # Pinata Gateway → Decrypt
zk storage list                # List files (metadata from DB)

API Details

Upload to Pinata

zkTerm uploads encrypted files using Pinata’s pinFileToIPFS endpoint:
const response = await fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${PINATA_JWT}`,
  },
  body: formData,
});

const { IpfsHash } = await response.json();
// IpfsHash is the CID, e.g., "QmXyz123..."

Download from Gateway

Downloads use the Pinata gateway with fallbacks:
const gateways = [
  'https://gateway.pinata.cloud/ipfs',
  'https://cloudflare-ipfs.com/ipfs',
  'https://ipfs.io/ipfs',
];

const response = await fetch(`${gateway}/${cid}`);

File Structure

Each zkStorage upload creates two IPFS objects:
ObjectContentCID Example
Encrypted FileAES-256-GCM encrypted dataQmXyz123...
MetadataIV, salt, original name/sizeQmAbc456...
Metadata JSON:
{
  "originalName": "document.pdf",
  "originalSize": 1234567,
  "iv": "base64...",
  "salt": "base64...",
  "iterations": 100000,
  "zkEnabled": true,
  "commitment": "poseidon-hash..."
}

Error Handling

ErrorCauseSolution
PINATA_JWT not configuredMissing API keySet PINATA_JWT env var
Upload failed: 401Invalid JWTRegenerate Pinata API key
Gateway timeoutIPFS propagation delayRetry or use fallback gateway
CID not foundFile unpinnedRe-upload the file

Retry Logic

Uploads include automatic retry with exponential backoff:
for (let attempt = 1; attempt <= 3; attempt++) {
  try {
    return await uploadToPinata(buffer, filename);
  } catch (error) {
    await sleep(1000 * attempt); // 1s, 2s, 3s
  }
}

Timeouts

OperationTimeout
File upload60 seconds
Gateway fetch30 seconds
Metadata fetch30 seconds

Environment Variables

VariableDescription
PINATA_JWTPinata API JWT token (required)

Getting a Pinata API Key

  1. Create account at pinata.cloud
  2. Go to API Keys section
  3. Create new key with pinFileToIPFS permission
  4. Copy JWT token to PINATA_JWT environment variable

Pricing

Pinata offers:
  • Free tier: 1GB storage, 100 pins
  • Starter: $20/month, 25GB storage
  • Professional: Custom pricing
zkStorage uses minimal storage since only encrypted blobs are stored.

Migration Notes

zkStorage previously used NFT.Storage (deprecated June 2024). Migration to Pinata provides:
  1. Better upload reliability
  2. Faster gateway access
  3. Active maintenance and support