A Dead Simple CLI tool to share files from your machine, without uploading them anywhere.
One command. A link and a QR code. Any browser can download. No account. No cloud.
demo.webm
Quick start β’ Usage β’ How it works β’ Troubleshooting β’ Contributing
share0 send ./video.mp4Local http://192.168.1.42:8787/a8Fd
QR scan with your phone camera
Done link is in your clipboard
Press Ctrl+C to stop
That is the whole flow. If the devices share Wi-Fi, the bytes move over your network and never touch a third party server. Kill the command and the share dies.
If this saves you a WeTransfer upload, please star the repo. It helps more than you think.
- π Send anything. Single files, multiple files, full directories. Add
--zipto serve a directory as one archive. - β‘ Starts at once, resumes on drop. The server streams from disk with HTTP Range support. A 20 GB video never loads fully into memory.
- π± Phones just work. The recipient scans a QR code and downloads in the browser. No app, no login.
- π Public when you need it. Add
--publicand share0 opens a free tunnel, checks/healththrough it, and prints only links that answer. - π Private when you need it. Short passwords like
482-719, expiry like30m, download limits like--downloads 1. - π₯ Push the other way.
share0 receiveturns your machine into a drop box someone can upload to. - π€ Skip the link.
--to anishsends to a name instead of a long URL. Optional, and the only part that needs a server. - π©Ί Fixes its own network issues.
share0 doctorchecks ports, firewalls, IPv6, UPnP, and tunnel binaries and prints the exact fix.
Install the binary:
curl -fsSL https://raw.githubusercontent.com/Xeven777/share0/main/scripts/install.sh | bashOr download it manually from Releases and put it on your PATH.
Run from source with Bun 1.1 or newer:
bun install
bun run apps/cli/index.ts send ./file.mp4Every command here is one of these. Pick the row that matches your situation.
| Situation | Command | Needs |
|---|---|---|
| Someone is next to you | share0 send ./file |
nothing |
| Someone is far away, one file | share0 send ./file --public |
nothing, they scan a QR |
| Someone sends to you | share0 receive --public |
nothing |
| Someone is far away, often | share0 send ./file --to <name> |
a hub (below) |
Only the last one needs a server. It is worth the setup if you send to the same person regularly, and not worth it otherwise.
The default. Both devices on the same network, so no account and no internet.
share0 send ./video.mp4They scan the QR, or open the printed URL. Works for one file, several files, and whole directories.
share0 send ./photo.jpg
share0 send file1.jpg file2.jpg
share0 send ./project # browsable in the browser
share0 send ./project --zip # one archive insteadIf it does not work, run share0 doctor. It checks the port, the firewall,
and the network and prints the fix. Guest and hotel Wi-Fi usually blocks this.
Add --public. share0 opens a free tunnel to your machine, waits for it to
answer, then prints a link and a QR code. They open it in any browser. No app,
no account, nothing installed on their side.
share0 send ./video.mp4 --publicThe file stays on your disk and is served as they download, so you can stop the command the moment they are done.
Pick a provider with --tunnel: auto, ask, pinggy, localxpose,
cloudflare, localtunnel, localhost.run, zrok. The default is auto,
which tries each until a link actually works.
share0 send ./video.mp4 --public --tunnel pinggyFree tunnels end on their own. Pinggy runs about 60 minutes, zrok allows 5 GB a day, Cloudflare quick tunnels last as long as the command does.
receive turns your machine into a drop box, so they push to you instead of
you serving.
share0 receiveThat gives you a LAN URL. To accept files from another network, add --public:
share0 receive --publicThey push to the URL you print:
share0 send ./report.pdf --to http://192.168.1.10:8788
share0 send ./report.pdf --to http://xyz.trycloudflare.comFiles land in ./share-inbox. Directories are archived automatically, so
--to http://β¦ ./photos sends a zip.
The optional one. It suits people you send to often, where pasting a 60-character tunnel URL into a chat app gets old.
The receiver publishes a name, you use it:
# on their machine
share0 receive --public --as anishSend to this inbox
share0 send ./file --to anish
# on yours, from then on
share0 send ./holiday.zip --to anishNo link to copy. Worth setting up only for a name you will reuse.
If you would rather say something out loud once than tell them a name every
time, use --code and share0 prints three characters:
share0 receive --public --codeA name only resolves while the receiver is running. They refresh it every 10 minutes and the hub forgets anything unrefreshed after 30, so a closed terminal stops resolving on its own and there is nothing to clean up. A push also checks that the inbox answers before it starts, so a name whose tunnel died fails with a clear message instead of hanging.
The hub maps a name to a live URL. It never carries file bytes and knows nothing about your files.
Run one on any machine that stays switched on, then point both sides at it:
share0 hub --host 0.0.0.0 # listens on 8790
export SHARE0_HUB=http://that-machine:8790--host 0.0.0.0 is needed when the sender or receiver is on another machine.
Leave it off to test on one machine.
Or host it free as a Cloudflare Worker, which suits both sides being on different networks. From the repo root:
cd hub
npx wrangler kv namespace create SHARE0 # paste the printed id into wrangler.toml
npx wrangler deploybun run hub:deploy does the same thing and handles the directory. Wrangler
refuses to run from the repo root, because the root package.json declares
workspaces.
Set SHARE0_HUB to the resulting https://share0-hub.<you>.workers.dev on
both machines. There are no accounts. To skip the environment variable, pass
--hub <url> to send or receive instead:
share0 send ./file --to anish --hub https://your-hub.exampleThe free tier allows 1,000 writes a day. A receiver refreshing every 10 minutes spends 144, so several inboxes fit.
Two things to know. The hub has no authentication, so anyone who knows its address can claim a name, and a name is not a secret. The in-memory version loses everything on restart, which is fine because receivers re-announce within 10 minutes.
share0 send secret.pdf --password # generates one and prints it
share0 send secret.pdf --password 482719 # or pick your own
share0 send ./video.mp4 --expires 30m # stop serving after 30 minutes
share0 send ./video.mp4 --downloads 1 # allow one download, then stopBare share0 opens a menu covering all of the above:
Send files⦠share a file or folder
Send to a name⦠push to someone's inbox
Receive files⦠open an upload inbox
Publish an inbox⦠get a name others can send to
Run a hub⦠name lookup server
Discover nearby⦠find LAN shares
Active shares list running shares
Doctor diagnose network + tunnels
share0 discover # shares and inboxes on the LAN
share0 list # shares you are running
share0 stop a8Fd # stop one
share0 doctor # check ports, firewall, and tunnelsshare0 send ./file --port 9000 # if 8787 is taken
share0 send ./file --qr=off # no QR code
share0 send ./file --no-p2p # skip the WebRTC attempt
share0 send ./file --upnp # ask the router for a port mapping
share0 send ./file --detach # keep serving after you close the terminal
share0 send ./file --quiet # print URLs only, for scripts
share0 send ./file --json # same, as JSONYour machine serves. The browser downloads. There is no database and no account. Exit the CLI and the share ends.
YOU THEM
βββββββββββββββββββββββ ββββββββββββ
β share0 send ./f β β β
β β ββ LAN βββββββββββΆβ browser β same Wi-Fi
β serves ./f from β β β no internet
β disk, streams it β ββ tunnel βββββββΆβ browser β far away
β β β β
βββββββββββββββββββββββ ββββββββββββ
your file never leaves your disk
Two shapes cover everything:
You serve, they download. send starts an HTTP server on your machine
and prints a URL. receive does the same but accepts uploads instead. This is
the whole tool.
A name stands in for the URL. Only for --to <name>. The receiver
publishes anish -> https://xyz.trycloudflare.com on a hub, and the sender
looks that up. A lookup, not a transfer; the bytes still move directly.
| Transport | When share0 uses it | How it moves bytes |
|---|---|---|
| LAN | Same Wi-Fi or cable, the default | Direct HTTP to your LAN IP |
| IPv6 | Both sides have direct IPv6 | Direct HTTP to your IPv6 address |
| UPnP | You pass --upnp |
Short lived router port mapping, removed on exit |
| WebRTC P2P | Offered by default, uses UDP 52000 to 52100 | Browser DataChannel with STUN for NAT traversal |
| Tunnel | You pass --public |
Free tunnel adapter, link checked with /health before display |
share0 picks the first one that works and never reports a link it has not probed. Tunnel adapters include Pinggy, LocalXpose, Cloudflare quick tunnel, LocalTunnel, localhost.run, and zrok.
| Task | Cloud upload tools | share0 |
|---|---|---|
| Send a 10 GB video | Wait for upload, then wait for download, pay for storage | Stream it from disk, start at once, pay nothing |
| Share on home Wi-Fi | Still round trips through a data center | Moves over LAN at LAN speed |
| Share with a phone | Ask them to install an app and make an account | They scan a QR code in the camera app |
| End access | Hope the expiry setting worked | Press Ctrl+C, the server stops, the URL dies |
"X" is not available right now
The name did not resolve on the hub, so the receiver is not publishing.
# on their machine, check it is actually running
share0 receive --public --as anishIf that works, the two machines disagree about the hub. Compare
echo $SHARE0_HUB on both. It must be the same address and reachable from
both.
The name resolves but the inbox does not answer
The hub entry is alive but the receiver's tunnel died, which can happen up to 30 minutes after a crash. share0 checks the inbox before pushing and says so instead of hanging.
Ask them to run share0 receive --public --as anish again, or push to the
printed URL while their tunnel is still up.
The hub is not reachable
share0 hub binds to 127.0.0.1 by default, which only works for testing on
one machine. Use --host 0.0.0.0 when another machine needs it:
share0 hub --host 0.0.0.0Behind a router you also need the port forwarded, or a tunnel in front of it.
Phone cannot open the LAN URL on the same Wi-Fi
Work through this list in order.
- Confirm the phone uses Wi-Fi, not mobile data, with no VPN active.
- Skip guest, hotel, and office networks. Many block device to device traffic.
- Check the laptop firewall.
share0 doctorflags a blocked port and prints the fix. - The CLI prints every LAN IP it finds. Try each one.
- If you switched networks after starting, restart the share. The CLI warns when your IP changes.
Fix a blocked port 8787:
sudo ufw allow 8787/tcpsudo firewall-cmd --add-port=8787/tcp --permanent
sudo firewall-cmd --reloadPinggy shows a warning page
That is Pinggy free link screening. The recipient taps Enter site once and the download page loads. Curl clients skip it.
Tunnel link returns 404
Some providers hand out a URL before edge routing exists. share0 requests /health through each tunnel and prints only links that answer. Dead links never print.
SSH asks for a password for Pinggy
Press Enter. Empty input is the documented answer for free tunnels.
The P2P button does nothing
The browser must reach the signaling URL first. Symmetric NAT often blocks UDP hole punching. Plain download still works. To open the P2P port range on ufw:
sudo ufw allow 52000:52100/udp- LAN sharing with QR codes and clipboard copy
- Streaming with HTTP Range resume
- Passwords, expiry, download limits, ZIP mode
- Free tunnel adapters with verified links
- UPnP port mapping and WebRTC P2P offer
- Receive mode and LAN discovery
- Inbox mode: hub handles,
receive --public,send --to <name> - Sender-minted 3-character codes (
send --pair) for one-off shares - PWA receiver and installable share pages
- Optional end-to-end encryption
- Native desktop and mobile wrappers
Have an idea? Open an issue or send a PR.
Contributions are welcome. Big feature? Open an issue first so we agree on scope.
git clone https://github.com/Xeven777/share0.git
cd share0
bun install
bun test
bun run apps/cli/index.ts doctorPlease run bun test before pushing. Keep transport vendors behind the adapter interface in packages/transport, and keep packages/core free of vendor imports.
Built with Bun. Tunneling via Pinggy, LocalXpose, Cloudflare, LocalTunnel, localhost.run, and zrok. Inspired by the original share-cli idea of a tiny local server plus a tunnel.
MIT. See LICENSE.
