How it was made
The grown-up page. Each claim names its limits and a way to check it.
The short version
Your browser encrypts each file with AES-256-GCM before upload. We store ciphertext and never receive the key. Everything after # is the key. Browsers don't send it to any server, including ours.
We delete the ciphertext from our disk within a minute of its exit. If the exit was a download, we wait for it to finish (at most ). We keep a note that the link existed, so we can tell you why it's gone. We never had the key, so any leftover bytes are noise.
Every exit goes on the public record: we add it to SealLog on the Ootle at the next checkpoint (within ). It proves we said we deleted it, publicly and permanently. It can't prove we kept no copy.
Your key is the account
No email, no password. Your key is the account. Keep it like a house key: lose it and nobody, including us, can let you back in.
We store a random id and a hash of your login key. No email and no public key; a drop link stores its own address, by design.
Make my drive makes the key in this browser at once. Save it as a key file, locked with a passphrase if you like, and Open my drive takes it back on any device. Your drive's Tari keys come from the same key, the first time a Tari feature needs them.
The locks
- Files: AES-256-GCM in your browser, in chunks; size padded so the exact size stays hidden.
- Names: file and folder names are locked too. Search runs in this browser. Our server can't read your file names.
- The key: in the link after #, or sealed to a Tari address inside a stealth payment's encrypted memo.
- Secret words: stretched in your browser with PBKDF2, rounds; we slow down guessing after wrong tries.
- Photos: location and camera data removed before locking, unless you keep the original.
The cryptography, in full
Everything that touches a file or a key runs in your browser, with your browser's own WebCrypto. The server never receives a file key, a link's secret, a password or your seed. At sign-in it sees your login key and keeps only its SHA-256.
- Files: a fresh random 32-byte key per file. The content is padded with Padmé (at least 1 KB, at most 12% over above that), cut into 4 MB chunks and encrypted with AES-256-GCM. Each chunk's nonce holds its index and a “last chunk” flag, and every chunk authenticates the file's header, so reordering, truncation and extension all fail.
- Metadata: the name, type and real size are encrypted separately, also with AES-256-GCM, padded to a multiple of 256 bytes.
- Keys from keys: HKDF-SHA256 with a separate label for every purpose, so one derived key reveals nothing about another.
- Links: every link has its own 32-byte secret, after the
#. It unwraps the file key and derives an access token; our server stores only the SHA-256 of that token. - Passwords: PBKDF2-SHA256, rounds, combined with the link's secret by default (link + password), so a copy of our database isn't enough to guess it.
- Your drive: one 32-byte seed made in your browser, kept in its storage, and backed up as your key file (locked with a passphrase if you choose) or written down. Everything else is derived from it: the login key (we store its SHA-256), the drive's Tari keys (made only when a Tari feature first needs them), a separate wallet key, and the dead-drop tags. There is no email and no password with us.
- Folders: every folder has its own random key. A file's key is wrapped under its folder's key, and a folder's under its parent's, up to your drive's root key. A folder link carries one folder key, so it opens what is below that folder and nothing above it.
- Safety code: 12 emoji (96 bits) derived from a Tari address's public keys, so two people can compare an address out loud.
- Proof of work: SHA-256 with a number of leading zero bits, instead of a captcha. Your browser solves it in a background worker.
- Photos: location and camera data are removed from JPEG, PNG and WebP files before encryption, unless you choose to keep the original.
Sealing a file to a Tari address
When you send a file to a Tari address, your browser seals the link's secret to that address's two public keys, the owner key and the view key, using the official Tari library for the key agreement. The sealed blob is 125 bytes, and it travels as the memo of a stealth payment.
- Why a view key can't open your files: the seal needs both the owner key and the view key to open. A view key alone shows that a delivery arrived, its amount and its memo text, but not the file.
- The exception, labelled: a sender can choose “The recipient uses another Ootle wallet”. That seal uses the view key only, so anyone with that view key can open the file, and the send screen says so.
- Who posts the payment: our relay. We post the payment for you, so we learn the address you send to, never the key. The server writes the memo from its own address; a client supplies only the sealed blob.
- Who posts the payment: your browser, once its wallet holds the testnet grant our sponsor wallet sends when a drive opens: it builds, signs and pays for it, and our server passes it to the network without learning who it's for. Until the grant arrives (a minute or two), our relay posts the send and learns the address while it's sent. A browser send spends that grant, so the chain links your sends to the sponsor wallet and to each other. Dead-drop deliveries still go through our relay, because a drop's address is public by design.
Who could attack a link, and what stops them
| Adversary | Can | What stops them | What remains |
|---|---|---|---|
| Network observer | see TLS metadata | HTTPS only, HSTS, Padmé size buckets | timing and volume |
| A curious or compromised server (including our host) | read the database and the stored files, change responses, watch IP addresses live | everything is encrypted in the browser; tokens and verifiers are hashed; no public keys or drive addresses stored for logins; folder and file names encrypted; no sharing graph; each chunk is authenticated together with its header | can serve malicious JavaScript (see verify); can refuse service; sees the metadata listed on How it works; knows each relay send's destination; can ignore a deletion and keep ciphertext, which is useless without the key |
| Someone with only the link's path | ask for its status | the key lives only after the #; opening needs an access token derived from it; link + password adds a link proof | learns that a link exists, and its state |
| Password guesser, online | try passwords | the access token comes from the password; growing pauses; proof of work; only holders of the full link can count as wrong passwords | slow guessing against weak password-only links |
| Password guesser, with a copy of our database | guess password-only links offline | PBKDF2-SHA256 with rounds; link + password is the default, and password-only links come with a warning | weak password-only secrets |
| Chain observer | read all public state | stealth outputs, encrypted memos, one fee and one amount for every relay send, decoy sends | the fee and the transaction graph are public; relay sends are recognisable as ours; sends from a browser spend a grant from our sponsor wallet and then their own change, so they can be linked to each other and to us |
| A view-key holder | read arrivals, amounts and memo text | files locked to a drive need its owner key too | files sent with “The recipient uses another Ootle wallet” open with the view key (labelled at send time) |
| Phishing through our relay | make our wallet send a link | the server writes every memo our relay sends, from its own address; a client supplies only a 125-byte sealed blob; sends need a drive; drop deliveries only go to the drop's own address | someone can send unwanted hidebox deliveries to an address: daily caps, closable drops, a separate inbox section, no automatic preview, a warning before programs. Sends from browsers carry a memo the browser wrote, so a modified client can put any text on chain to any address, within one sponsor grant a day per drive, and two per network; inboxes show a memo that isn't exactly a hidebox link as plain text, never as a link |
| Griefer | fill storage, push proof of work up, drain daily budgets, lock links | reserved lanes for small and anonymous files, limits per network prefix, a first-day limit for new drives, public and reserved budget slices with alerts | someone with many networks can use up the public slices for a day; the reserved slices keep the guided demo working |
| Someone hosting abuse | upload and share | small and short-lived anonymous files, one open by default, proof of work, rate limits, downloads only, a warning before programs, no listing or search, noindex, the report form, removal by id | we can't scan encrypted content, by design |
| Script injection or a poisoned dependency | run code in our origin | no third-party code; a strict content security policy with Trusted Types where supported; no HTML sinks in our code; the one vendored artifact is pinned by hash; SRI on each page's top-level scripts and stylesheets, and the hash manifest for the modules they import | browser extensions (out of scope) |
| Draining our wallet through the API | spend the sponsor wallet | the internet-facing API runs as a separate user that can't reach the wallet; jobs are validated; daily budget slices; a firewall rule per user on the wallet port | at most the daily budgets |
Folders and their keys
Folders are a tree of keys. Each file's key is locked under its folder's key, so one folder link opens the files below it and nothing above.
Our server sees which files share a folder, how deep it goes and how many files are in it. Never a name.
A folder link counts visits, not files. A Once folder link is one visit of ; then the link ends.
Moving a file into a folder that has a link shares it too, and the drive says so before it moves.
Pay to open
Pay to open is built and switched off on this deployment. Until it is on, this is how it will work.
The money goes straight to the owner's Tari address; our server keeps half of the key and hands it to the buyer once Tari's chain shows the payment, so the owner can be offline and we never hold the money or the whole key.
- We keep half of the key and give it to you once the chain shows your payment. The other half is in the link, which we never see. Neither half opens it alone.
- If the link leaks, our server could open it too.
- Our half of a paid link's key stays in our database backups for after the link ends.
- We check the amount and that the payment is on the chain. Only the owner's drive can check the money is theirs to spend.
- Test money comes from our demo wallet, so it can be grouped with our other payments. It opens at once, because our wallet built it. Paid from your own wallet? It opens after the owner's drive checks it.
- Payments can't be taken back, by us or by Tari. Pay only if what you see here is enough.
- To see who paid you, we keep your sales view key. It is sealed, so a copy of our database can't read it. Our payment checker on our server can. A view key can see payments, never spend them.
Next: the locked payment, a Tari payment with a refund built in, so a buyer gets the money back if the owner never delivers. It isn't built yet.
Send by Tari
Send by Tari: the key rides inside a payment to their address. Only they can open it.
In relay mode we post the payment for you, so we learn the address you send to, never the key.
In relay mode we post the payment for you, so we learn the address you send to, never the key. In direct mode your browser sends it and we learn nothing.
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Our courier payments all come from one wallet with the same fee, so someone watching could group them as “sent by hidebox”, but not see to whom.
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Sends from your browser spend a grant from our sponsor wallet, then each send's change, so anyone reading the Ootle can link them to each other and to our sponsor wallet, but not see to whom or how much.
How the seal works, and why a view key alone can't open it: sealing a file to a Tari address.
Who sees what
Our server
We see a padded size (rounded up so the exact size stays hidden: at least 1 KB, and at most 12% over for anything bigger), the hour it arrived, its exit rule and how often it's opened. Not the name, the type, the contents or the key.
We count opens per hour. We never store who opened it or their IP address.
Our server sees which files share a folder, how deep it goes and how many files are in it. Never a name.
Our server sees your IP address while you're connected. We don't write it to disk: rate limits use a keyed hash, kept in memory, with a key that changes every day.
The Tari™ chain
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Our courier payments all come from one wallet with the same fee, so someone watching could group them as “sent by hidebox”, but not see to whom.
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Sends from your browser spend a grant from our sponsor wallet, then each send's change, so anyone reading the Ootle can link them to each other and to our sponsor wallet, but not see to whom or how much.
In relay mode we post the payment for you, so we learn the address you send to, never the key.
Whoever has the link
The file, as many times as its rule says (once, by default). The link is the key: anyone who gets the full link can open it until it's gone.
A view key
A view key can't spend, but it can read everything sent to this address, including files. Give it only to someone allowed to see those.
For files sent to a drive, the view-key screen lists arrivals and opens no file. Files sent to another wallet open with its view key.
For space you rent from us we see the amount, because we're the seller. Nobody else does.
To see who paid you, we keep your sales view key. It is sealed, so a copy of our database can't read it. Our payment checker on our server can. A view key can see payments, never spend them.
Every party, fact by fact
| Our server | Anyone watching the Ootle | Someone with the full link | The person you send it to | |
|---|---|---|---|---|
| File contents | no (ciphertext) | no | yes | yes |
| Name and type | no (encrypted inside) | no | yes | yes |
| Exact size | no (a padded bucket) | no | yes | yes |
| The key | no | no | yes (the link is the key) | yes |
| That a file exists, its arrival hour, its rule, its opens | yes; opens counted per hour, never who | counts only, in steps of 64: records (stored files plus exits) per checkpointno | partly (state, opens left) | does not apply |
| Which files share a folder | yes: the shape of the tree and the counts, never a name | no | a shared folder's files, by name | does not apply |
| Your IP address | while you're connected; not written to disk | no (Tari's indexer sees it only if you press “Check it on Tari's indexer”) | no | no |
| Who you send to | yes for every send and dead-drop delivery, while it's sent; then the job is scrubbedsends from your browser: no; sends our relay posts (before your browser's wallet has its grant) and dead-drop deliveries: yes, while they're sent | no (a one-time stealth address) | does not apply | they know it's them |
| That we sent a payment | yes | yes for relay sends: one wallet, one fee; sends from your browser trace back to our sponsor wallet's grant, and to each other through their change | does not apply | does not apply |
| Payment amounts | for space rented from us, because we're the seller | no (a Pedersen commitment) | does not apply | does not apply |
| The network fee | does not apply | public | does not apply | does not apply |
| SealLog (hashes, counts, times) | yes | public by design, written by the same key that holds our public tTARI | does not apply | does not apply |
Our server, in detail
- Sees and stores
- Ciphertext and its padded size; the hour an item arrived and when it left; expiry and view counts, and the time of a link's first open (shown only to its owner); the exit rule (link or password); for drives: a random-looking drive id, a hash of its login key, the first and last active day, how many distinct days it was active, quota and packs bought; for folders: which items share a folder and how many levels down it sits, never a name; per link, how many times it opened in each hour; whether a file is in the trash, and when it is set to leave; invoices (pack, amount, status) and the chain outputs that paid them; drop links: the drop's Tari address, its label, its tag and daily counters; abuse reports; notes about ended links (state and day).
- Sees, never stores
- Every uploader's and every opener's IP address, live (so at the moment it happens we could link who uploaded a link with who opened it); the login key at sign-in; access tokens (hashed on arrival); which drop a file was left in, and the submitter's IP; the destination Tari address of every send, the sealed 125-byte blob, which drive sent it, and by timing which link it carries, kept in the job until sent and then scrubbed; that your drive submitted a transaction, not to whom; for dead-drop deliveries, the drop's address while the delivery is sent.
- Never sees
- File names, types, contents and real sizes; file keys, link secrets, passwords, seeds or any private key; your drive's Tari address, unless you make a drop or a relay send goes to it; who you send a file to, when your browser's wallet sends it (until its grant arrives, our relay sends it and learns the address while it's sent); which chain outputs your browser can open (everyone downloads the same copy of the chain); who shares with whom (a link is the only way a file leaves your drive, and we never learn who holds it).
Everyone else
- A network observer
- That you talk to us, timing, and padded transfer sizes. Nothing inside TLS.
- Someone with only the link's path (no
#) - That a link exists, whether it needs a password, and its state; once it has ended, why and on what day, and its exit receipt: the file's id and exit hour, and through the log which checkpoint windows its arrival and exit fell in. Not the key, the contents, the name, the real size or the open count.
- A view-key holder
- Every delivery and payment to that address: amount, time and the memo's link text. Not the files locked to a drive, and it can't spend. The one exception: files a sender sealed with “The recipient uses another Ootle wallet” do open with the view key, and the send screen says so.
- The public, through our live numbers
- The free quota; our disk's size and free space, rounded; whether uploads or the Tari features are paused; how many drives were active recently, once there are enough of them; SealLog's latest checkpoint; how many minutes ago someone last locked a file; and for up to 24 recent files, each at least an hour old, a coarse size and an exit rule, never whether one was opened, only when at least 8 exist in the last week. Nothing per person, but someone who knows a file's size and rule may recognise it there.
- Our host (Hostinger)
- Everything our server stores, plus the disk and any snapshots it keeps; in principle, live memory.
Space, and when files go
Free
Today: per drive · of free
How we decide: every hour we take the disk, set aside what the system needs, what people have rented and a share for uploads without a drive, and divide the rest among drives used in the last . We overbook like an airline (×): most people use a fraction of their share. The number moves slowly, never below or above .
If the disk ever truly fills, new uploads pause for everyone, and nothing already stored is lost.
When the number goes down, nothing of yours is deleted. You can't add more until you're under it again.
Files shared without a drive have their own reserved space, and small files (up to ) have a reserved lane of their own, so a busy day of big uploads can't crowd out a quick note.
If you don't open your drive for days, we delete what's in it.
Files in the trash still use space. We empty it after days. Links add no space.
Any file can be set to leave on its own: Delete it after takes a day, a week or up to . When it leaves, it gets a receipt like any other exit.
When extra space ends, nothing goes for days. Then files in the trash go first, then the oldest, until you fit.
Limits
| No drive | Free drive | More space | |
|---|---|---|---|
| Largest file | |||
| Opens per link | 1 to | 1 to | 1 to |
| Longest life of a link | |||
| Files in your drive | no | until you delete them | until you delete them |
| Text secrets | characters | characters | characters |
| Sends to a Tari address | a free drive opens for you | a day | a day |
A drive's first day is smaller, so a burst of new drives can't take the disk (and new uploads pause before it fills): files up to , in total, links up to , sends to a Tari address. From its second active day, the free share applies.
Files left in a drop link can be up to and open up to times within .
More space, paid in tTARI
| Pack | Price (testnet) | |
|---|---|---|
| + for | tTARI | Pay in tTARI, or try it with demo tTARI from the testnet grant your browser's wallet gets. |
| + for | tTARI | The same, for people who keep more. |
| Sold out | none | The disk is finite. We'd rather say no than overbook paid space. Free space still works. |
How paying works
We credit a payment only after the official Tari library opens its commitment to the exact amount and a Tari-run public indexer shows it committed on the chain.We credit a payment only after our wallet sees it and a Tari-run public indexer confirms it exists on the chain. The amount is the one our wallet reads. You pay our receiving address with this invoice's reference inside it; the reference travels in the payment's encrypted memo, so the chain doesn't show which invoice was paid, or by whom. The network fee is public on the chain. The amount and who paid are not. For space you rent from us we see the amount, because we're the seller. Nobody else does.
Sending a file to a Tari address
tTARI plus a network fee of per send, on us during the testnet.
Demo tTARITestnet tTARI for your browser
Try paid features with demo tTARI from our sponsor wallet, payments a day. Our demo wallet pays our own invoice: a real stealth transaction with play money. You can check it on the chain.When your drive opens, our sponsor wallet sends its browser wallet a grant of tTARI, and again on a later day when the wallet runs short: one grant a day per drive, and two a day per network. Packs are paid from it: a real stealth transaction with play money, built and signed in your browser. You can check it on the chain.
Testnet. tTARI has no monetary value. These prices are real logic with play money.
Ootle mainnet launches . Paid features stay on the testnet until our code has been reviewed.
Receipts on the Tari chain
When a file leaves your drive, we write that on the Tari chain, for anyone to check.
A link that ends on a file you keep gets no receipt: the file is still in your drive.
Every exit goes on the public record: we add it to SealLog on the Ootle at the next checkpoint (within ). It proves we said we deleted it, publicly and permanently. It can't prove we kept no copy.
Who sees what
Our server
We see a padded size (rounded up so the exact size stays hidden: at least 1 KB, and at most 12% over for anything bigger), the hour it arrived, its exit rule and how often it's opened. Not the name, the type, the contents or the key.
Our server sees your IP address while you're connected. We don't write it to disk: rate limits use a keyed hash, kept in memory, with a key that changes every day.
The Tari™ chain
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Our courier payments all come from one wallet with the same fee, so someone watching could group them as “sent by hidebox”, but not see to whom.
The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Sends from your browser spend a grant from our sponsor wallet, then each send's change, so anyone reading the Ootle can link them to each other and to our sponsor wallet, but not see to whom or how much.
In relay mode we post the payment for you, so we learn the address you send to, never the key.
Whoever has the link
The file, as many times as its rule says (once, by default). The link is the key: anyone who gets the full link can open it until it's gone.
A view key
A view key can't spend, but it can read everything sent to this address, including files. Give it only to someone allowed to see those.
For files sent to a drive, the view-key screen lists arrivals and opens no file. Files sent to another wallet open with its view key.
For space you rent from us we see the amount, because we're the seller. Nobody else does.
Every party, fact by fact
| Our server | Anyone watching the Ootle | Someone with the full link | The person you send it to | |
|---|---|---|---|---|
| File contents | no (ciphertext) | no | yes | yes |
| Name and type | no (encrypted inside) | no | yes | yes |
| Exact size | no (a padded bucket) | no | yes | yes |
| The key | no | no | yes (the link is the key) | yes |
| That a file exists, its arrival hour, its rule, its opens | yes | counts only, in steps of 64: records (stored files plus exits) per checkpointno | partly (state, opens left) | does not apply |
| Your IP address | while you're connected; not written to disk | no (Tari's indexer sees it only if you press “Check it on Tari's indexer”) | no | no |
| Who you send to | yes for every send and dead-drop delivery, while it's sent; then the job is scrubbedsends from your browser: no; sends our relay posts (before your browser's wallet has its grant) and dead-drop deliveries: yes, while they're sent | no (a one-time stealth address) | does not apply | they know it's them |
| That we sent a payment | yes | yes for relay sends: one wallet, one fee; sends from your browser trace back to our sponsor wallet's grant, and to each other through their change | does not apply | does not apply |
| Payment amounts | for space rented from us, because we're the seller | no (a Pedersen commitment) | does not apply | does not apply |
| The network fee | does not apply | public | does not apply | does not apply |
| SealLog (hashes, counts, times) | yes | public by design, written by the same key that holds our public tTARI | does not apply | does not apply |
Our server, in detail
- Sees and stores
- Ciphertext and its padded size; the hour an item arrived and when it left; expiry and view counts, and the time of a link's first open (shown only to its owner); the exit rule (link or password); for drives: a random-looking drive id, a hash of its login key, the first and last active day, how many distinct days it was active, quota and packs bought; invoices (pack, amount, status) and the chain outputs that paid them; drop links: the drop's Tari address, its label, its tag and daily counters; abuse reports; notes about ended links (state and day).
- Sees, never stores
- Every uploader's and every opener's IP address, live (so at the moment it happens we could link who uploaded a link with who opened it); the login key at sign-in; access tokens (hashed on arrival); which drop a file was left in, and the submitter's IP; the destination Tari address of every send, the sealed 125-byte blob, which drive sent it, and by timing which link it carries, kept in the job until sent and then scrubbed; that your drive submitted a transaction, not to whom; for dead-drop deliveries, the drop's address while the delivery is sent.
- Never sees
- File names, types, contents and real sizes; file keys, link secrets, passwords, seeds or any private key; your drive's Tari address, unless you make a drop or a relay send goes to it; who you send a file to, when your browser's wallet sends it (until its grant arrives, our relay sends it and learns the address while it's sent); which chain outputs your browser can open (everyone downloads the same copy of the chain); who shares with whom (there is no sharing inside hidebox).
Everyone else
- A network observer
- That you talk to us, timing, and padded transfer sizes. Nothing inside TLS.
- Someone with only the link's path (no
#) - That a link exists, whether it needs a password, and its state; once it has ended, why and on what day, and its exit receipt: the file's id and exit hour, and through the log which checkpoint windows its arrival and exit fell in. Not the key, the contents, the name, the real size or the open count.
- A view-key holder
- Every delivery and payment to that address: amount, time and the memo's link text. Not the files locked to the drive, and it can't spend. The one exception: files a sender sealed with “The recipient uses another Ootle wallet” do open with the view key, and the send screen says so.
- The public, through our live numbers
- The free quota; our disk's size and free space, rounded; whether uploads or the Tari features are paused; how many drives were active recently, once there are enough of them; SealLog's latest checkpoint; how many minutes ago someone last locked a file; and for up to 24 recent files, each at least an hour old, a coarse size and an exit rule, never whether one was opened, only when at least 8 exist in the last week. Nothing per person, but someone who knows a file's size and rule may recognise it there.
- Our host (Hostinger)
- Everything our server stores, plus the disk and any snapshots it keeps; in principle, live memory.
The locks
- Files: AES-256-GCM in your browser, in chunks; size padded so the exact size stays hidden.
- The key: in the link after #, or sealed to a Tari address inside a stealth payment's encrypted memo.
- Secret words: stretched in your browser with PBKDF2, rounds; we slow down guessing after wrong tries.
- Photos: location and camera data removed before locking, unless you keep the original.
The cryptography, in full
Everything that touches a file or a key runs in your browser, with your browser's own WebCrypto. The server never receives a file key, a link's secret, a password or your seed. At sign-in it sees your login key and keeps only its SHA-256.
- Files: a fresh random 32-byte key per file. The content is padded with Padmé (at least 1 KB, at most 12% over above that), cut into 4 MB chunks and encrypted with AES-256-GCM. Each chunk's nonce holds its index and a “last chunk” flag, and every chunk authenticates the file's header, so reordering, truncation and extension all fail.
- Metadata: the name, type and real size are encrypted separately, also with AES-256-GCM, padded to a multiple of 256 bytes.
- Keys from keys: HKDF-SHA256 with a separate label for every purpose, so one derived key reveals nothing about another.
- Links: every link has its own 32-byte secret, after the
#. It unwraps the file key and derives an access token; our server stores only the SHA-256 of that token. - Passwords: PBKDF2-SHA256, rounds, combined with the link's secret by default (link + password), so a copy of our database isn't enough to guess it.
- Drives: one 32-byte seed made in your browser, kept in its storage, and backed up as your recovery code or key file. Everything else is derived from it: the login key (we store its SHA-256), the drive's Tari keys, a separate wallet key, and the dead-drop tags. There is no email and no password with us.
- Safety code: 12 emoji (96 bits) derived from a drive's public keys, so two people can compare a drive out loud.
- Proof of work: SHA-256 with a number of leading zero bits, instead of a captcha. Your browser solves it in a background worker.
- Photos: location and camera data are removed from JPEG, PNG and WebP files before encryption, unless you choose to keep the original.
Sealing a file to a Tari address
When you send a file to a Tari address, your browser seals the link's secret to that address's two public keys, the owner key and the view key, using the official Tari library for the key agreement. The sealed blob is 125 bytes, and it travels as the memo of a stealth payment.
- Why a view key can't open your files: the seal needs both the owner key and the view key to open. A view key alone shows that a delivery arrived, its amount and its memo text, but not the file.
- The exception, labelled: a sender can choose “The recipient uses another Ootle wallet”. That seal uses the view key only, so anyone with that view key can open the file, and the send screen says so.
- Who posts the payment: our relay. We post the payment for you, so we learn the address you send to, never the key. The server writes the memo from its own address; a client supplies only the sealed blob.
- Who posts the payment: your browser, once its wallet holds the testnet grant our sponsor wallet sends when a drive opens: it builds, signs and pays for it, and our server passes it to the network without learning who it's for. Until the grant arrives (a minute or two), our relay posts the send and learns the address while it's sent. A browser send spends that grant, so the chain links your sends to the sponsor wallet and to each other. Dead-drop deliveries still go through our relay, because a drop's address is public by design.
Who could attack a link, and what stops them
| Adversary | Can | What stops them | What remains |
|---|---|---|---|
| Network observer | see TLS metadata | HTTPS only, HSTS, Padmé size buckets | timing and volume |
| A curious or compromised server (including our host) | read the database and the stored files, change responses, watch IP addresses live | everything is encrypted in the browser; tokens and verifiers are hashed; no public keys or drive addresses stored for logins; no sharing graph; each chunk is authenticated together with its header | can serve malicious JavaScript (see verify); can refuse service; sees the metadata listed on How it works; knows each relay send's destination; can ignore a deletion and keep ciphertext, which is useless without the key |
| Someone with only the link's path | ask for its status | the key lives only after the #; opening needs an access token derived from it; link + password adds a link proof | learns that a link exists, and its state |
| Password guesser, online | try passwords | the access token comes from the password; growing pauses; proof of work; only holders of the full link can count as wrong passwords | slow guessing against weak password-only links |
| Password guesser, with a copy of our database | guess password-only links offline | PBKDF2-SHA256 with rounds; link + password is the default, and password-only links come with a warning | weak password-only secrets |
| Chain observer | read all public state | stealth outputs, encrypted memos, one fee and one amount for every relay send, decoy sends | the fee and the transaction graph are public; relay sends are recognisable as ours; sends from a browser spend a grant from our sponsor wallet and then their own change, so they can be linked to each other and to us |
| A view-key holder | read arrivals, amounts and memo text | files locked to a drive need its owner key too | files sent with “The recipient uses another Ootle wallet” open with the view key (labelled at send time) |
| Phishing through our relay | make our wallet send a link | the server writes every memo our relay sends, from its own address; a client supplies only a 125-byte sealed blob; sends need a drive; drop deliveries only go to the drop's own address | someone can send unwanted hidebox deliveries to an address: daily caps, closable drops, a separate inbox section, no automatic preview, a warning before programs. Sends from browsers carry a memo the browser wrote, so a modified client can put any text on chain to any address, within one sponsor grant a day per drive, and two per network; inboxes show a memo that isn't exactly a hidebox link as plain text, never as a link |
| Griefer | fill storage, push proof of work up, drain daily budgets, lock links | reserved lanes for small and anonymous files, limits per network prefix, a first-day limit for new drives, public and reserved budget slices with alerts | someone with many networks can use up the public slices for a day; the reserved slices keep the guided demo working |
| Someone hosting abuse | upload and share | small and short-lived anonymous files, one open by default, proof of work, rate limits, downloads only, a warning before programs, no listing or search, noindex, the report form, removal by id | we can't scan encrypted content, by design |
| Script injection or a poisoned dependency | run code in our origin | no third-party code; a strict content security policy with Trusted Types where supported; no HTML sinks in our code; the one vendored artifact is pinned by hash; SRI on each page's top-level scripts and stylesheets, and the hash manifest for the modules they import | browser extensions (out of scope) |
| Draining our wallet through the API | spend the sponsor wallet | the internet-facing API runs as a separate user that can't reach the wallet; jobs are validated; daily budget slices; a firewall rule per user on the wallet port | at most the daily budgets |
Where Tari comes in
The Ootle is Tari's layer for applications, where payments are private by default and can carry sealed messages. We use it to deliver keys and move money, and every use below is something you can check.
Hidden on the chain
- Stealth outputs
- Sending a file to a Tari address, dead-drop deliveries, and paying for more room.
- Check:
/verify/tx/<id>shows the amount hidden, the owner a one-time key, the memo encrypted and the fee public. - Encrypted memos (XChaCha20-Poly1305)
- The file's key rides to one address inside the payment.
- Check: only the receiving drive opens it; any other key fails to decrypt.
- View keys, separate from spending
- Your inbox scans the chain in your browser;
/watchshows what a view key sees. - Check: for files sent to a drive, the view-key screen lists arrivals and opens no file. Files sent to another wallet open with its view key. More in who sees what.
- Private transactions built in your browser
- Sends signed and paid by your browser.
- Check: the transaction's input is your browser wallet's output, not ours.
Checked on the chain
- Payments
- Packs of more room, paid in tTARI to our receiving address with the invoice's reference inside it; the reference travels in the encrypted memo, so nothing on chain shows which invoice was paid, or by whom.
- Check: the official Tari library opens the commitment to the exact amount, then a Tari-run public indexer shows it committed.Check: our wallet sees it and a Tari-run public indexer confirms it exists on the chain. The amount is what our wallet reads.
- SealLog, a template of our own
- Public hashes of stored files and of every exit, on the Ootle.
- Check:
/verifychecks an inclusion proof against the root on chain.
Keys, not accounts
- Your drive
- No email, no password: a key that lives in your browser, and its Tari address.
- We store a random id and a hash of your login key. No email and no public key; a drop link stores its own address, by design.
Why not an escrow template? On the Ootle, TARI held by a template reveals its amount. Native stealth outputs keep it hidden, so that's what we use.
How payments are checked
In September a wallet bug on the testnet showed deposits that never happened (forum thread). A wallet saying “sent” isn't enough, so we credit rented space only after our own checks.
We credit a payment only after the official Tari library opens its commitment to the exact amount and a Tari-run public indexer shows it committed on the chain. The check runs in a separate process that can only read; walletd never decides a credit.
We credit a payment only after our wallet sees it and a Tari-run public indexer confirms it exists on the chain. The amount is the one our wallet reads; this deployment doesn't prove it with the official Tari library.
The exit record
Every exit goes on the public record: we add it to SealLog on the Ootle at the next checkpoint (within ). It proves we said we deleted it, publicly and permanently. It can't prove we kept no copy.
Every stored file's ciphertext hash goes into SealLog on the Ootle. Your browser can check that your file is in it, unaltered. It proves what we held and when, not what we did with it later.
This deployment keeps no public record yet: SealLog is off here.
SealLog is a template on the Ootle. At each checkpoint, at most apart, we add one Merkle root over the ciphertext hash of every file we stored and a record of every exit. The log in use is the one /api/v1/config names.
- What a proof shows: that the tree we committed to at that epoch contains your file's ciphertext hash, or the record of its exit. Not that we held the bytes, and not that we deleted them.
- What nobody can change: the log has no owner, so nobody, us included, can change its rules or the code behind it. Anyone can check
owner_rule: "None"in its header on an indexer. Checkpoints are only ever added. - Who writes it: one key, our
servicekey, the same one that holds our public tTARI. It can add any root, stop, or start a new log. Nothing yet proves that each checkpoint extends the last one. - What it shows everyone: a rough upper bound on how many records (stored files plus exits) we've written, in steps of 64; for each checkpoint window, how many whole multiples of 64 records arrived (usually none); and the writer key.
- The code it runs: anyone can check that the template on the chain is our published
seal_log.wasm, byte for byte, withtemplate/check.sh --onchainand the template's address.
The Tari map
The Ootle is Tari's layer for applications, where payments are private by default and can carry sealed messages. We use it to deliver keys and move money, and every use below is something you can check.
Hidden on the chain
- Stealth outputs
- Send by Tari, dead-drop deliveries, more space, and pay to open.
- Check:
/verify/tx/<id>shows the amount hidden, the owner a one-time key, the memo encrypted and the fee public. - Encrypted memos (XChaCha20-Poly1305)
- The file's key rides to one address inside the payment.
- Check: only the receiving drive opens it; any other key fails to decrypt.
- View keys, separate from spending
- Received scans the chain in your browser;
/watchshows what a view key sees. - Check: for files sent to a drive, the view-key screen lists arrivals and opens no file. Files sent to another wallet open with its view key. More in who sees what.
- Private transactions built in your browser
- Sends signed and paid by your browser.
- Check: the transaction's input is your browser wallet's output, not ours.
Checked on the chain
- Payments
- More space, paid in tTARI to our receiving address with the invoice's reference inside it; the reference travels in the encrypted memo, so nothing on chain shows which invoice was paid, or by whom.
- Check: the official Tari library opens the commitment to the exact amount, then a Tari-run public indexer shows it committed.Check: our wallet sees it and a Tari-run public indexer confirms it exists on the chain. The amount is what our wallet reads.
- Pay links
- Our server releases its half of a pay link's key only once a Tari-run public indexer shows the payment committed.
- Check: the buyer's page shows each step and links the payment's tx; the owner's Received lists the payment.
- SealLog, a template of our own
- Public hashes of stored files, and a receipt for every file that leaves, on the Ootle.
- Check:
/verifychecks an inclusion proof against the root on chain.
Keys, not accounts
- Your drive
- No email, no password: a key that lives in your browser, and its Tari address.
- We store a random id and a hash of your login key. No email and no public key; a drop link stores its own address, by design.
Why not an escrow template? On the Ootle, TARI held by a template reveals its amount. Native stealth outputs keep it hidden, so that's what we use.
How payments are checked
In September a wallet bug on the testnet showed deposits that never happened (forum thread). A wallet saying “sent” isn't enough, so we credit rented space only after our own checks.
We credit a payment only after the official Tari library opens its commitment to the exact amount and a Tari-run public indexer shows it committed on the chain. The check runs in a separate process that can only read; walletd never decides a credit.
We credit a payment only after our wallet sees it and a Tari-run public indexer confirms it exists on the chain. The amount is the one our wallet reads; this deployment doesn't prove it with the official Tari library.
Check us
SRI only pins files to this page; it can't protect you from us. What can: comparing what we serve with the published source, release hashes notarized on the Ootle, and running your own copy.
- Every build writes a manifest with the SHA-256 of every file this site serves and the git commit it came from.
tools/verify-deploy.pyrebuilds the site from a clean checkout of that commit, downloads every file in the live manifest, and prints any difference.- Since SealLog went on, each release's manifest hash is also notarized through it; /releases.json lists them with their transactions. Anyone can notarize, so only events whose signer is the log's writer key are ours.
$ git clone https://github.com/c7b/hidebox $ cd hidebox $ python3 tools/verify-deploy.py https://srv2052606.hstgr.cloud c73c7b415ecdc88bc848a76fc68c91528702be62
It needs git and Python 3.11 or newer. This checks the site now. A browser can't pin it for you between visits.
Check it in your devtools
No analytics, no trackers, no third-party scripts or fonts. Your browser contacts a Tari-run indexer only when you press “Check it on Tari's indexer”. To see it for yourself:
- Open your browser's developer tools and choose the Network tab.
- Reload this page, then drop a file on the home page.
- Every request goes to this site's own address. Nothing else appears unless you press “Check it on Tari's indexer”.
Headers and content security policy
Every response carries HSTS, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy: same-origin, X-Frame-Options: DENY and a Permissions-Policy that turns off the camera, microphone, location, payment and USB APIs.
Pages allow scripts, styles, fonts and requests only from this origin, and nothing inline is allowed to run. The app pages add WebAssembly for the official Tari library, Trusted Types where the browser supports them, and the two Tari-run indexers for the “Check it on Tari's indexer” button.
Try to read one of our files
(Our hourly test file is between takes. Check back in a few minutes.)
Every hour we lock a test file. Here it is, exactly as our disk holds it.
Here's a file we locked . It's on our disk right now.
Download the stored bytescurl -OJ https://srv2052606.hstgr.cloud/api/v1/canary/rawIts key left on the Ootle, sealed to our own test drive: tx .
It shows what our disk holds. It can't prove we can't read your files: we locked this one ourselves, and we publish its key a day later.
Yesterday's key: now you can read it.
Once a test file is a day old, we publish its key. Your browser fetches the stored bytes and opens them here, the same way a link does.
Key:
Live evidence
Our own test transactions, never a user's. They come from /evidence.json, refreshed every hour; older ones are archived in the repository
- Network
- Official Tari library
- ootle-wasm , SHA-256
- Test deliveries (a file key sealed to our own test drive)
- Test payments (our demo wallet paying our own invoice)
Report a vulnerability
Please tell us privately before you tell anyone else. We'll answer, fix what's real, and credit you if you want.
- Leave us a file through our own drop link
- Open a private security advisory on GitHub
- Report a link that hosts something harmful
The full threat model, with the findings of our own review, is in docs/threat-model.md.
What we can't hide
Beyond what our server sees (who sees what), here is what we can't protect you from:
- The person you send it to. They can save, screenshot or forward it.
- Anyone who gets the full link. The link is the key. Add a secret word (sent another way) or send it to one Tari address.
- Chat apps that preview links. Their servers see the full link. They can't use up a view (opening takes a click), but they could open it.
- Your own browser. Your browser history, and any history sync, can keep the full link, key included. We clear the key from the address bar once it's opened, but we can't reach your history. Extensions allowed to read your tabs can see the link. Clipboard history (and cloud clipboards) can keep a copied link.
- A compromised device or extension. Encryption in the browser can't beat malware in the browser.
- A malicious version of this website. It's the known weak spot of all web encryption: if our server were compromised, it could serve code that leaks keys. SRI only pins files to this page; it can't protect you from us. What can: our verify-deploy script and running your own copy.
- Metadata. We see a padded size (rounded up so the exact size stays hidden: at least 1 KB, and at most 12% over for anything bigger), the hour it arrived, when it leaves (rounded up to the hour when that's an hour or more away; “midnight” is your own local midnight, which shows your time zone), its exit rule and how often it's opened. Not the name, the type, the contents or the key. Our server sees your IP address while you're connected. We don't write it to disk: rate limits use a keyed hash, kept in memory, with a key that changes every day. A VPN or Tor hides your IP from us; links without a drive work in Tor Browser's Safer mode, and Tari features need WebAssembly.
- On-chain facts. The chain shows an output with a hidden amount, a hidden owner and an encrypted memo. The fee is public. Payments our relay sends all come from one wallet with the same fee, so someone watching could group them as “sent by hidebox”, but not see to whom. Sends from your browser spend a grant from our sponsor wallet, then each send's change, so anyone reading the Ootle can link them to each other and to our sponsor wallet, but not see to whom or how much. SealLog shows roughly how many records (stored files plus exits) we write, in steps of 64, and when.
- Deletion you can't see. We delete on cue, and nothing can prove we kept no copy. Disks can't be securely wiped, so deleted ciphertext can linger in free blocks. Without the key it's noise. Our host also takes automatic snapshots of the whole server and keeps them for a few weeks. A file that existed when one was taken stays in it, as ciphertext, until that snapshot is rotated out.
- Testnet resets. Esmeralda can be reset. Payments and on-chain records from before a reset are archived in our repository; files on our server are not affected.
- Legal requests. If compelled, we can hand over what we have: ciphertext, the columns “What our server knows” prints, invoices. Not contents. Not keys.
What never touches a disk
Our server and web server never write your IP address, your links or your files' ids to a log. The database holds file and link ids, never keys; Privacy lists every table.
- Web server: no access log, and the error log drops addresses, paths, hosts and headers.
- API: logs only the route's name, the status and how long it took.
- Rate limits: a keyed hash of your network prefix, kept in memory, with a key that changes every day.
- Background jobs: fixed codes, never addresses, ids or memos. Our own test transactions are the only ones we publish.
- The firewall: logging is off. System journal: kept for seven days.
That's not the same as “nobody ever sees your IP”: our server sees it while you're connected, and so could our host.
How it was built, and tested
No analytics, no trackers, no third-party scripts or fonts. Your browser contacts a Tari-run indexer only when you press “Check it on Tari's indexer”.
Built with AI coding agents under the author's direction. The design, threat model and crypto choices were reviewed by the author. Python's standard library on the server, plain JavaScript in the browser. One runtime dependency: Tari's official ootle-wasm , pinned by its sha512.
Every release runs the server, site, browser and end-to-end test suites first; the records are in the repository. This deployment runs commit c73c7b415ecdc88bc848a76fc68c91528702be62.
Run your own
File sharing needs Python's standard library and a web server. The Tari features also need a walletd on the Ootle testnet and Node for the payment checker. Self-host guide
Who's behind it
hidebox is built by an independent developer in Mexico, for the Tari community's October 2026 “Spooky Secrets” build contest, and it's meant to outlive it.
The fine print (privacy, terms and how to report a link) is on one page: the fine print.
How we tested
Every release runs the server's tests, the site's checks, the browser flows of the drive, and the deploy checks; then the end-to-end scenarios run against this deployment through the drive, as a stranger would. Our own security review found that a pack payment could count when its one-time key wasn't ours. Now a payment counts only if its one-time key is ours. We tested that fix on a fake chain, never on the public testnet.