Security whitepaper
Updated October 12, 2026
This is the technical description of how Terebyte protects files: what runs where, which keys exist and how they’re made, what each part of the system can see, and the limits we know about. For a plainer version, see How zero-knowledge works. What we protect against, and what we don’t, is in the threat model.
1. The goal
A member’s files, and their file and folder names, are readable only on devices where the member has unlocked their vault. Nobody else can read them: not Terebyte, not the companies that host us, and not anyone who gets a copy of our database or storage. That includes a court or government that orders us to hand them over.
2. The parts of the system
| Part | Runs on | Handles |
|---|---|---|
| The web app (in your browser) | Your device | All encryption and decryption, with the browser’s Web Crypto API |
| The website and API | Vercel (United States) | Sign-in, account records, encrypted names and locked keys, upload and download permissions |
| The database | Neon (United States) | Account records; encrypted names and locked keys, as stored by the API |
| File storage | Backblaze B2 (United States) | Encrypted file pieces, uploaded straight from the browser |
| Download servers | Our own servers at OVHcloud (Virginia and Oregon) | Passing encrypted pieces from storage to the browser |
| Resend | Sign-in links and service notices | |
| Payments | Stripe | Card payments on Stripe’s own checkout page |
3. Keys
All keys are made and used in the browser. Randomness comes from the Web Crypto API (crypto.getRandomValues).
| Key | How it’s made | What it protects |
|---|---|---|
| Master key | 256 random bits, once per vault | Everything below |
| Passphrase key | PBKDF2-SHA256, 600,000 iterations, 16-byte random salt, from the vault passphrase | Seals the master key (AES-256-GCM) |
| Recovery key | 160 random bits, shown once as 32 characters; HKDF-SHA256 turns it into a sealing key | Seals the master key a second time (AES-256-GCM) |
| Name key | HKDF-SHA256 from the master key | File and folder names (AES-256-GCM) |
| File-key wrapping key | HKDF-SHA256 from the master key | Each file version’s key (AES-256-GCM) |
| File version key | 256 random bits for every version of every file | The file’s contents |
| Vault proof | HKDF-SHA256 from the master key; the server stores only its SHA-256 | Proves to the server that a request comes from someone holding the master key |
Every AES-256-GCM value uses a fresh 12-byte random IV and carries associated data naming its purpose (for example “master key, sealed by passphrase”, “file key” or “name”). So a value can’t be moved into a slot meant for something else. Derived keys are held as non-extractable CryptoKey objects in memory.
The server stores the two sealed copies of the master key, their salts and the iteration count. The browser refuses a sealed copy with fewer than 600,000 iterations or of the wrong length. So a malicious server can’t serve a weaker one to make passphrase guessing cheaper. Each vault records its key-derivation method, so it can move to a stronger one later.
4. Files
- Each file version is split into pieces of 8 MiB. Each piece is encrypted with AES-256-GCM under the version key. The IV is 8 random bytes followed by the piece number.
- Each piece’s associated data holds a fixed label, its number and whether it is the last piece. Reordering, repeating, dropping or altering pieces makes decryption fail.
- The browser uploads pieces straight to storage, using upload addresses signed for one piece’s exact size. Our servers record the encrypted size; storage holds only ciphertext under random names.
- Downloads go through our download servers. A short-lived, signed permission lets one of them send one byte range of one encrypted object. They never hold keys, and they keep no access logs.
- Previews decrypt in memory in the browser. Decrypted files are never shown as web pages or in frames, so a file can’t run code on our site.
5. Unlocking, reloads and locking
Unlocking derives the passphrase key and opens the master key in the browser. So that a reload doesn’t ask again, the browser keeps the master key in IndexedDB, sealed under a key derived (HKDF-SHA256) from a secret only the server holds for that sign-in. The server never receives the sealed copy, and the device can’t open it without the server. The server deletes its secret:
- when the vault is locked;
- at sign-out;
- after the inactivity time the member picks (5 minutes to 4 hours);
- after 7 days in any case.
Changing the passphrase, changing the recovery key and deleting the account at once all need the vault proof (section 3). A member who has lost both keys can still close their account without it, but only through a link emailed to them and after a 14-day wait, during which every sign-in shows the closing date and one click keeps the account; nobody at Terebyte can shorten or skip it. So a stolen sign-in session alone can’t lock a member out or delete the account.
6. Share links
- A share link carries a 128-bit secret after the “#” (
/s/<id>#<secret>), which browsers never send to a server. The file’s key and name are sealed for the link under keys derived from that secret. - Before releasing anything, the server asks for a value derived (HKDF-SHA256) from the secret, and stores only its SHA-256. Someone who only knows the link’s id can’t fetch anything or use up password attempts.
- With a password, the proof is derived from both the secret and PBKDF2 of the password. So a copy of our database can’t be used to guess the password offline. Attempts are limited.
- The recipient’s page removes the secret from the address bar and history once read. Links can expire, and owners can turn them off.
7. Signing in
- Sign-in is passwordless: an emailed link (Auth.js). Signing in gives access to the encrypted vault, never to its contents; the vault passphrase is separate and never sent to us.
- Requests for sign-in links pass an invisible bot check (Vercel BotID), are limited, and always end on the same page, whether or not an account exists.
8. Delivering the app
The app is delivered to the browser on every visit. Each page has a strict Content Security Policy: only scripts carrying that page’s random nonce run, the page can’t be framed by other sites, and plug-ins are blocked. That stops injected scripts, but not a change to the code we serve ourselves. See section 11.
9. Our own operations
- The nightly database backup is encrypted with age to a public key, before it leaves the machine that makes it. The private key is kept offline by the company’s owner. Backups are stored with object lock for 30 days.
- Each storage key we use can do only what its own job needs: the app, each download server and the backup each have their own.
- Database connections verify the database’s certificate.
- Alerts sent to us carry only statuses and totals, never member emails, file information or keys.
10. What each party can see
| Party | Can see | Can’t see |
|---|---|---|
| Terebyte (servers and database) | Email address, plan and payments, storage and download totals, size and time of each upload, the number of files and folders and how they nest, share-link dates and use counts, IP addresses briefly | File contents, file and folder names, file types, passphrase, recovery key, share-link secrets and passwords |
| Storage provider | Encrypted pieces, their sizes and times | Anything readable |
| Download servers | The downloader’s IP address and which encrypted piece, while sending it (not stored) | Anything readable |
| Someone with a share link | What was shared through that link | Everything else in the vault |
11. Known limits
- Web delivery. Members trust the code we serve on each visit. A compromised or coerced server could serve code that captures keys. Native apps, with signed, installed code, are what we’re building next.
- PBKDF2, not Argon2id. Browsers’ built-in cryptography offers PBKDF2; 600,000 iterations follows OWASP’s guidance. A weak passphrase is still weak.
- Metadata. Sizes, times and how folders nest are visible to us by design. That’s needed to store and bill.
- Record binding. Encrypted names and file keys are bound to their purpose, not yet to one particular file. Someone able to write to our database directly could swap two files’ names, or point one file at another’s contents. They still couldn’t read either.
- Shared copies. Turning a link off stops it working, but anyone who already downloaded the file keeps their copy.
- Sign-in by email. Whoever controls a member’s inbox can sign in to the account. They still can’t open the vault without the passphrase or recovery key.
12. Audit status
No independent firm has reviewed Terebyte’s code or cryptography yet, and none is booked. We’ll say who and when on Security once one is, and publish the full report when it’s done. Reports of problems are welcome at security@terebyte.com (disclosure policy).