Skip to content

Security whitepaper

Encrypted where you work, not where we store it.

ReachFS servers cannot read your file contents or file names, by construction. This paper shows how, and states every limit of that claim.

Last updated October 3, 2026

Summary

ReachFS servers cannot read customer file contents or file names. Not by policy, by construction: compromising ReachFS infrastructure yields ciphertext.

Every file and name is encrypted on the customer's own computer before it is uploaded. The keys that open them exist only on that workspace's approved devices, and the server only ever relays them sealed. Object storage, whether ReachFS-managed or the customer's own bucket, holds ciphertext only.

The claim has edges, and this paper states them: the server sees the shape of the folder tree, file sizes and when things happen; notifications name the few files that were locked, conflicted or stuck uploading; and a workspace whose approved devices and recovery phrase are all lost cannot be recovered, by anyone. Known limitations lists every one.

Architecture at a glance

Plaintext exists only on your computersYour computersReachFS app and engineEncrypts before uploadDecrypts after downloadKeys: Credential ManagerFile list, unsent changesPlaintext; needs BitLockerReachFS metadata serviceSees: tree shape, sizes, timingHolds: sealed keys it cannot openObject storage: B2 or your bucketEncrypted blocks onlyTeam cache box (your LAN)Encrypted blocks, no keyssync and lockssealed metadataencrypted blocksmints scoped keysencrypted blocksread-only key

Only members' computers ever hold keys or plaintext. The metadata service syncs sealed metadata and hands out short-lived storage keys; object storage and the optional team cache hold only encrypted blocks.

Threat model

The design assumes ReachFS's own servers, staff and storage provider may be compromised, and protects file contents and names against all three.

Threat Protected How
ReachFS server compromise Yes The server holds no key that can decrypt
Malicious ReachFS insider Yes Same: an insider sees ciphertext and the tree's shape
Object storage compromise Yes Blocks are encrypted before upload
Network interception Yes TLS 1.3, with the payload already encrypted underneath
Stolen laptop, powered off Yes, with full-disk encryption Device key in Windows Credential Manager; offline copies are ciphertext. The file list and unsent changes rely on BitLocker
Stolen team cache box Yes It holds encrypted blocks and no keys
Departed employee, files written after they left Yes Removal starts a new workspace key they have no path to
Departed employee, files written before they left Opt-in They may keep the old key; re-encryption closes it for live files
Stolen ReachFS password, inbox or session Partly Decrypts nothing without an approved device. Can request a new device, which an existing device must still approve
Malicious client binary, supply chain Partly Signed releases; reproducible builds are planned
Stolen laptop, unlocked and running No The drive is mounted and readable, as any local disk would be
Traffic analysis No Sizes and timing are visible; see What the server can and cannot see

Cryptography

Each workspace has a 256-bit workspace master key (WMK), generated on the device that creates the workspace and never transmitted unwrapped. Everything else derives from it.

  • Device keys. Each device generates an X25519 keypair at enrollment. The private key stays in Windows Credential Manager; the public key goes to the server. A device receives the WMK only as a sealed box addressed to its public key.
  • Derived keys. BLAKE3 derive_key turns the WMK into a metadata key (file metadata and directory-entry names), a content-addressing key (block ids) and a name key (case-insensitive name hashes, so the server can enforce one name per folder without seeing names).
  • Blocks. Files are split into 4 MiB blocks. A block's id is a keyed BLAKE3 hash of its plaintext, and its key and nonce are derived from that id:
key || nonce = BLAKE3_xof_keyed(block_root, block_id)[..56]

The key is a function of the plaintext, so each key encrypts exactly one message in its life, with no counter to keep. Identical content in one workspace produces the same block, which is stored once. Deduplication is workspace-scoped by design: another customer's copy of the same footage has a different id, so dedup cannot reveal what other tenants hold.

Metadata and names are rewritten in place, so they use a random 24-byte nonce, and each blob is bound by associated data to the row it belongs to: a blob moved to another file fails to open.

Purpose Primitive
Block, metadata and name encryption XChaCha20-Poly1305
Key wrapping to a device X25519 sealed box
Key derivation, content addressing, name hashing BLAKE3 (keyed and derive_key)
Recovery phrase BIP-39, 24 words, with its checksum
Stored tokens (invites, sign-in codes, sessions) SHA-256 of 32 random bytes
Passwords argon2id, 64 MiB, 2 passes
Server-side secrets at rest AES-256-GCM
Transport TLS 1.3, HSTS

Devices and keys

The server is a relay, not a key holder. An attacker with the full database and full control of sign-in still cannot enroll a device that decrypts: only an already-approved device can wrap the WMK to a new one.

  1. First device. It generates the WMK, seals it to its own public key, and uploads the sealed blob and a 16-byte BLAKE3 fingerprint. The server can open neither.
  2. Later devices. A new device registers its public key and waits as pending. An approved device belonging to the same person, or to an owner or admin, seals the WMK to it. The new device opens it locally.
  3. Revocation. Revoking a device deletes its sealed key on the server and flags the workspace for a new key epoch. On its next request the device is told it is revoked: it unmounts, then deletes its file list, unsent changes, cache and private key. Admins can do this to a member's device with Remove and wipe.

Every request names the device making it, and the server checks that the device belongs to the signed-in user and is not revoked. Long-lived sync streams re-check the session, device and membership every 30 seconds, so revocation reaches an open stream within one heartbeat.

A revoked device cannot be re-approved; it must enroll again with a new keypair. A device that never reconnects keeps what it already had on disk.

Removing people

A removed member cannot read anything written after their removal. Removing someone, or revoking a device, starts a new key epoch: a fresh random WMK, sealed to every remaining device, with the previous key wrapped under it.

epoch 1   WMK_1
epoch 2   WMK_2  + wrap(WMK_1)
epoch 3   WMK_3  + wrap(WMK_2)

Current members walk down this ladder to read anything ever written. A departed member holding an old key has no path up: each new key is fresh randomness, not a derivation. The server cannot rotate keys itself; the removing admin's device does it at once, or the next approved device to connect. Once rotated, the server refuses new data sealed under an older epoch, so a lagging device cannot keep writing what the departed member could read.

Removal also releases the person's file locks and revokes the storage credentials their devices held.

Question Answer
Can they read files written after removal? No
Can they read files written before? Yes, if they obtain the ciphertext; they may hold the old key
Can they get that ciphertext from ReachFS? No. Their membership and storage keys are gone; the risk is out of band, such as an old backup
What closes the past? Re-encryption (reachfs rewrap): every live file's content and metadata is rewritten under the current key, and the old blocks are deleted after the garbage collector's grace period
What re-encryption does not cover Directory-entry names, and old copies pinned by snapshots, the recycle bin or version history until they expire

One key is deliberately never rotated: the name key, which enforces one name per folder through a unique index. A departed member can therefore test whether a given file name exists, but cannot list names.

What the server can and cannot see

The server cannot read file contents, file names, folder names or media details. It does see the structure it needs to sync, lock and bill, listed here in full.

The server sees What that reveals
The folder tree's shape (which item sits under which) Counts and nesting depth
File, block and slice sizes A known file's size may fingerprint it
When every operation happens, and by which device Working hours, project cadence, deadline crunches, who works on what
Member emails and device names Needed to run the product
Paths of files named in notifications: a file someone was refused because it was locked, a conflicted copy, or files that keep failing to upload Those few paths are stored with the notification for 90 days and sent through the email provider
Which files each person favourites, by id only Interest, not names
Which files share blocks That identical content, or a copy by reference, exists within the workspace

Hiding these would cost what the product is for: tree shape needs oblivious data structures, sizes need padding, timing needs cover traffic. They are documented instead. Because the server cannot read names, it also cannot browse files: only the desktop app, holding the keys, can list them.

Data at rest and in transit

Everything ReachFS stores off the customer's computers is ciphertext or a hash. On the computers, two stores are deliberately plaintext and rely on full-disk encryption.

Where State Protection
Object storage (managed or own bucket) Encrypted blocks Client-side XChaCha20-Poly1305, plus the provider's own at-rest encryption
Server database: file metadata and names Encrypted Client-side; the server cannot open them
Server database: tree structure, sizes, times Plaintext Listed in What the server can and cannot see
Server database: own-bucket credentials, authenticator secrets Encrypted AES-256-GCM under a server secret
Server database: passwords, tokens, sessions Hashed argon2id; SHA-256 for random tokens
Computer: offline copies (block cache) Encrypted The same ciphertext as object storage
Computer: file list (mirror.sqlite) Plaintext names Windows account permissions; wiped on revocation. Plaintext so search is instant
Computer: unsent changes (write-ahead log) Plaintext Windows account permissions; drained as it uploads; wiped on revocation
Computer: device private key, session Protected Windows Credential Manager (DPAPI)
Team cache box Encrypted blocks Holds no key

Full-disk encryption (BitLocker or equivalent) is a deployment requirement: it is what protects the two plaintext stores on a stolen, powered-off laptop.

In transit, everything uses TLS 1.3 with HSTS, terminated at the server's reverse proxy. That is defence in depth: the payload was already encrypted on the device.

Accounts and sign-in

Accounts are ReachFS's own; no third-party identity service sits in the sign-in path. A ReachFS account identifies a person; it decrypts nothing on its own.

  • Passwords are hashed with argon2id, at least 10 characters. A sign-in for an unknown email takes as long as a real one, so it reveals nothing about who has an account.
  • Two steps for every password account. An authenticator app (TOTP) is required, with ten single-use backup codes. Each code works once.
  • Passkeys (WebAuthn with user verification) count as both steps.
  • Google and Microsoft sign-in uses OpenID Connect with PKCE. Only a verified email is accepted, because invites match on email.
  • Limits. 10 wrong passwords in 15 minutes lock the account for 15 minutes; sign-in and reset requests are rate-limited per address. Security events (a new location, password changes, a new authenticator, backup codes used) are emailed to the person.
  • Workspaces can require two-step sign-in. Sessions record whether their sign-in had a second step, and the server refuses the rest.

Desktop sign-in happens in the browser and hands back to the app with a one-time code bound to a PKCE challenge, so a program that intercepts the reachfs:// link cannot use it. The app also checks a state value, so an attacker cannot sign a victim's app into the attacker's account. The resulting desktop session is stored only as a hash on the server, kept in Windows Credential Manager on the computer, rolls forward 30 days with use, and can be revoked. A session cannot create a new session: starting one always takes a live sign-in on the website.

Invite links carry their token in the URL fragment, which browsers never send to servers, and are accepted only by the invited email address.

Storage access

No ReachFS app ships a storage key. A signed-in engine asks the server for credentials, and the server checks membership and that the request comes from one of the person's own non-revoked devices. It then returns a key it minted that:

  • reaches only that workspace's prefix in the bucket;
  • can read, or read and write (viewers get read-only), and can never delete;
  • expires after two hours and is held only in the engine's memory.

Removing a member, demoting one to viewer, or revoking a device deletes the workspace's issued keys at the provider immediately, on every server instance. The keys that can delete stay on the server, with the garbage collector. A leaked engine key therefore reaches one workspace's ciphertext for at most two hours, and can destroy nothing.

Bring your own bucket. A workspace owner can keep the workspace in their own B2 or S3-compatible bucket. The owner's key is encrypted at rest on the server (AES-256-GCM, bound to the workspace) and never returned to any client.

B2 bucket Other S3-compatible bucket
Keys given to devices Minted from the owner's key, as above: one workspace, no delete, two hours The owner's own key, after the owner confirms it reaches only that bucket
On removal or revocation Revoked at once Valid until the owner rotates it at their provider

Either way the bucket only ever holds ciphertext, and never a workspace key.

Archive. Archiving a folder makes the server copy its encrypted blocks to a separate cold-storage bucket, verify each copy, and only then delete the original. The server moves ciphertext it cannot read; nothing is decrypted to archive or restore. Workspaces on their own bucket cannot be archived into ReachFS's archive storage.

Sharing outside the workspace

A share link gives someone without an account one file for review, or one folder read-only, as it was when the link was made. It carries its own key, and the server still cannot read what is shared.

  1. The app draws a fresh 256-bit secret and puts it in the link's #fragment, which browsers never send to a server.
  2. The link's manifest (each block's id with its own key, and how the bytes lay out) is sealed under that secret with XChaCha20-Poly1305. So are reviewers' comments and names.
  3. The secret goes to the server only sealed under the workspace's metadata key, so members' apps can read what reviewers wrote.
  4. The recipient's browser decrypts everything locally.

The server serves only the blocks the link was made with, only with a 12-hour token it issues after checking the link's password (bcrypt) and expiry. Guests never receive the workspace key, because a workspace key cannot be limited to one folder; they get exactly that folder's blocks.

The server learns that a link exists, which blocks it covers and their sizes, and when it is opened. Turning downloads off hides the download button, but a viewer who can watch can record the screen, and the app says so.

Key recovery

ReachFS holds no key that can open customer files, so it cannot recover them. If every approved device in a workspace is lost, the only way back is the workspace's recovery phrase.

  • When a workspace is created, its creator is shown a 24-word BIP-39 phrase once, and must confirm they saved it before the workspace exists. Its checksum makes a mistyped word fail at once instead of silently deriving a useless key.
  • The phrase derives an X25519 keypair. The server stores only the public half and a copy of the WMK sealed to it: one more blob it cannot open.
  • When the key changes after a removal, the rotating device re-seals the new key to the phrase's public half, so the phrase keeps working without anyone typing it in.
  • Recovery opens that blob on a new device with the phrase and seals the key to that device.
Option considered Decision
No recovery at all Rejected: one lost laptop would lose a solo user everything
User-held recovery phrase Shipped
Optional escrow held by the customer's own admin Planned; it would be the customer's choice, and stated as such
Escrow held by ReachFS Rejected: it would end the security claim

Operational security

Biased toward keeping data. Orphaned storage costs pennies; deleting live data is unrecoverable. So:

  • A block no file uses any more is kept for a 30-day grace period (14 days for what was already unreferenced when a recycle bin is emptied, never less) before the weekly sweep deletes it.
  • The sweep re-checks, inside its own transaction, that no live file uses a block before deleting it, whatever the reference count says. It runs as its own service so it can be stopped on its own, and stopping it is step one of the data-loss runbook.
  • A monthly audit compares storage with the database and reports orphans; it never deletes them.
  • Each computer keeps unsent changes in a write-ahead log until the server confirms them, so a crash or a refused upload loses nothing.

Application controls. Every call checks workspace membership and role on the server; roles are never trusted from the client. Inputs are bounded by schema and explicit checks, mirrored as database constraints. Queries are generated and parameterised. Membership, device and storage-configuration changes are audit-logged. Dependencies are scanned with cargo audit, govulncheck and pnpm audit. Releases are code-signed.

Incident response.

Scenario Response
Server compromise Rotate server secrets and storage credentials. Customer data stays encrypted. Customers notified within 72 hours
Client vulnerability A signed emergency release, and the server can refuse outdated clients
Suspected data-loss bug Halt the sweep at once, freeze deploys, investigate from the write-ahead logs and event log

Disclosure. Report vulnerabilities to security@reachfs.app (also listed in security.txt). We acknowledge within 2 business days, triage within 5, fix critical issues within 30 days, credit reporters who want it, and take no legal action against good-faith research that avoids customer data and service disruption. Flaws in the client's cryptography or device enrollment are the most valuable class to report.

Compliance roadmap.

Milestone Status
Documented security policy set and vulnerability disclosure policy Done
Third-party penetration test of server and client Planned before general availability
SOC 2 Type I Planned at launch
SOC 2 Type II 6 to 12 months after Type I
MPA / TPN After v1

Known limitations

Stated plainly, because overclaiming would be worse than the limits themselves.

  1. Metadata leaks. The server sees tree shape, sizes, timing and who touched which item. Notifications name the few files that were locked, conflicted or stuck.
  2. No recovery without a device or the phrase. If every approved device and the recovery phrase are lost, the files are gone. ReachFS cannot help.
  3. Removal does not reach the past by itself. A removed member may keep the old key. Re-encryption closes this for live files, but not for directory-entry names or for copies pinned by snapshots, the recycle bin or version history until they expire.
  4. Revocation needs the device to reconnect. A lost computer that never comes online again keeps what was on its disk.
  5. Two plaintext stores on each computer: the file list and unsent changes. Full-disk encryption is required to protect them.
  6. An unlocked, running computer is out of scope. Its drive is readable like any local disk.
  7. Own S3 buckets have weaker key control than B2. Devices hold the owner's key, which does not expire and is not revoked on removal until the owner rotates it.
  8. Share links can be recorded. Turning downloads off does not stop screen capture.
  9. The name key is never rotated, so a departed member can test whether a given file name exists, without being able to list names.
  10. Supply chain. Releases are signed; reproducible builds are not yet available.