This project implements a verifiable encrypted storage system using Storacha's MCP REST API. It provides client-side encryption, decentralized storage, integrity verification, and decryption with comprehensive key management and automatic backup systems.
- AES-256-GCM encryption in the browser with WebCrypto API
- Password-based key derivation using PBKDF2 (100,000 iterations, SHA-256)
- Encrypted key export/import with password protection
- Automatic encrypted key backup generation
- Chunked file upload to Storacha via MCP REST API (4MB chunks)
- File size validation (10MB limit)
- SHA-256 Merkle tree construction for integrity verification
- Merkle proof verification without decryption
- Challenge-response integrity verification system
- Client-side decryption and file reconstruction
- Smart key workflow: checks for existing keys before generating new ones
- Three key sources: generated, imported from file, or derived from password
- Automatic encrypted backup download for generated keys
- Password strength validation for derived keys
- Session-based key storage (lost on refresh unless backed up)
- Local manifest history storage (last 10 files) in browser localStorage
- Manifest recovery and reloading functionality
- Progress tracking with time estimates for upload operations
storacha-ves/
├── server/
│ ├── .env
│ ├── package.json
│ └── index.js
├── web/
│ ├── package.json
│ ├── index.html
│ ├── src/
│ │ ├── main.jsx
│ │ ├── App.jsx
│ │ ├── styles.css
│ │ ├── components/
│ │ │ └── LogPanel.jsx
│ │ └── utils/
│ │ ├── api.js
│ │ ├── bytes.js # hex/ArrayBuffer conversion helpers
│ │ ├── crypto.js # AES and SHA-256 helpers
│ │ └── merkle.js # Merkle tree and proof verification
└── README.md
- Before encryption, the system checks for existing keys in memory.
- If no key exists, the user is prompted to:
- Generate a new AES-256-GCM key (with automatic encrypted backup)
- Import an existing encrypted key file
- Derive a key from password using PBKDF2
- For generated keys, an automatic encrypted backup is created and downloaded.
- For password-derived keys, password strength is validated (minimum 8 characters, mixed case, numbers, symbols).
- The user selects a file (validated against 10MB limit).
- The file is split into 4MB chunks.
- For each chunk:
- Generate a random 12-byte IV.
- Encrypt the chunk with AES-256-GCM using the established key and IV.
- Compute SHA-256 hash of the ciphertext.
- Send the ciphertext to the server with
POST /upload-chunk.
- The server calls MCP REST
tools/callwith methoduploadand receives:payload.files[name]['/']→ File CID (used inchunkCIDs)payload.root['/']→ Root CID (not used in retrieval)
- The client collects all chunk CIDs and SHA-256 hashes to build a Merkle tree and compute the Merkle root.
- A manifest JSON is created and uploaded via
POST /upload-manifest. - The manifest is stored in local browser history (last 10 manifests) for recovery.
- A random chunk index is selected from the manifest.
- The chunk is fetched from IPFS using its CID.
- SHA-256 hash is computed and compared against the manifest's stored hash using a Merkle proof.
- If the proof matches the Merkle root, integrity is verified without decryption.
- All chunk CIDs are fetched from IPFS in sequence.
- Each chunk is decrypted with AES-256-GCM using the IV from the manifest and the AES key.
- The decrypted chunks are concatenated into the original file for download.
- Progress tracking shows completion percentage and estimated time remaining.
- AES keys are generated using WebCrypto API and stored only in browser memory during the session.
- Keys are never uploaded to the server or stored on Storacha.
- Automatic encrypted key backups use PBKDF2 (100,000 iterations, SHA-256) for password protection.
- Exported key files contain: encrypted key data, salt, IV, timestamp, and version information.
- Without the AES key, encrypted chunks cannot be decrypted even if their CIDs and IVs are known.
- Merkle tree verification ensures data integrity without requiring decryption.
- Each chunk uses a unique random IV, preventing replay attacks.
- Password-derived keys use cryptographically secure salt generation.
cd server
npm install
cp .env.example .env # fill in MCP_REST_URL, DELEGATION, CLIENT_ORIGIN
node index.jscd web
npm install
npm run dev{
"version": 3,
"fileName": "example.pdf",
"totalSize": 219581,
"chunkSize": 4194304,
"leaves": [
"c6dbb041110882b6fbe1360a547fa66fec351549114b7aac63fe82416fb6df50"
],
"merkleRootSHA256": "c6dbb041110882b6fbe1360a547fa66fec351549114b7aac63fe82416fb6df50",
"chunkCIDs": [
"bafkreigg3oyeceiiqk3pxyjwbjkh7jtp5q2rksirjn5kyy76qjaw7nw7ka"
],
"ivs": [
"5bef01fd20d3e9ba0eea3378"
],
"algo": {
"hash": "sha256",
"enc": "aes-256-gcm"
}
}