Skip to content

edaywalid/qrshare

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

qrshare

Move a file, a folder, some text, or a URL between your laptop and your phone over wifi.
Scan a QR code in your terminal, and the transfer happens in the phone's browser.
No app to install, no account, no cloud.

qrshare: run the command, scan the QR, files land on your laptop

CI Release License Go 1.25+


$ qrshare report.pdf     # phone scans, phone downloads the file
$ qrshare ./photos       # phone downloads the folder as a zip
$ qrshare                # phone uploads files back to your laptop
$ qrshare "some text"    # phone shows the text with a copy button
$ echo $API_KEY | qrshare

Both devices sit on the same wifi, so the phone reaches your laptop's LAN address directly. Nothing leaves the local network. There is no relay, no NAT traversal, and no third party in the middle.

Why you might want it

  • Nothing to install on the phone. The phone only needs a camera and a browser, which it already has.
  • No cloud, no account. Bytes go straight from one device to the other over your wifi.
  • Four ways to move things. Send a file, send a folder as a zip, receive uploads from the phone, or share text and links.
  • It goes both ways. Push a file to your phone, or pull photos off it, from the same tool.
  • Big files are fine. Transfers stream, so a 2 GB video never loads into memory on either side.
  • The URL is a secret. Every share lives behind an unguessable token. Anyone who did not scan the QR gets a plain 404.
  • One self-contained binary. Written in Go, no runtime, no dependencies to chase.

Install

Homebrew (macOS and Linux)

brew install edaywalid/tap/qrshare

This also installs shell completions for you.

Go

go install github.com/edaywalid/qrshare/cmd/qrshare@latest

Prebuilt binary

Grab the archive for your platform from the releases page, unpack it, and move qrshare onto your PATH.

From source

git clone https://github.com/edaywalid/qrshare
cd qrshare
make build      # binary lands in ./bin/qrshare

Go 1.25 or newer is required to build.

Usage

Run one of the four modes, then point your phone's camera at the QR. The URL prints under the code as a fallback in case the QR will not scan.

Send a file

qrshare report.pdf

The file downloads on the phone with its original name. Large files stream from disk, so memory use stays flat no matter the size. The server shuts down once the download finishes, unless you pass --keep-alive.

Send a folder

qrshare ./trip-photos

The folder is zipped on the fly and streamed to the phone as a single archive. Nothing is written to a temp file first.

Receive files from your phone

qrshare
qrshare --dir ~/Downloads --keep-alive

Scanning opens an upload page. Files land in the current directory, or wherever --dir points, and an existing file is never overwritten. The phone shows a live progress bar while the upload runs.

Share text or a URL

qrshare "https://long.example.com/link?token=abc"
echo secret | qrshare

The phone shows the text with a one-tap copy button. When the text is a link, it also gets an "Open link" button.

The pages the phone sees

The served pages match the qrshare look: a text-mode blue with cyan and yellow, built to be fast and readable on a phone held one-handed. The upload page also has a text box that sends a note or link straight back to your laptop's clipboard.

the qrshare upload and text pages as they appear on the phone

How it works

   your laptop                            your phone
  ┌────────────┐    same wifi (LAN)     ┌────────────┐
  │  qrshare   │ ◀───────────────────▶ │  browser   │
  │  :49213    │    http + secret token │  + camera  │
  └────────────┘                        └────────────┘
        │                                     ▲
        └──────────  prints a QR  ────────────┘
                     phone scans it

qrshare finds your real wifi address, binds a port, and mints a random token that becomes part of the URL. It prints that URL as a QR code in the terminal. Your phone scans it, opens the URL over plain HTTP on the LAN, and the transfer runs directly between the two devices.

Mode detection

qrshare picks the mode for you based on the argument:

  • an existing file path sends the file
  • an existing directory sends a zip
  • piped stdin is shared as text
  • any other argument is shared as text
  • no argument at all receives uploads

Force a mode with --send, --receive, or --text when the guess would be wrong. An argument that looks like a path but does not exist is treated as a likely typo and errors out, rather than being shared as text by accident.

Flags

--send            treat the argument as a path to send
--receive         receive uploads from the phone
--text            treat the argument or stdin as literal text
--port N          listen port (default: auto-pick a free port)
--host ADDR       override the resolved LAN IP
--dir PATH        where uploads are saved (default: current dir)
--max-size SIZE   reject uploads larger than SIZE, e.g. 500M, 2G
--keep-alive      stay up for multiple transfers
--timeout DUR     idle shutdown after DUR (default: 10m)
--qr-only         print only the QR code
--url-only        print only the URL, no QR
-q, --quiet       suppress status output
--version
--help

Shell completions

qrshare completion bash > /usr/local/etc/bash_completion.d/qrshare
qrshare completion zsh  > "${fpath[1]}/_qrshare"
qrshare completion fish > ~/.config/fish/completions/qrshare.fish

The Homebrew install wires these up automatically.

Security

Transfers are plain HTTP over your LAN, guarded by an unguessable random token in every URL. Anyone who does not scan the QR, or know the exact URL, gets a 404 with no hint that a server is even running. Uploads are confined to the target directory with sanitized filenames and no overwrite. The server accepts connections only while the process is running. Because this is a same-wifi tool, there is no cloud endpoint and no account to secure.

FAQ

macOS asks to allow incoming connections. Expected. The first time qrshare binds a port, the macOS firewall prompts you. Click Allow. This is not an error.

The phone scans the QR but nothing loads. You are most likely on a guest or "client isolation" network, which blocks devices from reaching each other even on the same wifi. Switch both devices to a normal home network or a personal hotspot.

The QR shows an address my phone cannot reach. If you are on a full-tunnel VPN, your laptop's route to the world runs through the VPN while your phone is on plain wifi. qrshare prefers your real LAN interface and warns when only a VPN or tunnel interface is available. Disconnect the VPN, or pass --host with your wifi IP.

Can I use this across the internet? No, by design. qrshare is LAN-only, with no relay, no NAT traversal, and no account. The QR is the whole discovery mechanism.

Development

make build      # build ./bin/qrshare
make test       # run the test suite
go test ./...   # same thing, directly

The code is split into small packages under internal/: netaddr resolves the LAN address, qr renders the terminal QR, transfer holds the HTTP handlers, session ties them together, and clipboard handles the reverse-text path. Each package is tested on its own with net/http/httptest, so no real phone or network is needed to run the suite.

License

MIT. See LICENSE.

About

One command to move files, folders, text, or URLs between your laptop and phone over the local network. No app, no account, no cloud.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages