Every connection you can reach, bonded into one.
A venue's flaky WiFi, a wired line, three people's phones: Bond sends every packet down whichever uplink gets it there first, and when one of them dies the rest keep going without the connection dropping. Built for the live stream that has to hold. Self-hosted end to end.
macOS, or Linux with Homebrew. Archives for Linux on x86, ARM64 and 32-bit ARM (a Raspberry Pi) are on the releases page.
Why Bond
For the connection that has to hold.
Every uplink carries part of the traffic. There are no speeds to enter and nothing to tune: Bond measures each path's round trip, loss and delivery, and places every packet where it will arrive first.
7.83 Mbit/s bonded
Faster together
Two interfaces shaped to 6 and 3 Mbit/s managed 5.20 and 2.62 on their own. Bonded, an upload to the Internet ran at 7.83.
~10 ms to rejoin
Holds when an uplink dies
Cut the faster of two links three seconds into an upload and the upload still completes. An uplink that drops and comes back is carrying traffic again in about 10 ms; an aggregator restarted under a running hub, in about a second.
Any IP traffic
Nothing to change in your apps
The splitting happens below the WireGuard tunnel, so everything that runs over IP works: TCP, UDP, ICMP, SRT, RTMP, WebRTC, games, other VPNs.
How it works
Three roles, one binary.
bondd runs as whichever part you need. A join code ties them into a mesh; it is a secret that never crosses the network, and it keys the encryption of the join itself.
Aggregator
Any machine with a public address: a VPS, a cloud VM, or a server at home, where it asks the router to forward its port with UPnP. Your traffic rejoins the Internet here.
Hub
The machine you want better Internet on — a laptop, a Raspberry Pi, a router. It opens a path through every uplink it has and sends each packet down whichever gets it there soonest.
Relay
Lends its uplink to hubs nearby. A phone in Share mode forwards a hub's already-encrypted packets over its cellular connection. Hubs find relays on the LAN by themselves.
Real WireGuard, with Bond underneath
Inside is unmodified wireguard-go. Bond replaces only its transport: each encrypted datagram goes out on one or more paths, and what arrives is put back in order as if it came from a single peer. Paths come and go beneath a WireGuard session that never notices.
Relays and anything else along a path see ciphertext only. How Bond works covers the wire format, the scheduler and the known limits.
Quick start
Bonded in a few minutes.
You need a server with UDP and TCP port 7777 open, and the machine you want to speed up.
Start the aggregator
From a clone of the repository, push.sh cross-compiles, installs a systemd service on the server and prints a join code and QR. Terraform for AWS, Google Cloud and Azure is in deploy/, and deploy/docker/ runs it as an unprivileged container.
Join a hub
All of the machine's traffic now goes through the bond. Without root, add --userspace: nothing is captured, and applications use the SOCKS5 proxy it opens on 127.0.0.1:1080.
Add uplinks — nothing to configure
Turn on a phone's USB tethering or hotspot and it shows up as a path within a couple of seconds. Run a relay on anything on the same LAN with its own connection, or turn on Share my connection in the phone app.
Watch it
The same view, live, is a dashboard at http://127.0.0.1:7780 on any machine running bondd. On the aggregator it shows the join QR and every device.
Keep it running
Installs a systemd unit on Linux or a launchd daemon on macOS. With --join it joins on the spot, so the code never ends up in a unit file.
Control
Say how each path may be used. Bond does the rest.
Set a policy with bond path <name> <policy> or from the dashboard; the choice is remembered. With little traffic Bond just uses the fastest path. As a path fills, its queue grows and traffic spills onto the next, so under load each path ends up carrying what it can.
| Policy | Meaning |
|---|---|
| primary | Preferred while healthy. The interface your machine was already using starts as this. |
| bonded | Used alongside whenever that gets a packet there sooner. Tethers and relays start as this. |
| backup | Idle until nothing else is healthy. For metered data you only want spent in an emergency. |
| off | Never used. |
Priority traffic
A device, a port or a protocol can be priority. Its packets go out on the two best paths at once and are never held back to be put in order, so a path stalling or dying costs them nothing. Other traffic makes room. Replies are prioritised too, without configuring the aggregator.
Everything else
Fits the setup you already have.
Share it with the LAN
sudo bondd hub --gateway turns the hub into a router. Point an encoder, cameras or a console at it and they are on the bond with no software of their own. On Linux, priority can be per device by address or MAC.
Through networks that block UDP
If an uplink's UDP path stays dead for eight seconds, the hub opens a WebSocket path over it instead, and drops it once UDP is back. Behind a Cloudflare Tunnel the fallback looks like ordinary HTTPS.
Phone apps
Android and iOS apps embed the same Go core: Bond this phone uses WiFi and cellular together, Share my connection makes the phone a relay. Build them from source; neither has run its tunnel on a real phone yet.
Stacks with Tailscale
Run Tailscale on the hub as usual and your tailnet rides the bond, surviving an uplink dying. Or advertise the mesh's routes from a kernel-mode aggregator to reach every bonded device from your tailnet.
Runs anywhere Linux does
Terraform for AWS, Google Cloud and Azure, a Docker image, or push.sh to any server. Big clouds bill traffic out at $0.08–0.12/GB; a 6 Mbit/s stream is about 2.7 GB an hour, so a VPS with a few TB included costs far less.
Yours, end to end
No account, no service in the middle. The aggregator is a machine you run, the code is AGPL-3.0, and IPv6 to the Internet is held off while bonded so it cannot quietly route around the tunnel.
Security
Built so a path can't be turned against you.
- WireGuard between hub and aggregator. Relays and every network on the way see ciphertext only.
- Join codes are secrets that never travel. Mint a single-use one with an expiry; remove a device with
bond kick. - Paths are authenticated per session. Nobody else can redirect one, and a path counts as up only after a round trip replayed traffic cannot fake.
- Relays forward narrowly. Only to their own mesh's aggregator, only for local addresses, only what looks like a hub's datagram.
- The aggregator guards its own host. It refuses to forward to its loopback and link-local space, the cloud metadata service included.
- The status API stays local. It listens on loopback and rejects requests a web page could forge.
Tested
Measured, not estimated.
Every number on this page came from a run. What hasn't been run yet is listed too.
| Test | Result |
|---|---|
| Linux, two links shaped to 6 + 3 Mbit/s, upload | 7.83 Mbit/s bonded, against 5.20 and 2.62 alone |
| Faster link cut 3 s into an upload | Upload completes; the link rejoins 2 s after it returns |
| Simulated 5 + 3 + 4 Mbit/s paths, 10–40 ms delays, TCP | 8.3 Mbit/s bonded, against 4.5 on the best path, in order |
| 25 % loss on both of two paths | Priority traffic 92 % delivered, ordinary 76 % |
| Whole container routed through the bond | HTTPS, ping and raw UDP all work |
| Android app as a relay for a desktop hub | Works; nothing leaks over WiFi while mobile data is off |
| Aggregator restarted under a running hub | Traffic resumes in about 1 s |
Not tested yet
It needs hardware or accounts this was not built with:
- Either phone app's tunnel on a real phone
- A hub behind real carrier NAT, with real phones as relays
- macOS kernel and gateway modes (the Linux equivalents are tested)
- The cloud Terraform applied, the Docker image, the Cloudflare Tunnel setup
- UPnP on a real router, rather than a simulated one
Not built yet: IPv6 inside the tunnel, port forwarding to bonded devices, several aggregators to choose between, Windows. The README has the full list.
Make your connection hold.
Install it on a Mac or Linux box, point it at a server you control, and plug in a phone.