no wallet
–

Drop files here, or click to select

Files are chunked at 64 KB Β· encrypted with AES-GCM (HKDF from your wallet) Β· erasure-coded Β· committed as DATA tx on the ZooCoin chain.

RETRIEVE BY HASH β€” paste any file hash from the chain to download (decryption requires the original wallet key).

CRYPTOZOOΒ·FS β€” Distributed Filesystem on ZooCoin

A content-addressed, blockchain-backed filesystem. Every byte you store lives inside the ZooCoin chain as DATA transactions. The file's SHA-256 plaintext hash IS its address β€” share the hash, anyone with your wallet (or the public version) can reconstruct the file from chunks.

Pipeline

  1. CHUNK β€” split file into 64 KB pieces.
  2. ERASURE-CODE β€” append XOR parity stripes so any 60% of chunks suffice.
  3. DERIVE-KEY β€” HKDF-SHA256 over your ECDSA private key β†’ AES-GCM 256-bit key (per file salt).
  4. ENCRYPT β€” AES-GCM each chunk independently with a random 12-byte IV.
  5. COMMIT β€” wrap each chunk in a type:"DATA" transaction; append directly to local chain (FS_TX, no PoW for demo).
  6. RETRIEVE β€” scan chain for matching file_hash, sort by index, decrypt, reassemble, verify SHA-256 round-trip.

Cryptography

All operations use the browser's Web Crypto API (subtle.encrypt, subtle.deriveBits with HKDF, subtle.digest SHA-256). No polyfills. ECDSA private keys never leave localStorage; HKDF binds the AES key to a per-file random salt so leaking one file's key does not compromise others.

Sharing

Public files have no encryption β€” anyone with the chain can rebuild them from the hash. Private files require the original wallet's private key. The "Share" action emits a #file=<hash> URL; opening that URL on a synced node (same chain) auto-resolves and downloads.

Storage Schema
// DATA transaction (appended to chain[].transactions)
{
  type: "DATA",
  fs: "FS_TX",          // no PoW
  file_hash: "<sha256-of-plaintext-hex>",
  chunk_id: "<sha256-of-cipher-hex>",
  chunk_index: 0..N-1,
  total_chunks: N,
  parity_count: K,      // last K are XOR parity
  encrypted: true|false,
  iv: "<12-byte hex>",   // null if public
  salt: "<16-byte hex>", // HKDF salt, null if public
  payload: "<base64 ciphertext or plaintext chunk>",
  meta: { name, size, mime, created, owner: "<address>" }, // only on chunk_index===0
  timestamp: ms,
  txid: "<sha256-of-tx-fields>"
}