Updated 16 October 2026
How HushOS protects your files.
What happens to a file from the moment you add it, in plain words. Each step keeps the exact terms underneath for anyone who wants to check them against the code. Where this page and the code disagree, the code is right and this page has a bug.
On this page
1Your password never leaves your device
- Never reaches us
- Your password, and this device’s own key
- What reaches us
- A proof that you know it, a record that can check that proof, and a name for this device
When you sign in, your device proves it knows your password without sending it, not even in a scrambled form. For your password, we keep only a record that can check that proof. Guessing your password from that record means trying passwords one at a time, and each try is slowed down on purpose. Signing in with an email that has no account gets the same kind of answer, so the sign-in page doesn’t reveal who has one.
Once you’re in, your browser holds a session that ends after 7 days unused, or after 30 days at most.
Each session belongs to a device. Your devices, in Account, lists every browser and phone signed in to your account. Removing one asks for your password and signs it out at once. The next time that device checks in, it learns it was removed and clears what it kept for your account; we can’t promise that, because a device kept offline never checks in. When a device we haven’t seen before signs in with your password, we email you.
The exact termsHide the exact terms
- Protocol
- OPAQUE (Ristretto), Serenity implementation
- Hardening
- Argon2id, 65,536 KiB memory, 3 iterations, parallelism 4
- Server stores
- The OPAQUE registration record
- Client receives
- An export key that only the client ever sees
- Unknown email
- Answered with a decoy record so lookups do not reveal accounts
OPAQUE produces two secrets. The session key is shared with the server; HushOS uses a separate random token for its application sessions. The export key is known only to the client. Only the export key is used to wrap the account key, and only after a further derivation step.
Sessions and transport
- Verification links
- 32 random bytes, sent in a URL fragment, stored only as SHA-256, valid 30 minutes
- Login attempts
- Random handles, stored hashed, consumed after 5 minutes or one finish
- Sessions
- 7 days idle, 30 days at most, 32-byte token, only its SHA-256 stored
- Devices
- Each session belongs to one device. A device keeps its own Ed25519 key and signs back in to its own entry with it; anything else signs in as a new device
- Removing a device
- Needs your password, through a one-use OPAQUE exchange bound to your session; ends its sessions at once. For a week, the device is told it was removed when it checks in
- New-device email
- Sent when a new device signs in with your password; it never names the device
- Cookies
- HttpOnly, SameSite=Lax, Secure with the __Host- prefix under HTTPS
- Mutations
- Exact Origin check, JSON only, 16 KiB body limit, per-address rate limits
- Responses
- private, no-store and no-referrer
2Your keys are made on your device
- Never reaches us
- Your account key, unlocked
- What reaches us
- Copies of it, locked by your password and by your recovery phrase
When you sign up, your device makes the key for your account. Your password locks one copy of it and your recovery phrase locks another. Changing your password locks the same key again, so nothing else has to change.
Your device also makes your identity keys, which are what other people lock things to when they share with you, and a key for your workspace. Every folder and file hangs off the workspace key, each with a key of its own.
The exact termsHide the exact terms
Your account key, also called the master key, is 32 random bytes generated in your browser. It is not derived from your password. Two independent wrappings of the same key exist:
- Password wrap
- HKDF-SHA-256 over the OPAQUE export key, then XChaCha20-Poly1305
- Recovery wrap
- HKDF-SHA-256 over the recovery secret, then XChaCha20-Poly1305
- Salt and nonce
- Fresh 32-byte salt and 24-byte nonce on every wrap
- Bound to
- Your account ID, root revision, and credential or recovery revision
Every envelope carries associated data that names its purpose, version, and owner. An envelope moved to another account or another version fails to open.
At signup the browser also generates your identity: an X25519 key pair for encryption, an ML-KEM-768 key pair (the post-quantum key exchange NIST standardised as FIPS 203), and two signing key pairs, Ed25519 and ML-DSA-65 (the post-quantum signature standardised as FIPS 204). The private halves are wrapped with keys derived from the account key. Your identity signs a statement over all four public keys and your account ID with both signing keys; the server refuses an identity whose statement does not verify, and every device checks it again. Only public keys and wrapped private material reach the server. The X25519 and ML-KEM keys together are what a share is sealed to, and what a report’s key is sealed to.
Identity keys
- Encryption
- X25519, libsodium
- Post-quantum
- ML-KEM-768 (FIPS 203), expanded from a 64-byte seed wrapped under the account key
- Signatures
- Ed25519 and ML-DSA-65 (FIPS 204), each from a 32-byte seed wrapped under the account key. A signature is both, 3373 bytes, and holds only if both halves verify
- Keys statement
- All four public keys, the key version and your account ID, signed with both keys; checked by the server on every upload and by every device that reads it
- Version
- 2. An identity made before ML-DSA keeps its keys and gains one at its next unlock
The twelve words two people compare are version 3: a digest of the X25519 and Ed25519 keys and of the ML-DSA and ML-KEM keys, so comparing words checks all four.
Every workspace has its own 32-byte key, generated in your browser. It is random, not derived from your account key, so it can be granted to another person later without giving them anything of yours. What you hold is a grant: the workspace key wrapped under a key derived from your account key.
Workspace key
- Workspace key
- 32 random bytes, created on your device when the workspace is
- Grant wrap
- HKDF-SHA-256 over the account key, then XChaCha20-Poly1305
- Bound to
- Your account ID, the workspace ID, your root revision, and the workspace key revision
- Server stores
- One grant per member per workspace, never the key
Folder and file keys hang off the workspace key, not off your account key. That is what keeps rotation cheap: replacing your account key rewraps one grant per workspace you belong to, and nothing beneath it moves.
In your browser
- Account key
- Held in plaintext only inside a Web Worker, never in application state
- Remembered unlock
- The worker wraps the account key with a non-exportable AES-256-GCM device key held in IndexedDB; locking or signing out clears it across tabs in that browser
- Device key
- A non-exportable Ed25519 key in its own IndexedDB database, which signs this browser back in to its own entry in Your devices. Kept when you lock, with your account ID beside it in the clear, since it is read before you unlock; deleted when you sign out or the browser is removed
- Tree mirror
- Your tree as the server holds it, ciphertext only, kept in IndexedDB so search and folder listings run on the device; cleared when you leave the workspace. Its IDs, sizes and times are as readable as they are to the server
- Rollback marks
- The highest settings and identity versions this browser has seen, so an older copy from the server is refused. Sealed with XChaCha20-Poly1305 under a key derived from your settings key, which a new account key keeps, so a locked browser holds ciphertext; a mark that does not open stops the check rather than count as none
- Signing out
- Deletes everything this browser kept for the account, databases included rather than emptied: the remembered unlock, the device key, the rollback marks, the tree mirror, the upload journal and stash, family records, a saved return to a link, and the email address a sign-up link went to. Only display settings stay: theme, contrast, sounds, list or grid, and sort order
How remembered unlock and the account key rotations work, step by step, is in the accounts and keys design document.
3Your recovery phrase gets you back in
- Never reaches us
- Your 24 words, readable
- What reaches us
- Your account key locked by the words, the words locked by your account key, and a public key that checks them
The 24 words you save at sign-up lock their own copy of your account key. With the phrase and a link we email you, you can set a new password and your files stay as they were. Email alone can never get you back in. Resetting your password this way gives you a new phrase and signs you out everywhere else.
In Account you can also change your password, make a new recovery phrase, or rotate your account key. Each one asks for your current password and signs you out of your other browsers. You can also replace your sharing keys, which the people you share with follow without comparing words again (see sharing below).
The exact termsHide the exact terms
- Phrase
- A 32-byte secret made on your device, shown as 24 English BIP39 words
- Backup
- The secret is also stored encrypted by the account key, so the phrase can be shown again while your device is unlocked
- Reset needs
- A verified email link and a signature from a key derived from the phrase
- After a reset
- The unchanged account key is rewrapped, every session is revoked, a new phrase is issued, and you are signed in with the new password
During a reset the browser re-registers with OPAQUE under the new password and rewraps the unchanged account key. The server checks the signature and every binding and replaces the credentials in one transaction.
In Account
- Change password
- Rewraps the same master key; preserves the recovery phrase and identity keys
- Make a new phrase
- Creates new 24-word recovery credentials; preserves the password, master key, and identity keys
- Rotate account key
- Generates a new master key and recovery phrase; rewraps the identity keys (current and earlier), the settings key and every workspace grant, and preserves the password
- Replace sharing keys
- Generates new identity keys at the next key version, vouched for by a signature from the current signing key; keeps the earlier ones, still wrapped under the master key, to open what was sealed to them; seals every share again under the new keys
Each action needs your current password through a fresh OPAQUE exchange, revokes existing sessions and pending recovery attempts, and rejects stale concurrent changes. After either rotation, save and confirm the new recovery phrase before continuing into the app. The full sequence is in the accounts and keys design document.
4Your files are locked before they’re sent
- Never reaches us
- Names, contents and thumbnails, readable
- What reaches us
- Locked, padded pieces, sent straight to storage
Your device locks each file, and its name, type and modified time, before any of it leaves. Each file is padded before it is locked, so from a locked copy our servers learn only roughly how large the file is, to within a few percent. The locked pieces go straight from your device to storage; our app server never handles them. Thumbnails are made on your device and locked inside the file itself. Downloads come back the same way and are unlocked on your device, and previews fetch only the parts they need.
- Padded to
- 5.00 MB
- Looks the same to us as
- Any file from 4.88 MB to 5.00 MB
The exact termsHide the exact terms
Every folder and file in Drive has its own key, wrapped under its parent’s key; the root folder’s key is wrapped under the workspace key. A file’s key locks its details and wraps a separate content key for each version of the file. The server holds a tree of ids and envelopes.
Folder and file keys
- Node key
- 32 random bytes per folder or file, wrapped under the parent node key
- Metadata
- Name, type, size and modified time, encrypted under the node key and padded to 256-byte steps, so its length does not follow the name
- Content key
- 32 random bytes per file version, wrapped under the node key
- Wrap
- XChaCha20-Poly1305 with a fresh 24-byte nonce
- Bound to
- The workspace, the node, its parent and the parent key epoch, or the version and object
An envelope moved to another folder, another version or another workspace fails to open. A key epoch counts each time a node’s key changes; it is what lets a rotation tell old envelopes from new ones.
Uploads and downloads
- Cipher
- XChaCha20-Poly1305, one 16-byte tag per 8 MiB chunk
- Chunk nonces
- Derived from a 16-byte per-object nonce and the chunk index
- Transfer
- Browser to object store directly, by signed URLs valid 15 minutes for shared content, an hour otherwise
- Resume
- A journal on your device records each chunk’s digest before it is sent. Until the upload finishes it also holds the file’s name and size in the clear, even while locked; signing out deletes it
- Thumbnails
- Rendered on the uploader’s device and sealed into the file’s own encrypted object, under a key derived from the content key; the server cannot tell which files have one
- Size
- The server records the size the store confirms for the encrypted object, which is padded (below); the file’s exact size is sealed in its envelope
File contents are padded before they are encrypted. Below 256 bytes, everything is 256 bytes. Above that, at most 6.25% is added, so the server learns a file’s size only to within roughly 2 KiB around 50 KB, 32 KiB around 1 MB, 2 MiB around 100 MB and 32 MiB around 1 GB.
Every name and every version is also signed by the account that wrote it. Your device checks each signature against your own keys and your contacts’ pinned keys. What a contact wrote shows as theirs; if you never compared words with them, Info says Not verified beside their name, with no mark on the item. Anything your device can’t tie to you or a contact is marked Unverified. It names the writer only from your own contacts: you, a contact, or someone it can’t tell; the server tells it nothing more than an account id. Keys someone has replaced no longer vouch for what they signed. If your device can’t run the check at all, because your contacts or your own earlier keys didn’t load, every item shows Couldn’t check until it can, never no mark. A key that only opens a folder, such as a link’s, can no longer plant a file or rename one unnoticed. Unverified items still open: it is a warning, not a lock.
Padding and signatures
- Padding
- The file and its thumbnail are padded together to a Padmé bucket: 256 bytes at least, then at most 6.25% more up to 64 KiB, 3.125% up to 4 GiB, 1.5625% beyond. Padding is under the content key and counts towards storage
- Fetching
- Downloads and whole-file previews fetch the padding too, and so does any read that reaches a file’s last chunk, such as seeking to the end of a video, so the store does not see where the file ends
- Viewers
- After you unlock, the app fetches every viewer, syntax grammar and thumbnail renderer once, so which ones it fetched does not say what you opened
- Signatures
- Ed25519 by the writer’s identity key over the workspace, item, kind, metadata version and SHA-256 of the metadata envelope; and over the item, version, object, content suite and SHA-256 of the content-key envelope
- Re-sealed
- A rotation, a copy or a rename re-signs what it locks again. Only a record that verified is signed as the writer’s own; anything else gets a re-sealed statement that says it was re-locked, not vouched for, and stays Unverified
- Checked
- By the server against the writer’s current key before it stores a change, and again on your device against keys you trust; a check that cannot run shows as Couldn’t check
- Writer
- Stored and served as an account id alone; your device names it from your contacts. Link visitors get no signatures and no writer
- Not signed
- Moves, trash and restore, which change no envelope; tags
Independent chunks are what make ranged previews and resumed uploads plain arithmetic. A chunk is never re-encrypted under the same key and nonce with different bytes: a resumed upload proves the file is the same one, chunk digest by chunk digest, or starts over with fresh material.
6Links keep their secret out of our sight
- Never reaches us
- The part after the #. It goes only where you send the link
- What reaches us
- The rest of the address
The secret part of a link sits after the # in the address, which browsers never send to a server. If you add a password, it is needed too, and we can’t tell whether there is one. Links can end on a date up to a year away, and you can turn one off at any time. Anyone with the link opens it in their own browser, without an account.
A made-up link. Point at or pick a part.
The exact termsHide the exact terms
A link is a share without a recipient. The node key is sealed under a key derived from the link’s secret and, if you set one, a password stretched with Argon2id on the visitor’s device. The server holds the sealed box and a hash of the link’s path; it can open nothing.
- Secret
- 32 random bytes in the URL fragment, never sent to the server
- Password
- Argon2id (64 MiB, 3 iterations) with a per-link salt, mixed into the key
- Expiry
- Optional, at most a year; revocation is immediate
- Visitor
- Browses, previews and downloads in their own browser, with no account
- Requests
- The link’s API requests carry its token in a header, not the address, and no session. They return no signatures and say nothing of who wrote what. The page itself is an ordinary page load: if you are signed in, it carries your session cookie, though the page does not ask who you are until you sign in or keep a copy
- Keeping a copy
- A signed-in visitor re-encrypts each file under their own keys; the copy shows as re-locked by them, since nothing vouched for who wrote it
7Stopping a share really stops it
- Never reaches us
- The item’s new keys, unlocked
- What reaches us
- The new keys, locked again for everyone who still has access
When you stop a share or turn off a link, that person is refused on their next request. Then your device changes the keys for that folder and everything in it, and locks them again for everyone who still has access. Nothing the removed person kept opens anything new.
The exact termsHide the exact terms
- Refused
- On the next request, on every route
- Keys
- The shared item and everything beneath it get fresh keys under a new epoch; the old keys are gone
- Re-sealed
- Every remaining share and link, under the new keys, to the pinned keys of each remaining person
- Content
- Not re-encrypted; the guarantee is about keys and authorization
- Download links
- Already issued ones expire within 15 minutes
How a rotation visits every item, survives moves made while it runs, and keeps older envelopes apart is in the Drive design document.
8Anything shared can be reported
- Never reaches us
- What is in anything nobody reported
- What reaches us
- A reported item’s key, locked to the people who run the service; a folder’s key opens what is in it
Anyone who can see something through a share or a link can report it. Reporting is the only way the people who run the service can see a file: the reporter’s device locks that one item’s key to them. They can then dismiss the report, remove the content, hold it, suspend the uploader, or pass it to the relevant authority. Nothing you haven’t shared can be reported, because nobody else can see it.
The exact termsHide the exact terms
- Who can report
- Anyone with access through a share or a link
- What is sealed
- The reported item’s key, sealed on the reporter’s device to each operator’s X25519 and ML-KEM-768 keys together when the operator has an ML-KEM key; otherwise, to their X25519 key alone, until they next sign in. The operators and their keys come from the server
- What is kept
- A snapshot of the reported tree and a hold on its stored bytes, past the owner’s deletion
- Evidence
- Optionally copied to a separate write-once bucket the application cannot delete from
- Not kept
- Uploader network addresses; the service does not log them
An operator opens a report under their own identity. Which operators a report is sealed to, and their keys, come from the server, so whoever runs the server can open what is reported to it.
What the server can see
- Name
- Locked, unreadable to us
- Folder
- Item 0192f3a1-… inside 0192f2c7-…
- Size
- About 1.22 MB, padded
- Contents
- Locked pieces in storage
- Changed
- Locked; we see a save on 14 Sept, 09:14, by your account
- Never
- File names, contents, thumbnails, folder names, or any key
- Always
- Your email, the shape of your tree (ids and parents), the size of each encrypted object (padded, so a file’s size only to within a few percent), times, how much you store, and which account wrote each change
- Sharing
- Which accounts you share with, and that a link exists; not what is behind it
- Search
- Nothing. Search runs on your device over names it has already decrypted; no query is sent anywhere.
- Tags
- That a sealed tag list exists, its size in coarse steps, and when it was last saved; not a name, which items carry a tag, or how many. Tags are never written on an item, so someone you share with sees none of them.
- Reports
- The content of an item, once someone with access reports it
- Devices
- The name each of your devices gave itself or you gave it (names are not encrypted), its kind, how it signed in, and when it was added and last active, to the hour
- Later, with a quantum computer
- Shares and reports are sealed with X25519 and ML-KEM-768 together, so opening one still needs the ML-KEM private key even if X25519 is broken, and the signatures that decide whose keys to trust (identities, their replacements, shares) need ML-DSA-65 too. Reports filed before reports became hybrid, and reports to an operator who has no ML-KEM key yet, are sealed under X25519 alone, and file and folder signatures are still Ed25519 alone.
Families, on servers that offer them: HushOS’s server knows the family’s name, who is in it and what each person can do, who invited and let in whom, the email addresses invitations went to, and how many family folders there are with their sizes, padded as every file’s is. It can’t open family folders.
Joining a family takes no words. When a manager lets someone in, the manager’s device writes the keys HushOS gives it for them into the family’s member list, and the newcomer’s device takes the family as HushOS first shows it, then holds on to it: from then on, a different member list, an older one, or another family key is noticed and the family is paused there, never used. Until two members check each other, whoever runs the server could hand a manager keys of its own for a newcomer, or show a newcomer a family it made up, and read what goes into those family folders. Checking takes twelve words, any time, from Check it’s them beside their name on the family page: when the words match, both devices see the same family and each has the other’s real keys; when they don’t, nothing is saved. A check is best made with the person who made the family. Nobody is given the role of managing the family, or of organizer, until whoever gives it has checked it’s them, since managers can let people in and change the family.
Where it runs
HushOS is open source, and anyone can run their own copy. Who runs the copy you use decides where it runs.
- hushos.com
- Run by HushOS, a company incorporated in Delaware, United States, with its servers and storage in the European Union
- A self-hosted copy
- Run by whoever set it up, on their own servers, under their own terms
Either way, your browser sends locked files straight to the object store, and the app server never handles file bytes. Every page is served with a content security policy that limits which scripts run and where the page can connect, and with the usual protective headers.
The exact termsHide the exact terms
Every page carries a Content Security Policy: scripts run only from the app’s own origin, under a per-request nonce, with WebAssembly allowed and eval not. The page may connect only to itself and to the object store. Every response carries X-Content-Type-Options, X-Frame-Options: DENY, a referrer policy, a cross-origin opener policy, a permissions policy that switches off camera, microphone, location and payment, and, over HTTPS, HSTS for a year.
What this can’t do
- If you lose your password, your recovery phrase and every browser that remembers you, your files are gone. Nobody can get them back, including us.
- Someone who downloaded a file while they had access keeps that copy. Stopping a share protects what comes next; it can’t reach into someone else’s device.
- Signing out other browsers, or removing a device, takes effect when they next check in with the server. It can’t erase keys or files a device already holds offline.
- Replacing your recovery phrase stops the old one from resetting your password. A recovery kit you downloaded earlier still holds the old phrase and your account key as it was then, so it can still open that key offline. Password resets and a new recovery phrase keep the same account key; only replacing the account key makes a different one.
- Replacing your account key can’t erase old locked copies, or take back private identity keys someone already pulled out of an older key bundle.
- The server sees what’s listed above: your email, padded sizes, times, who wrote each change and how your folders nest. Padding hides a file’s exact size, not roughly how big it is.
- For people you never compared words with, whoever runs the server could put its own keys in front of theirs the first time your device sees them, and then read what you share with them. Comparing twelve words, at any time, rules that out from then on.
- In a family nobody checked, whoever runs the server could plant its own keys for a newcomer, or show a newcomer a family it made up, and read what goes into those family folders. Two members checking each other rules that out from then on, not for what was added before.
- Items written before HushOS signed changes, and anything re-locked without a signature your device could verify, show as Unverified until someone writes them again.
- Whoever runs the server chooses which operator keys a report is sealed to, so they can open anything reported to them.
- Replacing sharing keys with stolen keys would look like a real replacement to the people you share with, and their devices follow it. Comparing twelve words again, from Check it’s them, shows the new keys’ words.
- Signatures say who wrote an item, not that it is the newest version: the server could still serve an older, signed state of it. A report to an operator without a post-quantum key, and any report filed before reports became hybrid, is sealed under X25519 alone.
Check it yourself
The byte-level protocols live in the @hushos/crypto package and are specified in two design documents beside it: one for accounts and keys, including key rotation and remembered browser unlock, and one for Drive, including folder keys, uploads, sharing and rotation. The rest of the suite will be built on the same keys and rules, and documented here in the same detail when it ships.