Start
Introduction
Bsafe Protocol is a decentralized storage network. Instead of one company's data centre, your files live on storage nodes run by independent operators: people and teams offering spare disk to the network.
Your browser encrypts each file before it leaves your device, and the sealed copy is stored on three different nodes. The network watches every node and rebuilds any copy that goes missing. You pay once, on-chain, and that payment funds a reward pool that only node operators can be paid from.
Three copies on three independent operators' nodes.
Files and names sealed in your browser. Nodes only see ciphertext.
Lost copies are rebuilt from healthy ones within seconds.
Anyone can run a node. A public contract pays them.
Start
Store your first file
About two minutes, start to finish.
- Get a wallet. Any browser wallet such as MetaMask, on Ethereum Mainnet (chain ID
1) with a little ETH. - Open the vault. Go to the vault, press Connect wallet and sign the message. Signing is free.
- Unlock storage. Press Unlock storage and confirm. The price is read from the contract, so you always send the exact amount.
- Upload. Drag files anywhere onto the vault or press Upload. Each one is encrypted on your device, then copied to three nodes.
- Come back from anywhere. Sign in from any browser with the same wallet and your files decrypt locally.
Browser forgot your payment? Sign in again. The backend reads hasPurchased for your wallet from the contract and restores access. You are never charged twice.
Start
Run a node
Nodes are the network. A node is a small Node.js server that keeps encrypted copies on disk and earns ETH for the space it holds, the data it serves and the hours it stays online. One machine can run several nodes, each in its own folder.
1. Mint a join token
Open Nodes and connect the wallet that should own the node and receive its rewards. Name the node, choose how much space to offer, and press Mint join token. A token works once and expires after 24 hours.
2. Start the node
You need Node.js 18 or newer and Git. The node software is open source: github.com/YOUR-ORG/bsafe-node. The console gives you this command with your token filled in.
$ git clone https://github.com/YOUR-ORG/bsafe-node.git $ cd usernode $ npm install $ node server.js --token bsj_… --port 4001 --capacity 5 --home ./nodes/home-server --tunnel
On its first start the node:
- creates its own identity key in
<home>/wallet.json; - signs a join request carrying your token;
- passes a contact check: the coordinator calls the node's
/healthand confirms the same identity answers; - saves its assigned ID in
<home>/node.json.
Later starts need no token. From the usernode folder, run the restart command shown on the node's card in the console. To update, run git pull there and restart.
| Flag | Env var | Default | What it does |
|---|---|---|---|
--token | JOIN_TOKEN | – | One-time join token (first start only) |
--port | PORT | 4001 | Port the node listens on |
--home | NODE_HOME | the usernode folder | Where identity, config and data live |
--capacity | CAPACITY_GB | 1 | Space you offer, in GB |
--tunnel | TUNNEL=1 | off | Open a Cloudflare quick tunnel and use it as --url |
--url | PUBLIC_URL | http://localhost:<port> | Address the coordinator uses to reach the node |
--coordinator | COORDINATOR | http://localhost:5000 | Where the coordinator lives |
Behind a home router
The coordinator pushes data to nodes, so a node needs an address it can reach. --tunnel handles it: npm install also fetches Cloudflare's cloudflared, and the node opens a free quick tunnel (no account, no port forwarding) and joins through it. The address changes on each restart; the coordinator picks up the new one when the node signs in, so identity and earnings carry over. Own a public address? Pass --url https://your-address instead.
3. Retire a node
Press Retire node in the console. The node stops taking new data, its copies move to other nodes, and it shuts down once empty. If the network has no spare room, retirement pauses and you can retry.
The network
How the network works
Five parts cooperate. Independent operators run the storage, Ethereum runs the money, your browser holds the keys, and two network services tie them together.
| Part | Run by | Job | Can see |
|---|---|---|---|
| Your browser | You | Derives your key; encrypts and decrypts files and names | Everything, for you alone |
| Storage nodes | Independent operators | Hold and serve copies; answer only the coordinator | Random-looking bytes |
| StorageIncentive | Ethereum | Takes payments, holds the reward pool, pays operators | Public on-chain state |
| Coordinator | Network service | Admits nodes, places copies, checks health, repairs, meters and settles rewards | Opaque blobs and sizes |
| Backend | Network service | Wallet sign-in, payment checks, the encrypted file index | Ciphertext, encrypted names, which nodes hold which file |
Upload path
Your browser seals the file. The backend checks your session and payment. The coordinator picks three online nodes with room, one per operator first and the least-loaded ones first, and writes the copies in parallel. The backend records which nodes hold the file.
Download path
The backend asks the coordinator for the file from any node holding a live copy. The ciphertext reaches your browser, which decrypts it. If one copy is missing or fails its integrity check, another one is used.
The network
Replication & repair
Each copy goes to a different operator whenever the network has enough of them, so one person switching off their machines can't take out every copy. When a node goes silent, the coordinator asks the backend to repair it: every copy that node held is re-created on another node from a healthy replica, and the index is updated.
A corrupted or tampered copy fails its integrity check on download and is served from another copy instead. How the network handles failures is on the Why page.
The network
Security model
- Keys. A random 32-byte vault secret is created in your browser and encrypts every file with AES-256-GCM. The backend stores it only wrapped, under a key your browser derives with HKDF-SHA-256.
- Metadata. Names, types, sizes and dates are encrypted too. The server sees opaque IDs.
- Integrity. AES-GCM authenticates every file, so a node can't change a copy without the change being detected.
- Node isolation. Nodes accept data requests only with the coordinator's session token and reject file IDs that could escape their data folder.
- Node identity. Each node signs its join and login challenges with its own key, so it can't pose as another node.
Money
Payments
Storage is a one-time purchase: call purchaseStorage() with exactly storagePrice. The contract rejects any other amount and any second purchase from the same wallet. Most of the payment goes to the reward pool for node operators; a fixed protocol fee (capped at 20% by the contract) goes to the protocol.
How the backend checks a payment
- Its RPC and the transaction are both on chain
1. - The transaction succeeded and has enough confirmations.
- It went to the StorageIncentive address, from the signed-in wallet.
- Its call data is exactly
purchaseStorage(). - Its value equals the configured price.
- The contract emitted a matching
StoragePurchasedevent.
A transaction hash can be used once. If it is missing or stale, the backend falls back to hasPurchased(wallet), read at a confirmed block. Live values are on the Contracts page.
Money
Node rewards
While a node is online, the coordinator meters three things:
| Source | Measured as |
|---|---|
| Storage | Bytes held × time |
| Bandwidth | Bytes served on downloads |
| Uptime | Time online and reporting in |
Rates and the minimum claim are set in the coordinator's configuration (REWARD_*, MIN_PAYOUT), and the console always shows the live numbers.
Getting paid
- Register your operator wallet once with
registerNode(). - Claim. The coordinator, as the contract's settler, calls
rewardNode()to move your earnings from the pool to your balance. A claim never exceeds what the pool holds; the rest stays unclaimed. - Withdraw your balance to your wallet with
withdraw(), any time. Once credited, nobody can take it back.
Reference
HTTP API
Backend
| Method | Path | Purpose |
|---|---|---|
| POST | /nonce | Get a login nonce for a wallet |
| POST | /login | Trade a signed nonce for a session |
| GET | /payment-status | Whether the wallet has storage (restores on-chain purchases) |
| POST | /payment | Verify a purchase; txHash is optional |
| POST | /upload | Upload a sealed file and its metadata |
| GET | /files | List your encrypted file index |
| GET | /download/:id | Fetch a file from any live copy |
| DELETE | /files/:id | Remove a file from every node |
Authenticated calls send the session in the x-session header.
Coordinator
| Method | Path | Purpose |
|---|---|---|
| GET | /network | Public network totals and reward rates |
| POST | /operator/nonce, /operator/login | Wallet sign-in for operators |
| GET | /operator/overview | Nodes, earnings, join tokens and payouts |
| POST | /operator/join-tokens | Mint a one-time join token |
| POST | /operator/nodes/:id/exit | Start retiring a node |
| POST | /operator/payouts | Settle unclaimed earnings on-chain |
Operator calls send the session in the x-operator-session header.
Reference
Troubleshooting
“Switch your wallet to Ethereum Mainnet”
Approve the network switch your wallet offers. To add the network by hand: chain ID 1, RPC https://eth.drpc.org, currency ETH.
The payment isn't confirmed yet
Wait a few seconds and press Unlock storage again. The same transaction is re-checked and you won't be charged twice.
“No storage nodes with free capacity are online”
The network needs online nodes with free space. Check the live node count on the Nodes page, or add a node of your own.
A node fails its contact check
The coordinator couldn't reach the node at its --url. Check the port and address, or use --tunnel.
The Claim button is disabled
Register your payout wallet first, make sure your unclaimed earnings pass the minimum, and check that the coordinator has settlement configured.