Every file stored with Safecloud is end-to-end encrypted before it leaves your device, chunked into a Merkle tree, and distributed across a network of browser-based storage nodes — with on-chain payment settlement.
Safecloud has no central server. Three kinds of participants compose the network. The Cloud is any browser visiting a page. Jets are lightweight routing servers. Drops are browser tabs that volunteer storage.
The end user's browser. Encrypts files locally, builds the Merkle tree, and manages access grants. Never sends plaintext to the network.
A Node.js server. Routes encrypted chunks between Clouds and Drops, verifies grants, issues proof-of-storage spot-checks, and relays payment claims to the blockchain.
Any browser tab that opts in to store chunks in IndexedDB. Earns Safebux tokens for each chunk served. Derives its EVM identity from a WebAuthn PRF — no seed phrase.
Safecloud is a three-tier system where each actor has a distinct role, a distinct key identity, and interacts with the others through signed messages. No actor needs to trust any other — every claim is cryptographically verified before being acted on.
Each actor derives a unique cryptographic identity without storing raw private key material.
The Cloud user's existing wallet (MetaMask, WalletConnect, etc.) acts as the root. A single interactive EIP-712 signature derives two non-extractable session keys: a P-256 key for off-chain signing and a secp256k1 key for on-chain payment authorization. Both live in IndexedDB as Web Crypto CryptoKey objects with extractable: false. The ceremony runs once per 30-day session.
The Jet server holds a secp256k1 session key derived at startup. This key is used to sign payment tokens (EIP-712 Payment structs) that authorize Drops to be paid for serving chunks. The Jet's EVM address is the payer on these tokens — it pre-funds a Safebux allowance to the OpenClaiming contract, and Drops claim against that allowance.
The Drop derives its identity from a WebAuthn PRF output — a secret bound to the device's Secure Enclave (Apple) or platform authenticator (Android, Windows Hello). The PRF output is deterministically delegated into two keypairs: a stable EVM address (secp256k1) used for on-chain claiming, and a P-256 session key used for signing announce payloads. No seed phrase is ever stored or displayed.
The settlement layer. Deployed at the same vanity address on every supported chain. Immutable — no proxy, no upgrade path. Accepts EIP-712 signed Payment structs, verifies the signature, enforces spending lines, and transfers Safebux tokens. Anyone can inspect the source on any chain's block explorer.
0x99999febd42cad798fe10ab0b1c563002fc99999 — verified on BSC and Ethereum
From the moment you pick a file to the moment it's replicated across Drops, every step preserves end-to-end encryption. The Jet never sees plaintext. The Drop never sees the decryption key.
The Cloud derives an encryption root and access root from a random file key. Each subtree node in the Merkle tree gets its own AES key, derived deterministically by path — so any branch can be granted independently without revealing sibling keys.
The file is split into fixed-size chunks. Each chunk is encrypted with AES-256-GCM using its leaf subtree key and a chunk-specific IV. The CID of each chunk is SHA-256(ciphertext ∥ tag) — a content address over the encrypted data, not the plaintext.
CIDs are arranged into an N-ary Merkle tree. The root CID commits to the entire encrypted file. An optional index chunk (for HLS video manifests) sits alongside under a separate subtree.
The Cloud sends encrypted chunks, OCP Role A grants (authorizing the Jet to accept this upload), and signed EIP-712 payment tokens (authorizing Drops to be paid in Safebux) to the Jet over an encrypted socket connection.
The Jet verifies the Role A grant, then forwards chunks to connected Drops. The Jet passes each Drop a signed payment token — its authorization to be paid for storing these chunks. Drops store chunks in IndexedDB and confirm.
After storing chunks, the Drop updates its Prolly tree root and sends a P-256-signed announce to the Jet. The Jet verifies the signature so it knows the Drop's declared inventory is authentic.
Retrieval is the mirror of upload. The Cloud reconstructs the Merkle tree from chunk responses, verifies each CID, and decrypts locally. The Jet and Drop only ever see ciphertext.
The Cloud fetches the manifest (stored as a separate encrypted index chunk). The manifest contains the root CID, tree shape (N, depth), and chunk count.
Each Drop maintains a Bloom filter of its stored CIDs. The Jet uses these filters to route chunk requests to Drops that are likely to have the chunks — without asking Drops to enumerate their entire inventory.
Before serving any chunk, the Drop checks the Jet's Safebux balance on-chain (cached for 1 hour). If sufficient, it stores the payment token in IndexedDB for later claiming and responds with the requested encrypted chunks.
For each chunk received, the Cloud recomputes SHA-256(ciphertext ∥ tag) and checks it matches the expected CID. Then it derives the per-chunk decryption key and decrypts with AES-256-GCM. Any tampered chunk fails CID verification before decryption is attempted.
Safecloud isn't just a file storage network — it's built from the ground up to stream encrypted media in real time. A viewer clicks play and the video starts within seconds, just like YouTube. The difference is that every byte is encrypted before it ever leaves the creator's device, and the decryption happens live inside the viewer's browser. No server ever sees the plaintext.
You click a link. The video starts playing. Behind the scenes, your browser is fetching encrypted chunks from a network of browser-based storage nodes (Drops), decrypting each one locally using a key embedded in the URL, and feeding the decrypted frames to the video player — all in real time. There are no blockchain popups. No wallet signatures mid-stream. The micropayments flow silently in the background as signed claims, settling on-chain in batches later.
The key lives in the URL hash — the part after the #. Browsers never send the hash to any server, so no server ever learns your decryption key, even the one hosting the page.
Safecloud picks the best streaming method for your browser automatically:
The default on Chrome, Firefox, and Edge (desktop and Android). A Service Worker intercepts HLS segment requests, fetches the encrypted chunk from the network, decrypts it, and returns plaintext to the video element. True streaming — no full file loaded into memory. Works with any HLS-compatible player.
Used on iOS Safari 16.4+ and other browsers with fMP4 support. A prefetch loop decrypts chunks slightly ahead of playback and feeds them directly into a SourceBuffer. Smooth adaptive quality switching between video resolutions is supported.
For short clips or older browsers. The full file is decrypted into memory and played as a Blob URL. Not true streaming, but works universally as a fallback. Suitable for audio tracks and short video clips.
Each video is stored as a Merkle tree of encrypted chunks across two tracks: a data track containing the actual fMP4 video segments, and an index track containing the HLS manifest and codec initialization data. The index track is requested first — it tells the player how many chunks there are, their timestamps (PTS values), and what codec to use. Then chunks are requested on demand as the player advances.
Encryption keys are derived per-chunk via an HKDF tree rooted at the file's master key. The path through the tree ["track","data","0","1"] uniquely identifies a chunk's position, and its encryption key is derived by chaining HKDF delegations down that path. This means two identical plaintext chunks at different positions in a video get different ciphertext — so the network sees no repeated patterns even for identical data, while deduplication still works on the ciphertext level (same position = same ciphertext, safely cached).
For more on the key tree, see Encrypted Access Control below.
A creator can upload multiple quality versions of the same video — 1080p, 720p, 360p — each derived from the same master video key via a version label. The player switches between quality levels automatically based on bandwidth, just like adaptive bitrate streaming on any major platform. Each version has its own encrypted chunk tree, and a viewer's access capability covers whichever versions they've been granted access to.
Drops never connect directly to viewers. All chunk requests flow through Jets, which are the only parties that know which IP address is requesting which chunks. Drops only see requests from Jets — not from the viewers behind them. There is no WebRTC between viewers and Drops, so your IP is not exposed to the storage network. Only the Jet you're connected to knows who you are, and even then it only sees your connection, not the decryption keys.
The encrypted chunks on the network are useless without the keys to decrypt them. The channel author decides exactly who gets those keys, when, and under what conditions. This is where the business model lives — not in server-side access checks, but in cryptographic key release.
A channel can publish a free preview — the first few minutes — by releasing decryption keys only for those chunks. The rest of the video remains encrypted and inaccessible. Viewers who want to watch beyond the teaser purchase access, at which point the channel releases keys for the remaining segments. Each tier is a distinct set of subtree grants over the HKDF key tree.
The channel itself is the key authority. A viewer pays (via subscription, one-time purchase, or crypto payment) and the channel releases a signed capability — a bundle of subtree grants covering the purchased content. This capability travels in the URL or is stored in the viewer's wallet. No third party can grant or revoke access; the channel author controls the keys end to end.
The player can be embedded as an <iframe> on any site — Netflix-style aggregators, social platforms, fan communities — without the host site ever seeing the decryption keys. The iframe loads the channel's own player page, which holds the keys in its own origin. The embedding site sees only the video output, not the cryptographic material behind it. This is standard same-origin isolation working as designed.
The decryption keys are only ever released inside an iframe environment the channel author explicitly trusts. The channel author defines which origins are permitted to receive key material. Two trusted environments today:
In both cases, the guarantee is the same: the decryption keys only materialize inside a context the channel author has cryptographically verified. An aggregator site cannot extract the keys from the iframe, even if it controls the surrounding page.
This is one of Safecloud's most innovative features. Access to a video isn't binary — you can grant someone access to a specific time range within a video, a particular quality version, or just the index track (so they can see chapter markers but not watch). All of this is enforced by cryptography, not by server-side checks.
For the simpler picture, see Storing a File. Here's what's happening under the hood.
Every file in Safecloud is navigated by the same path coordinate system — ["track","data","0","1"] for example. This single path indexes three separate trees simultaneously:
Starting from the encryptionRoot, HKDF is applied at each level with a domain-separated label: safecloud.track.data at the track level, safecloud.node.0 at the first subtree level, and so on. Each leaf produces a unique chunkKey and chunkIV for that specific chunk's AES-256-GCM encryption. The critical property: keys are derived top-down, so knowing a subtree key gives you all the leaf keys below it, but knowing a leaf key tells you nothing about its siblings.
CIDs are computed bottom-up: CID = SHA-256(ciphertext ∥ tag) at each leaf, then parent nodes are computed from their children. This tree is public — anyone can verify a chunk belongs to a given root without any secret. The Jet uses Merkle proofs to confirm that Drops are serving chunks that genuinely belong to the requested file.
Parallel to the encryption tree, a separate HKDF derivation produces access credentials at each node. When the Cloud grants someone access to a subtree, it produces a signed access proof that includes the rootCid, the link path, the access level, and an expiry. The Jet verifies this proof before serving chunks — so access is enforced at the routing layer, not just by withholding keys.
Because the key tree mirrors the chunk tree structurally, granting access to a time range (say, minutes 10–20 of a video) is a matter of finding the minimal set of subtree nodes that covers exactly those chunks — and granting only those nodes. The _pathsCoveringRange algorithm in the code does this greedily: it takes the largest subtree that fits within the requested range, then recurses into partial overlaps. The result is a small array of link paths that gives the viewer exactly the chunks they paid for, with zero exposure to the rest of the video.
The index track — which contains HLS chapter markers, initSegment codec data, and timestamps — lives in its own subtree (["track","index"]) with its own derived key. This means you can grant someone access to the index without granting access to the video data. Useful for previews: a viewer can see the chapter list and timestamps of a video before paying to unlock the actual content.
Drops cannot fake storage. Jets issue random spot-check challenges periodically. Failing a challenge has real consequences — and the challenges are paid for, so honest Drops are rewarded even for passing them.
The Jet picks a random CID from a Drop's announced Prolly tree inventory and sends a Safecloud/drop/challenge message. The challenge targets ciphertext the Drop has claimed to store — it cannot be predicted in advance.
The Drop responds with the full encrypted chunk (ciphertext + authentication tag). It cannot fabricate a correct response without actually having stored the chunk — the CID is a cryptographic commitment to the ciphertext itself.
If the recomputed CID matches, the Drop proved storage. Challenges are themselves paid operations — a Drop earns Safebux for passing a challenge just as it does for serving chunks.
If the Drop doesn't respond within the grace window, or returns wrong data, the Jet logs a failure. Repeated failures trigger a slashing event: the Jet notifies the governance contract, which can reduce or forfeit the Drop's pending Safebux balance. This creates a direct economic incentive to store data reliably.
Safecloud uses two signing curves for different purposes. Neither requires the user to manually manage private keys — both are derived from a single wallet signature (or WebAuthn PRF) via the delegation ceremony.
Both session keys are derived deterministically from one interactive wallet signature — no random key generation, no seed phrases to store. The delegation ceremony runs once per session (default 30 days).
Payment flows from the Cloud's wallet to Drops via the OpenClaiming protocol. The Cloud pre-authorizes a budget with a signed EIP-712 struct. Drops accumulate these payment tokens off-chain and settle them on-chain in batches — minimizing transaction costs.
Before uploading or streaming, the Cloud creates a Payment struct — payer address, Safebux token, a hash committing to the authorized recipient set, a max amount, and expiry — and signs it with its secp256k1 session key.
When routing a chunk request, the Jet attaches a signed payment token from its own Safebux balance. The Drop checks the Jet's on-chain balance before serving any chunks.
Each valid payment token is stored locally, deduplicated by SHA-256 of its canonical JSON. The Drop tracks total earned Safebux across all tokens.
When accumulated tokens exceed the configured threshold, the Drop calls OpenClaiming.paymentsExecute() on-chain. The contract verifies the EIP-712 signature, checks the spending line, and transfers Safebux from the payer to the Drop's EVM address in a single transaction.
0x99999febd42cad798fe10ab0b1c563002fc99999 — immutable, no proxy, verified on BSC mainnet and Ethereum mainnet
Opening a Safecloud Drop is as simple as visiting a page and keeping a tab open. Your browser uses its local storage — IndexedDB — to hold encrypted chunks for other users, and you earn Safebux tokens for every chunk you serve and every proof-of-storage challenge you pass.
Safebux per MB served to other users
Additional Safebux for passing proof-of-storage challenges
Seed phrases, private keys, or wallet setup required
Visit the Safecloud Drop page in any modern browser (Chrome, Firefox, Safari, Edge). Your browser will prompt for WebAuthn authentication — this uses your device's Secure Enclave or platform authenticator (Face ID, Touch ID, Windows Hello) to derive your unique Drop identity. No seed phrase is ever shown or stored.
The Jet registers your Drop and starts routing encrypted chunks to you for storage in IndexedDB. You never see the plaintext — only ciphertext and its SHA-256 content address. You can see how much you're storing and how much you've earned in the Drop dashboard.
Each time another user fetches a chunk you're storing, the Jet attaches a signed payment token. These accumulate in your local IndexedDB. When you've accumulated enough (configurable threshold), your Drop automatically submits a claim transaction to the OpenClaiming contract and receives Safebux in your EVM wallet.
Periodically the Jet issues a random proof-of-storage challenge — it asks for a specific chunk by CID. Responding correctly with the chunk earns you bonus Safebux. Failing too many challenges reduces your reliability score and can result in slashing of pending earnings. Keep the tab open and your browser running to pass challenges reliably.
Safecloud Drops use IndexedDB — the browser's built-in structured storage — to hold encrypted chunks. Each major browser sets its own quota. Here's what you can expect by browser, based on MDN's documented limits:
| Browser | Max per origin (best-effort) | With persistent storage | Eviction policy |
|---|---|---|---|
| Chrome / Edge | Up to 60% of disk | Same (auto-approved) | LRU — least recently used origins first |
| Firefox | 10% of disk, max 10 GiB | Up to 50% of disk (8 TiB cap) | LRU — skips persistent origins |
| Safari (macOS 14+ / iOS 17+) | ~60% of disk (browser apps) | ~60% (same) | Proactive after 7 days no interaction |
| Safari (embedded WebView) | ~15% of disk | ~15% | Proactive after 7 days no interaction |
Safecloud's Drop implementation uses LRU eviction internally — when IndexedDB fills up, it automatically removes the least recently accessed chunks first, freeing space for new ones while keeping frequently-requested chunks available. The Drop tracks stored bytes and reports used vs. available storage to the Jet on each announce.
Drops earn Safebux for storage. Jets earn significantly more — they're the routing infrastructure that makes the network work. Running a Jet means running a Node.js process on your own hardware or server, with your own domain, certificates, and stable connectivity.
A Jet accepts socket connections from Cloud clients and Drop browsers, routes encrypted chunks between them, verifies OCP grants and payment tokens, issues proof-of-storage challenges, and relays payment claims to the blockchain on behalf of Drops that can't cover their own gas. Jets are the load-bearing layer of the network.
The Jet server is open source. Clone it from GitHub or download the standalone Node.js executable. The codebase is the same one described on this page — you can read every line before running it.
git clone https://github.com/Safecloud/Safecloud cd Safecloud npm install node jet.js
Safecloud uses TLS certificates that are updated periodically by the Safecloud network. Your Jet downloads the current certificate bundle automatically on startup and checks for updates on a regular schedule:
https://safecloud.org/certificates/latest.zip
This zip contains the current trusted certificates for the network. Safecloud publishes new bundles when certificates are rotated, and it is Safecloud's responsibility to keep them current and announce rotations in advance. Your Jet verifies the bundle signature before trusting any new certificates.
Your Jet generates a unique subdomain under the Safecloud network. To use it locally during development, add an entry to your hosts file:
# macOS / Linux — edit /etc/hosts sudo nano /etc/hosts # Add a line like: 127.0.0.1 myjetnodeid.safecloud.local # Windows — edit C:\Windows\System32\drivers\etc\hosts # (run Notepad as Administrator)
For production deployment, point a real domain at your server's IP and configure the Jet with that domain. The certificate bundle from step 2 covers production Safecloud domains.
Jets earn Safebux from the Cloud clients that use them — a fraction of each Cloud's payment authorization flows through the Jet before reaching Drops. The more reliable and fast your Jet is, the more Clouds and Drops will connect to it, and the more you earn. The Router layer allows Clouds to discover and rank Jets automatically.
Safecloud is a working system. Core encryption, chunking, routing, and streaming are production-grade. On-chain settlement is live. The remaining items are the signing of relay requests and Jet-side payment token signing — both are partial implementations of the same pattern that already works on the Drop side.