Checking…
Create an account
Your files here are encrypted, so nobody, including the people who operate this site, could ever open them. Your password is what unwraps your private key, which is why a reset email would not help: the server could let you back into the account, but every file would be sealed. If you forget your password or lose a passkey, the recovery codes issued next are the only way back into your account.
Use a recovery code
Each recovery code can only be used once. You will be prompted to set a new password after recovering your account.
Account
Trash
These files will continue to count towards your storage quota until they are permanently removed, which happens automatically 30 days after deletion. Sharing links pointing to a file are removed when moved to the trash, and restoring a file does not recover them.
Support
Messages are encrypted with your private key before they leave your device, the same way your files are. Never send your password, authenticator setup key, recovery codes, API tokens, email address, or other personally identifying information in this thread.
Desk History
Every claim, release, reply, move and archive across all desks, newest first.
Desks
Each desk has its own key. Staff you seat at a desk can read every conversation filed there, and nothing from the desks they are not seated at.
Support Staff
Accounts made support staff from the Admin page appear here. Give one a desk and they hold its key until you take it back.
Embeds
Put an upload box on any website you run. Visitors' files are sealed in their own browser before they leave, and land in the folder you choose. No account is needed to send, and the box never sees your vault. Paste one line into your page and it is live.
Write Tokens
Give Shortcuts, scripts, or a scheduled job its own token so it can add files to your vault without your password. A token can only put things in: it cannot read, list, or download anything. What it uploads is encrypted upon arrival.
Read Tokens
A read token lets a script or the CLI list and fetch files from one folder, or your entire vault. What it serves stays encrypted: only the token key can unlock it, and only its hash is stored.
Webhooks
A webhook can call a URL of your choosing when a token or a receiving link takes in a file. Each call carries only the event, time, and file size. It cannot read names, contents, or keys.
Notifications
quattle can tell you when something happens without ever saying what it was. A notification never carries a file name, a message, or anything else you have stored: it only says that something arrived, so you know to look.
What to tell you about
These apply to every device you have turned notifications on for.
Devices
Each device you turn notifications on for appears here. Removing one stops it receiving anything.
Storage
What every live account could store between them, and how much they store currently.
Accounts
The darker bar is accounts opened, the lighter one is sign-ins.
Embeds
The darker bar is embed pages created, the lighter one is files sent through them.
Uploads
Files added over time, combined upload size, and average size.
Hotlinks
Requests for files served from a public address, and the sites embedding them.
Public Files
No Longer Public
Referrals
Support
The darker bar is conversations opened, the lighter one is conversations archived. Everything here is counted from timestamps, never from what was written.
Sharing Links
The darker bar is total created, lighter is opened.
Top Charts
The most opened links in the selected time span.
Embeds
The darker bar is embed pages created, the lighter one is files sent through them.
Version History
When an upload replaces a file already in your vault, the older copy can be kept so you can view it or go back to it. Kept versions count against your storage until they fall off the end of the list or you delete them.
Change Password
Passkeys
A passkey allows account access using your device's biometrics feature, such as Touch ID and Face ID.
Authenticator Codes
A temporary six-digit code that enables a second layer of authentication, asked for after your password. It cannot unlock your files, but it means that a stolen password on its own will never provide access to your account.
Create a password first. An authenticator code is asked for after a password, so it has nothing to add to an account that signs in with a passkey alone.
Security Keys
A key you plug in or tap, such as a YubiKey. It works the same way a passkey does and holds real key material, so it can open your files rather than only proving who you are. It needs to support hmac-secret, which the YubiKey 5 series and most keys sold since do.
Recovery Codes
A code can only be used once. Generating a new set immediately replaces the old ones, including any that have not been used.
Sessions
Devices where you chose to stay signed in for 7 days. Each holds a copy of your key that only that device can use. Removing one signs it out immediately.
Export
Every file you own in one zip, decrypted in this browser, with your folders as folders. Some browsers have to hold the whole archive in memory, so a very large vault is better taken a folder at a time.
Delete Account
Everything disappears: every file, folder, and sharing link you created. Your account will be permanently deleted, and there is absolutely no way to recover your data afterwards.
E2EE command line tool with support for git-annex
Moves files in and out of your vault via scripts, scheduled jobs, and the terminal. One crypto file with no dependencies: nothing to install beyond Python 3. Everything it uploads is encrypted on your device.
- quattle.py 1.5.37d7fde0f9a65e89c865c66e15575496c3c6074e9bd68b5b55e8d41d5fb82069f
- quattle_annex.pyfc8bb2ac36a3d8faf03e8617037ac5b59fb0e051f74e91063db3e68ac0a98f49
- vectors.jsona808c1958fcc0f8cdabc4339db531d25d7b46c32b3696eac61c6c02817a6f88e
- quattle-cli.zip7a3747e222660c69ec138fc75878eb9123acf69738b48a40295d1430138bc02e
Install
Two downloads and a self-test. The test checks the tool's own cryptography against known answers generated from the app's crypto, including that its encryption produces byte-identical output. If the test passes, the tool is trustworthy; if it fails, stop.
Expect a list of pass lines ending in all vectors pass. You can also confirm the download is the published release by comparing shasum -a 256 quattle.py against the hash ledger at quattle.app/cli.
mkdir -p ~/quattlecd ~/quattlecurl -O https://quattle.app/cli/quattle.pycurl -O https://quattle.app/cli/vectors.jsonpython3 quattle.py test
Tokens
The CLI never sees your password. It uses tokens you mint on the Automation page, each doing exactly one job. A write token can only add files. A read token can only fetch them, and the server holds just a hash of it. Set them as environment variables; they last for the terminal window, so re-export after restarting it. Two commands use both at once: share, and serve when it mounts read-write.
Treat tokens like passwords. A token pasted anywhere public is burned: revoke it on the Automation page and mint a fresh one. Both token kinds take an optional limit per hour, day, week, month, or year of your choosing. A write token's limit is a number of uploads: a file of any size counts as one. A read token's limit is an amount of data served, in megabytes: fetching a 40 MB file spends 40 MB of it, listings are free, and a resumed download counts only the bytes actually sent. The tally resets when the chosen period turns, and read tokens keep a per-day history of what they served.
export QUATTLE_DROP_TOKEN="your-write-token"export QUATTLE_READ_TOKEN="your-read-token"
put
Uploads one file, encrypted on your device at any size up to about 900 GiB, or 500 MB on a free account, and a file over your limit is refused before a single byte is sealed or sent. Large files stream in parts, so nothing is ever held in memory whole, and several parts fly at once: --workers N anywhere on the line sets how many, three unless you say otherwise. --path files it into folders under the token's root, creating any that do not exist, up to eight levels deep. If the token allows links, the response includes a ready sharing link; the part after # is the key and never reaches the server. Sending the same unchanged file to the same folder twice says it is already in the vault instead of storing a duplicate; delete it in the app and the same command uploads it fresh.
A big upload that dies partway is not lost. Run the same command again and it resumes from the last part that made it, re-sending nothing: Ctrl-C, a dropped connection, or a closed laptop all end the same way, with the tool telling you to run it again. Parts already in flight finish sending first, so the resume count can be higher than the last line you saw. If the file itself changed in between, resuming would be wrong, so the tool says so and starts over. Progress lives in your temp folder, readable only by you, and is removed the moment the upload completes.
In a terminal, open printed links with open "url" in quotes or by copying them. Tapping a link in some terminal apps mangles the # and sends the key to the server; treat any link opened that way as burned.
One timing note: a file added from the CLI is readable by your read tokens at once, because the CLI wraps the key for them at upload. Its folder path, though, is filled in when you next open the app, so until then it lists at the top level. A file dropped by a simpler client, like a Shortcut, appears after that next open.
Passing - instead of a file reads standard input, spooled through a temporary file, so pipes work: pg_dump db | python3 quattle.py put - --name backup.sql. A name is required when reading a pipe. Long uploads report themselves part by part as they go.
python3 quattle.py put <file or -> [--name shown-as] [--path folder/sub]
$ python3 quattle.py put report.pdf --path invoices/2026Added report.pdf (48213 bytes, sealed on this device)https://quattle.app/s/xxxxxxxx#k=xxxxxxxx
$ python3 quattle.py put archive.tar --workers 4Sent part 1 of 12Sent part 3 of 12^CUpload paused. Run the same command again to resume where it stopped.$ python3 quattle.py put archive.tarResuming: 2 of 12 parts already sent
ls
Lists everything the read token covers, with folder paths, sizes, dates, and ids. Names and paths are decrypted on your device; listings are free and do not count against a read limit. --path narrows the listing to one branch.
python3 quattle.py ls [--path folder/sub]
$ python3 quattle.py lsinvoices/2026/report.pdf 48213 2026-08-13 09:12 hv1XQEx2wsDAv2jkcFIgyw
get and cat
get downloads and decrypts one file, then prints its SHA-256 so you can compare it with the checksum in the app's file menu. cat streams the decrypted bytes to standard output for piping. If the connection dies mid-download, get resumes from where it stopped, announcing itself; every chunk is authenticated, so a finished download cannot be silently wrong.
python3 quattle.py get <name or id> [-o outfile]python3 quattle.py cat <name or id>
$ python3 quattle.py get report.pdf -o report.pdfWrote report.pdf (48213 bytes)sha256 0d4c...e2a1
sync
Mirrors a local folder into the vault. It asks the server which files it already has, then uploads only what is new or changed, preserving your subfolder structure. Change detection is name, size, and modified time, so rerunning it is cheap and safe: unchanged files are skipped without uploading a byte. Files and folders whose names start with a dot are ignored.
This makes a nightly backup one line in a scheduled job. Interrupted runs are safe to repeat: every upload carries an idempotency key, so nothing is ever stored twice.
python3 quattle.py sync <folder> [--path folder/sub]
$ python3 quattle.py sync ~/projects --path backup+ notes/todo.txtUploaded 1 new or changed, skipped 42 unchanged
watch
Runs sync on a loop, every 60 seconds unless you say otherwise, until you stop it with Ctrl-C. Drop a file into the watched folder and it is in your vault within the interval.
python3 quattle.py watch <folder> [--path folder/sub] [--every seconds]
rm
Deletes one file within the read token's scope. It lands in your trash like any other deletion, recoverable until the trash empties, and requires a read token with deleting turned on, which is off by default.
python3 quattle.py rm <name or id>
share
Mints a sharing link for a file already in the vault. The link behaves exactly like one minted at upload: the write token's configured lifetime and open settings apply, and the key rides in the part after #, which never reaches the server. This command uses both tokens: the read token finds and unlocks the file, the write token mints the link, and the file must sit inside the write token's folder.
python3 quattle.py share <name or id>
verify
Proves a local folder matches the vault without downloading a byte. Every file's SHA-256 is computed locally and compared against the sealed checksum stored at upload, decrypted on your device. Files that differ or are missing from the vault are listed and the exit code is 1, so it slots straight into scheduled jobs.
python3 quattle.py verify <folder> [--path folder/sub]
restore
The mirror of sync: rebuilds the read token's files into a local folder, folder structure included, decrypting everything on your device. Files already present are skipped after their checksum matches the stored one, so rerunning fetches only what is missing or changed. --path restores just one branch, placed at the destination's root.
python3 quattle.py restore <folder> [--path folder/sub]
$ python3 quattle.py restore recovered --path invoices+ 2026/report.pdfRestored 1, skipped 0 already present
serve
Runs a WebDAV bridge on your own machine, so the vault mounts as a volume. With just the read token it is read-only: browse, open, copy out. Set the write token as well and it goes read-write: save into the mount, delete from it, make and fill folders, move and copy files, and it all lands sealed in the vault. Every byte is decrypted on your device as it is read and sealed on your device as it is written; the bridge listens on localhost only and stays up until Ctrl-C. Partial downloads work: Range requests decrypt only the chunks they need.
On a Mac the vault becomes a Finder volume: Go, Connect to Server, http://localhost:8787, Connect as Guest. Read-write it behaves like a disk: drag files in and they upload sealed, drag them to the Trash and they land in your quattle trash, make folders and fill them. Deleting and moving need a read token with deleting turned on. Two honest limits: renaming a folder is not supported, so move the files inside it instead, and an empty folder made through the mount lasts as long as the bridge runs, becoming permanent once a file lands in it. The hidden housekeeping files Finder writes are accepted and quietly discarded, so they never clutter the vault.
The bridge speaks the standard verbs: PROPFIND for listings, GET and HEAD with Range, and when writable PUT, DELETE, MKCOL, MOVE, COPY, LOCK and UNLOCK, so ordinary WebDAV clients work unmodified. That makes it the door for rclone, and with it most sync tooling: point a remote of type webdav at the bridge and rclone sync a folder both directions, rclone copy for one-way, rclone ls to look around. Files arrive as real, openable, shareable files in the vault, not opaque blobs. Files you save into folders through the bridge show up in the right place while it runs; if you restart it before next opening the app, they appear at the top level until that next open files them properly.
python3 quattle.py serve [--port 8787]
rclone config create quattle webdavurl http://localhost:8787 vendor otherrclone ls quattle:rclone sync ~/Documents quattle:Documentsrclone copy quattle:invoices ~/invoices
test and version
test re-runs the cryptographic self-check any time; it needs no network and no tokens. version prints the release, which you can match against the hash ledger. On slower devices, long uploads also announce each part as its encryption begins, so a working upload never looks stuck.
The tool seals and opens through the OpenSSL library your system already has, which uses the processor's own AES instructions: sealing runs at hundreds of megabytes per second. Where that library is not found, the built-in dependency-free implementation takes over automatically, slower but identical. test names the backend in use, and when the fast one is active it also proves the two produce the same bytes.
python3 quattle.py test [vectors.json]python3 quattle.py version
$ python3 quattle.py testpass chunk0...pass crypto backend: opensslpass pure python agreesall vectors pass
Machine output
Add --json anywhere on the line and ls, get, put, sync, test and version print one JSON document on standard output, progress chatter moves to standard error, and failures print {"error": "..."} with exit code 1. One format for every outcome, made for scripts, launchers, and anything that would rather parse than scrape.
python3 quattle.py --json ls
git-annex
If you keep large files out of git with git-annex, quattle can be one of its remotes. The repository still tracks names and history; the file contents live in your vault, sealed on your machine before they leave it. Nothing about your annex is readable here, and a drop token can add content without being able to read any of it back.
Put quattle_annex.py beside quattle.py, make sure a program called git-annex-remote-quattle is on your PATH, then set up the remote once:
It uses both tokens: the drop token stores content, the read token fetches it and checks what is already there. The read token must cover the drop token's folder or something above it, or stored content will not be readable back. prefix chooses the folder inside that scope, and defaults to annex.
Dropping content from the remote sends it to your trash rather than destroying it, so a mistaken drop is recoverable until the trash empties. That requires a read token with deleting turned on, which is off by default.
git annex initremote quattle type=external externaltype=quattleencryption=none prefix=annexgit annex copy big.bin --to quattlegit annex get big.bin --from quattlegit annex drop big.bin --from quattle
When something says no
Errors are printed as the server said them. The ones with a meaning worth knowing: a file over your plan's size limit is refused the moment the upload starts, before any data moves, and on a free account the message says so plainly; a token over its allowance is refused until its period turns, and the error names the period; an account out of storage refuses uploads with the room it lacks; a revoked or expired token is refused everywhere. A message about the connection dying means the tool already retried eight times and the network genuinely was not there.
What the CLI can and cannot see
Uploads are sealed before they leave your device, and downloads are unsealed after they arrive, so the server never holds a file, a filename, or a folder name it can read. A write token cannot list or fetch anything. A read token can fetch only what it covers, within whatever monthly limit you gave it, and everything it serves is written to a per-day history you can inspect. The full accounting, including the one honest caveat about where trust still sits, is in SECURITY.md in the repository.
Good to know
syncandwatchnever delete anything, locally or in the vault. They are backup semantics, not mirroring- Tokens are read from the environment only, never from arguments, so they stay out of shell history and process listings. On a Mac, keep them in Keychain and load one with
security find-generic-password -s quattle-read -w - On iPhone and iPad, upload parts go one at a time; everything else behaves the same
- a-Shell keeps one Python alive between commands. If an interrupted run leaves the next one failing instantly with a fatal Python error, close and reopen a-Shell
- The single file runs anywhere Python 3 does, including a-Shell on iPhone and iPad
Shortcuts
One shortcut turns the share sheet into an upload button: pick a photo or file anywhere on iPhone or iPad, tap the shortcut, and a moment later the file is sealed in your vault with a sharing link on your clipboard. It talks to the same endpoint as the CLI using a write token, so it can add files but never read any.
Download the shortcut, then open it once in the Shortcuts app and paste your write token into the Authorization header. Or build it yourself in five minutes:
Build it
- Receive from Share Sheet. Tap the types and make sure Images is on, plus anything else you want to send. Leave "If there's no input" on Continue
- Get Contents of URL, address
https://quattle.app/d/. Expand it: Method POST. Under Headers, addAuthorizationwith the valueBearer, a space, and your write token. Tap Request Body and switch it from JSON to Form, then add a field of type File namedfileand set its value to the Shortcut Input variable. Add a Text field namedpathif you want uploads filed into a folder, likephotos - Get Dictionary from Contents of URL
- Get Value for
linkin Dictionary - If the Dictionary Value has any value: Copy to Clipboard (the same Dictionary Value), then Show Notification, title
quattle, bodyUploaded · link copied to your clipboard - Otherwise: Show Alert, title
quattle, message set to the Contents of URL variable, and turn Show Cancel Button off - End If, and name the shortcut. Done
The traps, so you skip them
- Request Body defaults to JSON in a new action. The server needs Form, and will say "Send the file as form data" until it gets it
- The file field must be the File type. A Text field turns your photo into text, and the server answers "No file in the request"
- Test from the share sheet, not the editor's play button. The play button has no input, so the run sends an empty form even when the shortcut is perfect
- When you rotate tokens, edit only the Authorization header. If the editor ever refuses to hold a change, duplicate the shortcut and edit the copy
Links minted this way follow the write token's settings: the lifetime and the open-once switch you chose on the Automation page apply to every link the shortcut produces.
Your vault on the Mac
The CLI's serve command turns the vault into a volume your Mac can mount and a WebDAV endpoint any tool can talk to. Everything below assumes it is running; the second section makes that automatic. Every byte is still sealed and unsealed on your machine, and the tools you point at the bridge talk only to localhost and never see your tokens.
Mount it in Finder
Start the bridge with both tokens set, then in Finder: Go, Connect to Server, http://localhost:8787, Connect as Guest. The vault mounts like a disk. Quick Look previews work, dragging files in uploads them sealed, dragging them to the Trash sends them to your quattle trash, and new folders are a right-click away. Copying out decrypts as fast as the disk can take it, and video plays straight off the mount, scrubbing included, because the bridge serves the exact byte ranges the player asks for.
Treat the mount as a place to move things in and out of and browse, not as a working directory. Apps that keep documents open, autosave in place, or save through temporary files belong on your disk: edit locally, then drag or sync the result in. The section at the bottom lists what that covers.
export QUATTLE_READ_TOKEN="your-read-token"export QUATTLE_DROP_TOKEN="your-write-token"python3 ~/quattle/quattle.py serve
Keep it running
A launch agent starts the bridge when you log in and restarts it if it stops, which is what makes the mount and everything below feel like part of the machine. The tokens live in the agent's environment, so Finder, rclone, and anything else that uses the bridge never holds a credential of its own.
Swap in your own username in the two paths, paste your tokens, then load it once. It runs from the next login onward.
cat > ~/Library/LaunchAgents/app.quattle.serve.plist <<'PLIST'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>app.quattle.serve</string><key>ProgramArguments</key><array><string>/usr/bin/python3</string><string>/Users/you/quattle/quattle.py</string><string>serve</string></array><key>EnvironmentVariables</key><dict><key>QUATTLE_READ_TOKEN</key><string>your-read-token</string><key>QUATTLE_DROP_TOKEN</key><string>your-write-token</string></dict><key>RunAtLoad</key><true/><key>KeepAlive</key><true/></dict></plist>PLIST
launchctl load ~/Library/LaunchAgents/app.quattle.serve.plist
rclone
rclone speaks WebDAV, so one remote definition connects it to the bridge and the whole rclone toolbox follows. sync on a schedule is a nightly encrypted offsite backup whose contents are real files: openable in the viewer, shareable by link, restorable one at a time, not opaque pack blobs. copy moves things either direction without deleting anything, and check audits a folder against the vault without transferring a byte.
Deletes and moves through rclone need a read token with deleting turned on, and everything deleted lands in your quattle trash, so a mistaken sync flag is recoverable. bisync, rclone's two-way mode, does work against the bridge, but it is experimental on rclone's side and this one: keep it to folders you can afford to re-sync.
Skip rclone mount: it needs macFUSE and a system extension, and the Finder mount above already covers what it would give you.
rclone config create quattle webdavurl http://localhost:8787 vendor other
rclone sync ~/Documents quattle:Documentsrclone copy quattle:releases ~/releasesrclone check ~/Documents quattle:Documents
What belongs on the mount, and what does not
- Moving files in and out, browsing, previewing, and playing media are what the mount is for. All of it is sealed and unsealed on your Mac
- Do not point apps at the mount as a working directory. Anything that keeps a document open, autosaves in place, or writes through temporary files, which covers most document editors, Xcode projects, and any kind of database, should live on your disk. Edit locally, then drag or sync the finished file in
- Backup tools that write their own pack or bundle formats, like restic, Arq, or Time Machine, are the wrong fit. They would fill your vault with blobs nothing can open, and they re-encrypt data the vault already seals. Use
rclone syncor the CLI's ownsync, which keep files as files - Renaming a folder through the mount is not supported. Move the files inside it instead
- An empty folder created through the mount lasts as long as the bridge runs, and becomes permanent once a file lands in it
- Files saved into folders show up in the right place while the bridge runs. If you restart it before next opening the app, they appear at the top level until that next open files them properly
- Nothing works offline. The mount is a live window onto the vault, not a copy, so the bridge has to be running and the network reachable
- Spotlight does not index the mount, and there is no Files-app or share-sheet presence. Those need a native app
- The hidden housekeeping files Finder writes are accepted and quietly discarded, so they never clutter the vault
- The bridge listens on localhost only. Nothing on your network can reach it, and nothing that uses it ever holds your tokens
Sealed uploads from any web page
A single module your site imports to take files into a vault. Everything is sealed in the visitor's browser before it leaves, against a token that can write and read nothing. There is no interface and nothing is injected: you build the form in your own design, quattle.js does the cryptography.
- quattle.js 1.10aac170423e1c324a04a06ec9fc35fff71d780cfb90c6a5d257f46970230b76c
- vectors.jsona808c1958fcc0f8cdabc4339db531d25d7b46c32b3696eac61c6c02817a6f88e
Add it to your page
One import, no dependencies, nothing to build. Load it from here, or copy the file to your own site and verify it against the hash above; a copy you host updates only when you update it. Put your code in its own file rather than inline: sites with a strict security policy block inline scripts, and yours may be one of them.
<script type="module" src="/send.js"></script>
import { drop } from "https://quattle.app/sdk/quattle.js";
Send a file
One call does everything. The file is sealed on the visitor's device in 4 MB chunks, uploaded in parts with live progress, and wrapped to your read tokens, so the app, the CLI, and your automations see it the moment it lands. When the token mints sharing links, the result carries one, and the part after # is the key, which never reaches the server.
Options: path files it into a folder inside the token's scope, name overrides the shown name, onProgress receives sentBytes and totalBytes, and base points the calls somewhere other than quattle.app, which the token section below puts to use.
const result = await drop(token, file, {path: "intake",onProgress: (state) => {meter.value = state.sentBytes / state.totalBytes;},});if (result.link) show(result.link);
The token
Mint a dedicated write token for each site on the Automation page, and give it the shape of the job: a cap per hour or day, an expiry, a pinned folder, and link minting only if you want links handed out. The token sits in your page where anyone can read it, and that is the design. It can add files and see nothing, not names, not contents, not your vault. If someone abuses it, they can only spend the allowance you chose; revoke it any time and every refusal shows up in the token's History.
If you would rather not show it at all, put a small proxy on your own domain that holds the real token and forwards the /d/ calls with the authorization header attached, then pass your proxy's address as base. The file is sealed before it leaves the visitor's browser, so your proxy relays ciphertext and never sees a byte of plaintext. This is optional hardening against someone spending your quota, not a security requirement.
Errors and repeats
Every failure throws with a plain sentence meant for people, so show it to your visitor as it is. A file over the account's size limit is refused before a single byte is sealed. Sending the same unchanged file to the same folder again comes back with repeated set and no link, and nothing is stored twice.
try {await drop(token, file, { path: "intake" });} catch (trouble) {status.textContent = trouble.message;}
Your page's security policy
If your site sets a Content-Security-Policy, it needs two allowances: script-src for wherever quattle.js loads from, and connect-src https://quattle.app for the upload itself. A site with no policy needs nothing.
script-src 'self' https://quattle.app;connect-src 'self' https://quattle.app
Prove the crypto
The module carries a self-test that runs the same known-answer vectors the app and the CLI are held to, in the visitor's own browser. Anyone can paste this into a console and watch the checks pass, which is the whole trust story in two lines.
const sdk = await import("https://quattle.app/sdk/quattle.js");await sdk.selfTest("https://quattle.app/cli/vectors.json");
What people build with it
- A client intake page on a business site, where contracts and statements land sealed instead of sitting in an email inbox
- A return slot for a photographer or designer, where clients send selects and revisions straight into the project folder
- Attachments on a contact or bug-report form, with logs and screenshots arriving encrypted and the form owing nothing to a third-party upload service
- A drop box for an event or a kiosk, taking files all day on a token that expires when the day does
- A personal send-me-the-file page you text to someone who has never heard of quattle and never needs an account
Good to know
- Works in any current browser. The cryptography is the browser's own WebCrypto plus the same sealed format everything else here uses
- Uploads land at the top level of the vault until you next open the app, which files them into the token's folder
- Limits are simply your account's and the token's own. Free and paid accounts embed the same way and hit the same caps they already have
- Parts go up one at a time, and a closed tab abandons the upload; the unfinished pieces clean themselves up on the server within a week. There is no resume, a reload starts over
- There is no widget and no quattle branding to carry, on purpose. The live example above is a plain page you can view source on and take as a starting point
Invites
Moderation
Paste a reported share link to see what it points at and who created it. Nothing here reads content. The key that opens a share travels in the part of the URL browsers never send, so it never reaches this server. Removing a link kills it immediately and gives anyone who follows it a page saying the content was removed following a legal request.
Codes
A code carries months of quattle+ and is redeemed once. Nothing records who it was given to.
Accounts
Deleted
quattle+
- 256 GB of encrypted storage, 64 times the free plan
- Files up to 2 GBs in the browser, and no per-file limit in the CLI
- Version history, so a file can be recovered
- 180-day trash instead of 30
- No limits on tokens, webhooks, links, shared folders, or secrets
Save $29 a year over paying monthly
Billed monthly, cancel whenever you like
12 month code that never expires
quattle
The core experience is completely free
- 4 GB of encrypted storage, 6 GB with your two invites used
- Sharing and receiving links with analytics
- Collaborate on files with other people
- Passkey and authenticator support
- Webhooks and write/read tokens
- Command line tool and git-annex remote
- folders, favorites, search, and sorting
- 30-day trash with a countdown, 180 on quattle+
- video and audio that stream
- sha-256 checksum for every file
- hardware security keys and recovery codes
- quick look for almost any file
- built-in editor for text and markdown
- light and dark themes
- links with expiry and a passphrase
- custom names on the links you share
- receiving links, no account needed
- shared folders with a history
- place a shared folder anywhere in your own vault
- send files to another account
- shortcuts that upload for you
- one page of every live link
- invites that earn extra storage
- column view, walkable with the arrow keys
- mount your vault as a volume on macOS
- a WebDAV bridge that reads and writes
- rclone sync, both directions
- keyboard shortcuts throughout
- multi-select for moving, downloading, or trashing
- drag files and folders in from your desktop
- desktop-class file browser with drag and drop and spring-loaded folders
- folder uploads that keep the directory tree
- paste a file to upload it
- drag in from the files and photos apps
- swipe a row to trash
- install to your home screen
- download a folder, or the whole vault, as one zip
What the free plan holds
Every feature is included, with room to try each one: 4 GB of storage, files up to 500 MB, one shared folder of your own with no limit on folders you join, one write token, one read token, one webhook, and one receiving link, plus five sharing links and five secrets at a time. Expired and revoked ones never count against you. quattle+ lifts every one of these limits.
If your plan lapses
Nothing is deleted the day a plan ends. Everything stays readable and downloadable, your links keep working, and only adding new files stops while you hold more than the free plan allows, and anything in the trash moves to the free plan's 30-day clock. You have 60 days to download what you need or delete down to size, and you will be reminded along the way, here and by notification.
If you are still over after 60 days, your files go to the trash, where they can be restored for 30 more days before the trash empties as usual. Resubscribing at any point before then puts everything back exactly as it was. Nothing here ever chooses which files to keep: your files are sealed, so only you can.
Payment and relevant tax are handled by Stripe