EP
Own work
Personal project, from idea to release

ConsignaFile sharing

File sharing between the devices on one Wi-Fi network. One program runs on a computer, and a phone joins by scanning a QR code: nothing to install, no accounts, and nothing leaves the network.

ClientPersonal project
Stack
GoReact 19TypeScriptVitetusServer-Sent EventsVitestPlaywright

What the problem was

I edit videos of my motorcycle rides on a laptop and watch them on my phone. Without a Mac there is no AirDrop, so every video went across on a USB cable. Consigna is one Go binary with the interface built in: start it on the laptop, scan the code with the phone and drop the file in. Any device with a browser can join, on purpose only over the local network. There is no internet, no cloud and no account, files expire after 24 hours, and everything is deleted when it stops. I built it in about a week, with Claude helping on the networking and the design. The CRT look is my idea, taken from Cyberpunk's interface.

The host view on the laptop: a QR code to scan, the join address, the session code FPK5HP with a button for a new one, and a list of what to check if the phone can't connect
The host view
The host view

What I did

Uploads that carry on from the last byte

Uploads speak tus, a resumable upload protocol. I wrote the server myself because the standard one brings in cloud-storage SDKs. If a phone locks or the Wi-Fi drops, the upload pauses and resumes where it stopped. A reconnecting phone takes over from its own dead request, and a finished upload is remembered, so a phone that missed the last reply doesn't start again. Each upload reserves its full size up front so two at once can't overfill the tray. Before the release I closed a hole where a length near the integer limit made that sum overflow and slip past the cap.

No timeout on a transfer that takes hours

A whole-request timeout would cut off a large video halfway, so there isn't one. Instead every read and write pushes a 60-second idle deadline forward: a stalled connection is dropped, and a slow, healthy one keeps going. Downloads support byte ranges, and several files download as a ZIP that streams as it is read, without waiting for an archive to be built.

One message puts a sleeping phone right

Live updates go out over Server-Sent Events and always carry the whole state rather than what changed. A phone that slept through ten changes is correct again after one message, with no replay logic. Each listener has room for one pending wake-up, so a burst of changes becomes one update and a slow phone never holds up the server.

Safe without a password

A guest joins with a six-character code, written without characters that look alike, and gets a token in a cookie that the server stores only as a hash. Failed joins are rate-limited. The host is recognised by being on the same machine, so the server checks the Host header against DNS rebinding and the request's origin against forgery, and downloads open sandboxed. Before the release I closed a hole where any website open on the laptop could create devices through an image tag pointing at it.

How it stays fast

  • One 8.5 MB binary for Linux, macOS and Windows, with the interface and fonts inside, so it runs with no internet at all
  • The interface ships 103 KB of gzipped JavaScript, and the CRT effects switch off for reduced motion or a calm mode
  • Go tests under the race detector with a fuzzed file-name sanitiser in CI, and Playwright driving the real binary with a guest joining over the network

How it turned out

Released as v0.1.0 in October 2026, open source under MIT. I use it every day to get ride videos from the laptop to my phone. HTTPS and use outside the home network are designed but deliberately not built: staying on a network I trust is what keeps it simple to secure.

github.com/EymanEP/consigna

Selected media

On desktop
The tray on a laptop: a drop zone on the left, four shared files on the right with size, sender and a GET button each, and the tray at 15.6 MB of 10 GB
The tray
The tray
On a phone
The phone just after joining: the CONSIGNA banner reading local session, no internet, a Tap to add files area and the link to bring in another device
Joined, nothing shared yet
Joined, nothing shared yet
The tray on a phone: four files, two of them marked new, each with a download button, above the link to bring in another device
The tray on a phone
The tray on a phone