Open it on two devices. One picks files and gets a 6-digit code and a QR. The other enters the code or scans it. The files stream straight between the two over a WebRTC data channel.
Works iPhone to Android to Windows to Mac to Linux, across networks. The two devices don't need to be on the same Wi-Fi.
Device A (sender) ◄──── WebRTC data channel ────► Device B (receiver)
│ │
└────── signaling relay (6-digit code) ──────┘
handshake only
- Sender picks files, gets a 6-digit code and a QR.
- Receiver enters or scans it. The WebRTC handshake goes through the relay.
- Sender sends file metadata over the data channel; the receiver accepts.
- Files stream in 16 KiB chunks, device to device.
- Channel closes, signaling room is abandoned.
Files are read with file.stream() and sent in 16 KiB chunks, so memory stays
flat no matter how big the file is. There is no size cap.
It carries the 6-digit code and the WebRTC SDP/ICE exchange between two peers. A few KB, once, at the start. It never sees file bytes, file names, or sizes, and nothing is stored on it. Rooms are ephemeral and destroy themselves on disconnect.
The 6-digit code gives a million possibilities against a room that exists for the length of one handshake. Transfers themselves are encrypted by WebRTC's DTLS, which is part of the protocol rather than something bolted on.
Needs bun.
bun install
bun run devThe signaling server starts inside the Next.js dev process via
src/instrumentation.ts, so there's no second process to run. Open two tabs,
send from one, receive in the other.
NAT traversal uses public STUN servers. On a restrictive network (symmetric NAT)
you'll need a TURN server added to ICE_SERVERS in
src/lib/filebeam/signaling.ts.
Next.js 16 (App Router), TypeScript, Tailwind CSS 4, shadcn/ui,
RTCPeerConnection and a data channel for the transfer, and socket.io for
signaling.
Two pieces: the frontend on Vercel, and the signaling relay somewhere it can hold a socket. Files always go peer to peer regardless.
Signaling relay. It's a ~130-line socket.io app in
mini-services/signaling/. On Render: New → Web Service →
Public Git repository → https://github.com/JeffreyHamilton6399/filebeam, with
root directory mini-services/signaling, build npm install, start npm start.
You get back a URL like https://filebeam-signaling.onrender.com.
It's stateless, so a free tier is fine. Render's free tier sleeps after 15 minutes idle, which costs a one-second cold start on the first connection.
Frontend. Import the repo on Vercel, set NEXT_PUBLIC_SIGNALING_URL to the
relay URL, deploy.
Local development needs none of this.
Files go directly between devices. The relay sees only the code and the
handshake. Nothing is stored on any server, there's no analytics, and
localStorage holds a theme preference and nothing else.
PRs welcome against main. Run bun run lint first.
MIT © Jeffrey Hamilton