dropbin
upload a file, get a 4-digit pin. one worker, one bucket, and nothing that lists what's inside.
what it is
there are two things you can do — upload and download — and the whole tool is built around only those two. uploading gets you a pin; anyone with the pin and the password can take the thing back out, in a browser or in a terminal. nothing lists what is stored, anywhere. the only number either front end reports is how much room is left.
a pin holds exactly one thing. upload a file and the pin holds that file. upload a folder and it's zipped on your machine first, so the pin holds one zip — and downloading gives you that zip straight back.
dropbin files.dharun.dev ---------------------------------------------------------------------- 1 upload a file or a folder 2 download with a pin ---------------------------------------------------------------------- [#.....................] 9.71 GB free of 10.0 GB 6 pin(s) stored 1 or 2 to choose, q to quit
how it fits together
one worker holds everything: the web dialogs, the api, and — as plain text with the host swapped in on the way out — the two shell clients and the two installers. r2 holds the bytes. there's no database, no queue, and no build step.
the pin flow
a pin is 4 digits by default — 9000 of them, checked for collisions before one is handed out, and widened to 6 digits if they ever all fill up. a custom pin is anything up to 64 characters that isn't a slash, a control character, or a reserved word.
setup · deploy your own
four commands, about five minutes. you need node 18 or newer, and r2 enabled on the cloudflare account.
-
install and log in
npm install npx wrangler login # opens a browser, once -
create the bucket, then deploy
npx wrangler r2 bucket create dropbin # needs R2 on the account npx wrangler deploythat prints a
https://dropbin.<you>.workers.devurl, which already works. -
point it at your own domain
[[routes]] pattern = "files.example.com" custom_domain = true
deploy again. the zone has to be on the same cloudflare account; wrangler makes the dns record itself.
-
set the password
npx wrangler secret put ACCESS_PASSWORD # takes effect at once npx wrangler secret list # [] means it is still changeme
it's
changemeuntil you set one — and that fallback lives in the code, not inwrangler.toml, which deliberately keeps the password out of[vars]. changing it invalidates every browser cookie and every outstanding share link, since those are signed with the password as the key.
setup · install the client
anyone who uses this more than once should install the tool. one command, then db is on the path.
curl -fsSL https://files.dharun.dev/install.sh | sh
npm i -g dropbin # or, anywhere npm is
irm https://files.dharun.dev/install.ps1 | iex
all three land the same thing: a zero-dependency node package (18 or newer) installing db, with dropbin as a longer alias. fetched from a deployment, an installer bakes that host into the shim it writes, so the db it leaves behind already points at your instance — DROP_HOST still overrides it.
| installer | files | the db shim |
|---|---|---|
| install.sh | ~/.local/share/dropbin | ~/.local/bin |
| install.ps1 | %LOCALAPPDATA%\dropbin\app | …\dropbin\bin, added to PATH |
the db command
db up ./holiday.png # a file, random pin db up ./holiday.png photos # a file, your own pin db up ./project # a folder — zipped here, then sent db get 4821 # save it in this folder db view 4821 # print it here, if it is text db qr 4821 # a code to point a phone at db open 4821 # look at it in a browser db rm 4821 # throw the pin away db free # how much room is left
the password is asked for on every run and stored nowhere — there's no config file and no token cache. DROP_PASS skips the prompt for scripts and ci, DROP_HOST points db at another deployment. db up and db get print the one useful line on stdout and everything else on stderr, so they pipe cleanly.
nothing to install
for a one-off, or a machine without node: the worker hands you a shell client, already pointed at your host.
bash <(curl -s https://files.dharun.dev/cli)
irm https://files.dharun.dev/cli.ps1 -OutFile drop.ps1 ; ./drop.ps1 # windows
or skip the clients entirely and use curl:
export PASS=changeme curl -T ./holiday.png "https://:[email protected]/up/" # upload curl -OJ "https://files.dharun.dev/4821?p=$PASS" # download curl -X DELETE "https://files.dharun.dev/4821?p=$PASS" # throw it away
curl -T only appends the local filename when the url ends in / and has no query string — hence the basic-auth form above. in powershell write curl.exe; bare curl is an alias for Invoke-WebRequest.
the api
| method | path | what it does |
|---|---|---|
| GET | / | the two options and the meter |
| GET | /PIN | a browser looks at it, everything else downloads it |
| GET | /PIN?info=1 | what the pin holds, without fetching it |
| GET | /PIN?k=EXP.SIG | a share link: that one pin, read-only, no password |
| PUT | /up | raw-body upload — X-Name, X-Pin |
| POST | /up | multipart: one file field, optional pin |
| DELETE | /PIN | throw the pin away |
| GET | /cli · /cli.ps1 | the clients, pre-pointed at this host |
share links
db open and db qr hand a link to something that can't be asked for a password — a browser, or a phone pointed at a code — so they sign one instead of just linking it. ?k= is an expiry and an hmac of the pin, keyed on the password: nothing is stored anywhere, it opens exactly that one pin, it's read-only, and it lasts a week.
Content-Security-Policy: sandbox and nosniff, so an uploaded page can't act on the originQUOTA_GB counts down from r2's free 10 gb; it isn't an enforced capmit licensed. deploys, github releases and npm publishes are run by hand, not by a workflow. the threat model and hardening notes are in security.md.
built by dharun ashokkumar · all projects