BsafeDecentralized storage

Whitepaper · v2.0

Bsafe Protocol

How files are sealed, where they live, how the network mends itself, and how a single payment turns into rewards for the people who host the disks.

The short version

Abstract

Bsafe Protocol is a file storage network in which every file is encrypted in the owner's browser and then kept as three copies on nodes run by independent operators. People sign in with a wallet instead of a password, and the same wallet identity unlocks their vault key. A coordinator admits nodes, places copies, notices failures and re-creates lost copies within seconds. Storage is bought with one fixed payment to the StorageIncentive contract on Ethereum Mainnet; the contract splits the payment between a small protocol fee and a reward pool that only node operators can draw from. This paper sets out the design, what it guarantees, and where it falls short today.

1. Why this exists

Most people keep their files with a few large cloud providers. Those services encrypt data at rest, but they hold the keys. A provider can read stored files, scan them, lose them in a breach, hand them over on request or lock an account under terms that change without warning. The user pays a subscription for this arrangement, and all of the money goes to the provider.

Decentralized storage networks fix parts of the problem and bring trade-offs of their own. Content-addressed networks such as IPFS make a file available to anyone who knows its identifier, and keep it only while some node chooses to pin it. Incentivized object stores such as Storj offer encryption and durability, but are used like a cloud account, with usage-based billing and a dedicated token for operator payouts.

Bsafe aims at a narrower target: private personal storage. It should be as easy as a cloud drive, private by construction, durable without effort, and paid for in a way that rewards whoever supplies the disks.

2. Design goals

  1. Private by default. Plaintext and file names never leave the owner's device. Every server-side part handles ciphertext only.
  2. No passwords. Identity and key recovery come from a wallet the user already controls.
  3. Durable without effort. Data survives node failures with no action from the owner.
  4. Open to anyone. Anyone can contribute disk space and be paid for it.
  5. Money you can check. Payments and payouts follow rules enforced by a public contract.
  6. Small enough to reason about. Each component is simple enough to audit.

Non-goals

  • Public distribution. Files are private to their owner. Sharing is not part of this version.
  • Payment anonymity. A purchase is an on-chain transaction, so that a wallet bought storage is public. File contents are not.
  • Large-object throughput. Files are encrypted whole in the browser, so uploads are limited to 50 MB each.

3. The moving parts

Four off-chain roles and two contracts. Each is given the least information it needs.

ComponentJobCan see
Browser clientWallet sign-in, key derivation, encryption, decryptionEverything, only for its user
BackendSessions, payment checks, the encrypted file index, repair bookkeepingCiphertext, encrypted metadata, wrapped vault keys, wallet addresses
CoordinatorNode admission and authentication, placement, health checks, repair, reward metering and settlementOpaque blobs, file IDs, sizes, node addresses
Storage nodesHold and serve encrypted copiesRandom bytes under random IDs
StorageIncentiveTakes payments, holds the reward pool, pays operatorsPublic: purchases and payouts

The backend never speaks to nodes directly; all storage traffic passes through the coordinator. The two authenticate each other with a shared secret (INTERNAL_API_KEY) compared in constant time.

4. Cryptography

4.1 Sign-in

The client asks for a random 256-bit nonce for its address and signs Login nonce:<nonce> with its wallet (an EIP-191 personal message). The backend recovers the signer, deletes the nonce so it can't be replayed, and issues a random session token. Signing costs no gas and no password exists anywhere in the system.

4.2 Key hierarchy

Every user has two keys:

  • Unlock key. The client builds a fixed message, Bsafe Protocol Master Unlock v1 followed by the lowercase wallet address, hashes it per EIP-191, and runs the hash through HKDF-SHA256 (salt bsafe-protocol, info file-encryption) to produce a 256-bit AES-GCM key. It exists only in the browser.
  • Vault key. On first use the client draws 32 random bytes from the Web Crypto CSPRNG. This key encrypts the user's files. The backend stores it only after it has been encrypted (wrapped) under the unlock key.

On a new device the user signs in, re-derives the unlock key, downloads the wrapped vault key and unwraps it locally.

Known weakness. The unlock key is derived from public inputs: a constant message and the wallet address. It is not derived from a signature, so it is not secret from anyone who knows the address and the code. Anyone able to read the backend's database could therefore unwrap vault keys and decrypt that user's files after downloading them. Storage nodes cannot, because they never see wrapped keys. Moving the derivation onto a deterministic wallet signature (RFC 6979) would close this gap and is the first item under future work.

4.3 File and metadata encryption

Each file is encrypted with AES-256-GCM under the vault key, using a fresh random 96-bit IV that is prepended to the ciphertext. File metadata (name, type, size, upload time) is encrypted the same way as a separate object. Nodes and the coordinator handle only the encrypted blob, filed under a random UUID.

4.4 Properties

  • Confidentiality against nodes: no storage node holds anything that can decrypt user data.
  • Integrity: GCM's authentication tag makes any modification of a stored copy fail decryption.
  • Portability: any browser with the same wallet can recover the vault. There are no key files to back up.

5. Life of a file

5.1 Upload

  1. The client encrypts the file and its metadata.
  2. The backend checks the session and confirms the wallet has paid (section 7.1).
  3. The backend forwards the ciphertext to the coordinator.
  4. The coordinator picks three nodes. A node qualifies if it is online, active and has room. Nodes with lower load rank first, and nodes of operators not yet chosen come before a second node of the same operator, so one operator going dark can't remove every copy.
  5. The copy is written to every chosen node, and the backend records the file and one entry per copy.

5.2 Download

The backend checks that the session's wallet owns the file and asks the coordinator for it, trying each copy in turn until one answers. The ciphertext streams to the browser, which decrypts it locally.

5.3 Delete

The copy is removed from every node that holds it, and then the file and copy records are deleted.

6. The node network

6.1 Joining

An operator signs in to the console with their wallet and mints a join token. Tokens are single-use, expire after 24 hours and are stored only as a hash. On first start a node creates its own secp256k1 identity key and signs a join message binding the token, its identity address and the URL it will serve on. Before accepting the node, the coordinator runs a contact check: it calls /health at that URL and requires the node there to report the same identity. A node cannot register an address it doesn't control.

6.2 Authentication

After joining, a node proves itself by challenge and response: the coordinator issues a random nonce, the node signs it together with its ID and URL, and receives a session token valid for 24 hours. Every request between coordinator and node carries it.

6.3 Liveness and repair

Nodes send a heartbeat every 5 seconds with used space, file count, capacity and version. A node silent for 15 seconds is marked offline. The coordinator then asks the backend to repair every copy that node held: a surviving replica is fetched, written to a new node chosen under the same placement rules, and the records are updated. When an offline node returns, it re-authenticates and resumes.

6.4 Retirement

An operator can retire a node from the console. It moves to EXITING, its copies are migrated to other nodes while it is still online, and it becomes EXITED only once its data is safe elsewhere. An exited node cannot rejoin under the same identity.

7. Economics

7.1 Storage payments

Users call purchaseStorage() on StorageIncentive with exactly the current storagePrice. Underpaying, overpaying, buying twice and plain ETH transfers all revert. Before granting storage, the backend independently checks that the transaction is on the right chain, went to the contract, carries the purchase call data, paid the exact price, has enough confirmations and emitted a matching StoragePurchased event for that wallet. A hash can be used once. If it is lost, the contract's hasPurchased flag recovers the purchase.

7.2 The split

Each payment is divided on-chain: protocolFeeBps goes to protocol fees and the remainder to the reward pool. The fee is fixed at deployment and the contract caps it at 20%. Both numbers are public on the Contracts page.

7.3 Metering

While a node is online the coordinator meters three kinds of work:

ComponentAccrues as
Storagebytes held × time online × rate per GB-hour
Bandwidthbytes served to users × rate per GB
Uptimetime online × rate per hour

Rates are set by the coordinator's operator and published to nodes, so accruals can be tuned to track what the pool actually receives. Amounts are integers in wei.

7.4 Settlement

An operator registers a payout wallet with registerNode() and claims from the console. The coordinator's settler key calls rewardNode(operator, amount), which moves funds from the pool to the operator's balance. The contract refuses any reward larger than the pool, so every credited balance is fully backed. Operators then call withdraw() whenever they like (pull payments, guarded against re-entrancy). Claims above what the pool holds stay unclaimed until more purchases arrive, and anyone can top up the pool with fundRewardPool().

7.5 Owner powers

The owner can change the price for future purchases, replace the settler and collect protocol fees. The owner can't touch the reward pool or any operator balance, and can't raise the fee. Ownership moves in two steps, so it can't be sent to a mistyped address.

8. Security analysis

8.1 If one part is compromised

CompromisedAttacker getsAttacker can't
A storage nodeEncrypted copies under random IDsRead files, or alter them undetected
The backendEncrypted metadata, wrapped vault keys, the wallet-to-file indexRead files without the weakness in 4.2; with it, wrapped keys can be unwrapped (see below)
The coordinatorNode addresses, routing of opaque blobs, the settler keyDecrypt files, or pay out more than the reward pool holds. With a dedicated settler it also can't change prices or take fees
The network pathCiphertext in transit (inside TLS on public links)Read or silently change content

8.2 Known limitations

  • Key derivation. As explained in 4.2, the unlock key is not secret from the backend operator. Treat the backend as trusted for confidentiality until this is fixed.
  • Metadata leakage. The backend and coordinator learn how many files a wallet stores, their sizes and when they are accessed.
  • Wallet loss. Losing the wallet loses access. There is no recovery by design.
  • Coordinator trust. The coordinator is one operator-run service, trusted for availability, honest placement and accurate metering. It could under-report rewards, but it can never pay out more than the pool holds.
  • No storage proofs yet. Nodes aren't audited with proofs of retrievability. A node that silently drops data is caught when that copy is read or repaired, and the other copies cover it.
  • Key separation. The settler should be a dedicated wallet, not the owner. If one key holds both roles, a coordinator compromise also exposes owner powers.
  • No external audit. The contracts have unit tests but have not been independently audited.

9. Comparison

A rough map against alternatives, as understood at the time of writing. Check each project's own documentation for current details.

Bsafe ProtocolStorjIPFSCloud drives
Client-side encryptionAlwaysYesNoRarely; the provider holds keys
Persistence3 copies, auto-repairErasure coding, auto-repairWhile pinnedProvider-managed
IdentityWallet signatureAccountNoneAccount
PaymentOne-time, on-chainMonthly usageNot built inSubscription
Operator payoutsETH, on demand, contract-enforcedToken, monthlyNonen/a
Storage overhead3×Lower (erasure coding)VariesProvider-managed
CoordinationSingle coordinatorSatellitesPeer-to-peerCentralized

Bsafe gives up some storage efficiency and some decentralization of coordination in exchange for simplicity: full copies mean any one survivor can serve a download, and a single coordinator keeps placement and repair easy to reason about.

10. Future work

Directions under consideration, not commitments:

  • Signature-based key derivation: derive the unlock key from a deterministic wallet signature, so the backend can never unwrap vault keys.
  • Storage audits: periodic challenges that make nodes prove they still hold their copies.
  • Erasure coding: lower overhead at the same durability.
  • Multiple coordinators: federated or replicated coordination, removing the single point of failure.
  • Chunked, streaming encryption: to lift the per-file size limit.
  • Sharing: per-file keys that can be granted to another wallet.
  • On-chain node registry: making admission and reward rates publicly verifiable.

11. Conclusion

Bsafe tries to give people the ease of a cloud drive without handing a provider the keys. Encryption happens on the user's device, durability comes from replication and automatic repair across independent operators, and the money moves through a contract that guarantees every operator reward is backed. The design is deliberately small, so its guarantees and its limits can both be stated plainly, and the biggest limit today is named in section 4.2.

Appendix: parameters

Copies per file3
Heartbeat interval5 s
Offline after15 s without a heartbeat
Join token lifetime24 h, single use
Node session lifetime24 h
Largest file50 MB
CipherAES-256-GCM, 96-bit random IV
Key derivationHKDF-SHA256 over the EIP-191 hash of the unlock message
ChainEthereum Mainnet (chain ID 1)
StorageIncentive0x0299C202DfFbeB56e4abbea1888c5647e7608C0f
Storage price, protocol feeSet at deploy; live on the Contracts page. Fee cap: 20%