gfile: a WebRTC file transfer that leaves the bytes on the two peers
Direct file transfer over WebRTC
At a glance
- What is it?
- gfile is a Go command line tool that transfers a file directly between two computers over a WebRTC peer connection, using a STUN server only to help the peers find each other. It suits people who want to move a file without a hosting account or a third party server, and it depends on the two peers being able to reach each other over the network.
- Who is it for?
- gfile suits someone who needs to hand a single file to another person without uploading it to a service, and it does not suit a workflow that needs a durable link, an audit trail, or a transfer that survives both peers being online at the same time, since the connection is established live between two processes.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 86 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two peers, one STUN server, no server holding the file
gfile is a file exchange tool built on WebRTC, and the README states the goal in plain terms: it lets you share a file directly between two computers, without the need of a third party. The mechanism is the interesting part. The README is explicit that the STUN server is only used to help the two clients find each other through NAT, and that the data transferred with gfile does not transit through it.
That distinction matters for anyone with a sensitive document or a large archive. There is no upload step, no storage bucket, no retention policy at a hosting provider, and the file exists on the two machines that matter. The cost is that the two parties have to be present at the same time and the network has to allow a direct path between them.
The tool is written in Go, the repository is MIT licensed, and the top level layout shows a conventional Go project with cmd, internal, and pkg directories, a main.go entry point, a PROTOCOL.md describing the wire format and session protocol, and a Justfile for the task runner. The dependencies confirm the design: pion/webrtc and pion/ice carry the peer connection, pion/sctp handles the data channel framing, and rs/zerolog provides the logging. The README also notes the project is still in its early stage.
A compact encoded SDP is the whole handshake
There is no signalling server in this design, and no account to create. The exchange is done by hand. The sender runs a command, a compact encoded SDP appears in the terminal, and the operator sends that blob to the remote client by whatever channel they already trust, a chat message, an email, a piece of paper. The remote client produces its own SDP, sends it back, and the transfer starts.
The README gives the sender side as a single command:
gfile send --file filenameWhat follows is a short list of instructions: run the command, send the compact encoded SDP to the remote client, follow the instructions to send the client's SDP back to the process, and the file transfer should start.
The receiver pipes the SDP it received into the process and names the output file:
echo "$SDP" | gfile receive -o filenameHere too the steps are manual: pipe the other client's SDP to gfile, send the compact encoded SDP that appears back to the remote client, and the transfer starts. So the procedure is symmetric, and both sides have to be paying attention at the same time. The README links the SDP format to the RFC 4566 specification and points at PROTOCOL.md for the wire format and session protocol details, which is where a reader looking for the actual framing would go.
STUN is for discovery, and the --stun flag is what makes LAN use work
The default STUN configuration is Google's public server, stun.l.google.com:19302, and the README documents a --stun flag to override it. Three cases are shown.
# Use a specific STUN server
gfile --stun stun.cloudflare.com:3478 send -f filename
# Use multiple STUN servers (comma-separated)
gfile --stun stun.l.google.com:19302,stun.cloudflare.com:3478 send -f filename
gfile --stun="" send -f filenameThe third case is the interesting one. With STUN disabled the tool gathers only host candidates and mDNS candidates, which is enough for two machines on the same local network and removes the dependency on an external service entirely. Passing several servers comma-separated is the middle ground, useful when one public STUN server is unreachable or blocked on a given network.
Note that this is a per command flag, placed before the subcommand, so the setting applies to that invocation rather than to a configuration file. The README does not document a config file, an environment variable for the STUN server, or a persistent profile, so a repeated workflow means retyping the flag.
mDNS keeps the LAN address out of the SDP you paste around
By default gfile advertises host ICE candidates as .local mDNS hostnames instead of raw LAN IP addresses, and the README says this matches modern browser behavior. The stated benefit is that it keeps the internal network topology out of the SDP that you paste around, and it is enough for a LAN transfer even with STUN disabled.
That is a small but sensible default. The SDP blob is the artefact that travels over chat or email, and a blob containing a plain 192.168 address tells more about the sender's network than the transfer requires. The mDNS name resolves on the local network, so both machines can find each other without the address ever being written down.
The escape hatch is a --mdns=false flag, and the README tells you when to use it: disable it if a peer on the other side cannot resolve mDNS. That situation arises when the two machines are not on the same broadcast domain, since mDNS resolution depends on local name resolution rather than on a directory service.
gfile --mdns=false send -f filenameThe pion/mdns package is in the dependency list as an indirect dependency, which is consistent with this being a default behaviour of the ICE layer rather than a separate subsystem.
Multiple parallel peer connections trade peers for bandwidth
The sender command accepts a --connections flag with a range of 1 to 16, and the default is 1. The README states that higher values can improve throughput on high latency or high bandwidth links, and refers the reader to the multi-PC mode section of PROTOCOL.md.
This is a real design trade-off, and the tool lets the operator make it per transfer. A single data channel is simpler and behaves better on a clean path, while several parallel connections can keep aggregate throughput up when a single flow is limited by latency or by a middlebox. The cost of running several is more handshakes, more state to track on each side, and more connections that can fail independently, so a partially completed multi connection transfer is a case the operator has to reason about.
The compression library in the dependency list, klauspost/compress, also suggests the payload is compressed on the wire, which would matter for a file that compresses well and would cost CPU for one that does not. The README does not document a switch to disable compression, so the behaviour is not something the user controls today.
Bench mode measures the link and is still labelled an experiment
There is a third command alongside send and receive. The bench command measures the network speed between two clients, one sender and one receiver, and the README says the SDP exchange works the same way as in send and receive. That means the procedure is familiar: run the sender side, exchange the two SDP blobs, and the measurement runs.
# Run as 'sender'
gfile bench -s
# Run as 'receiver'
echo "$SDP" | gfile benchThe README qualifies this directly, with the sentence "This feature is still an experiment." That matters for how a measurement should be treated. The result describes the throughput of one particular WebRTC path between two particular peers at one moment, with the same STUN and candidate discovery constraints as a file transfer, so it is a useful number for sizing a transfer and a poor basis for a network capacity plan.
The debug output is controlled separately, through the GFILE_LOG environment variable. The README gives the most verbose level as an export of GFILE_LOG set to TRACE, and points at the setupLogger function in main.go for the full list of levels.
The logging library in the build list is rs/zerolog, so the trace output is structured rather than plain text.
Release history: one tag in 2026 after a long gap
The release list shows v0.2.0 published on 2026-04-20, and before that v0.1.0 from 2019-04-08 and v0.0.2 from 2019-03-13. The last push was on 2026-08-01, which is after the v0.2.0 tag, so the master branch carries work that is not in any tagged release.
For an adopter, that has two consequences. The first is that the version numbers carry almost no information about compatibility, since 0.0.2 and 0.1.0 shipped within a month of each other in 2019 and the jump to 0.2.0 took another seven years. The second is that pinning to a release means taking a snapshot from April 2026, while running from a built master gives you newer code and no version marker to record.
The README itself states the project is still in its early stage, and that warning is consistent with the versioning. The repository does include a CI workflow badge, a Go Report Card badge, a golangci configuration for linting, and a.goreleaser.yml, so the build and release machinery exists, and the code is written against Go 1.26.2 with a Go Report Card workflow over it.
The licence is MIT, which permits commercial use, modification and redistribution with the notice retained. The README credits the original author and acknowledges help with pion/webrtc and pion/sctp, which is where the hard part of the implementation actually lives.
Editorial conclusion
gfile suits someone who needs to hand a single file to another person without uploading it to a service, and it does not suit a workflow that needs a durable link, an audit trail, or a transfer that survives both peers being online at the same time, since the connection is established live between two processes. Before using it, confirm that the two machines can reach each other over the network you are on, because a symmetric NAT on either side can leave the peer connection with no candidate path, and check the multi connection setting, since the README notes that higher --connections values can improve throughput on high latency links.
Frequently asked questions
Does the file pass through the STUN server when I use gfile?
No. The README states the STUN server is only used to help the two clients find each other through NAT, and that the data transferred with gfile does not transit through it. The transferred bytes travel over a direct peer connection between the two machines.
How do I send a file with gfile?
Run gfile send --file filename, send the compact encoded SDP that appears to the remote client, then paste their SDP back into the running process. On the other side, pipe the received SDP into gfile receive -o filename and send the SDP it prints back.
Can I use gfile on a local network without an internet connection?
Yes, with STUN disabled. Passing --stun="" leaves only host and mDNS candidates, and the README says that is enough for a LAN transfer. The default mDNS advertisement of .local hostnames applies, and --mdns=false is there for a peer that cannot resolve them.
What does the --connections flag do in gfile?
It opens N parallel peer connections, with a range of 1 to 16 and a default of 1. The README says higher values can improve throughput on high latency or high bandwidth links, and points to the multi-PC mode section of PROTOCOL.md.
How do I get more verbose output from gfile?
Set the GFILE_LOG environment variable, for example export GFILE_LOG="TRACE". The README points at the setupLogger function in main.go for more information about the available levels.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/antonito-gfile)Was this useful?