Quickstart
In about fifteen minutes you will have two Linux machines joined by one encrypted tunnel that rides two network paths at once — and you will unplug one of those paths mid-ping and watch nothing happen. That behavior is the product; everything else in this documentation is refinement.
What you need
- Two Linux hosts that can reach each other over at least one IP network. Two networks (say, Ethernet and Wi-Fi, or two Ethernet segments) make the demonstration real — with one network you still get the tunnel, just nothing to fail over to.
- The
atlasdbinary — from Downloads with an approved evaluation (verify it against the release signatures first), or built from source if your organisation has repository access:cargo build --releaseproducestarget/release/atlasd. - Root or
CAP_NET_ADMINon both hosts — the daemon creates a TUN device and programs routes. In production the systemd unit grants exactly that one capability to an unprivileged service user; see Deploying with systemd.
1 — Generate the node identities
Each node's identity is a Curve25519 keypair. Generate one per host and keep the private key private — it never leaves the machine.
$ atlasd keygen
private key: 4Ai…kEY= # goes into this node's config, mode 0600
public key: 9Xp…bQc= # goes into the OTHER node's [[peer]] block2 — Write the two configs
One file per daemon: /etc/atlas/config.toml in production, any path during a bench run. The two files mirror each other — each lists its own private key and the other node's public key. Adjust the underlay addresses (192.168.…) and device names (eth0, wlan0) to your bench.
[interface]
name = "nexus0" # the TUN device this daemon owns
address = "10.0.100.1/24" # this node's address inside the tunnel
private_key = "NODE_A_PRIVATE_KEY" # from: atlasd keygen
listen_port = 51820
[[link]]
name = "wired"
bind_device = "eth0"
bind_address = "0.0.0.0" # survives the device being re-addressed
[[link]]
name = "wireless"
bind_device = "wlan0"
bind_address = "0.0.0.0"
[[peer]]
public_key = "NODE_B_PUBLIC_KEY"
endpoint = ["192.168.1.2:51820", "192.168.2.2:51820"] # one per link, in order
allowed_ips = ["10.0.100.2/32"]
persistent_keepalive = 25
node-b.toml
[interface]
name = "nexus0"
address = "10.0.100.2/24"
private_key = "NODE_B_PRIVATE_KEY"
listen_port = 51820
[[link]]
name = "wired"
bind_device = "eth0"
bind_address = "0.0.0.0"
[[link]]
name = "wireless"
bind_device = "wlan0"
bind_address = "0.0.0.0"
[[peer]]
public_key = "NODE_A_PUBLIC_KEY"
endpoint = ["192.168.1.1:51820", "192.168.2.1:51820"]
allowed_ips = ["10.0.100.1/32"]
persistent_keepalive = 25
Everything not written here takes its default: probes every 250 ms per link, a link declared dead after 5 consecutive misses, a re-handshake every 120 seconds, and the lowest_delivery_time scheduler picking the best path per packet. The configuration reference documents every one of those knobs.
3 — Validate before you run
config check runs the daemon's exact load pipeline without starting anything. Exit 0 means loadable; exit 1 means the daemon would refuse to start, with each finding pointing at the offending key.
$ atlasd config check -c node-a.toml
node-a.toml is valid4 — Start both daemons
$ sudo atlasd run -c node-a.toml
… handshake init from peer 1 processed, sending response
… handshake complete with peer 1
… all peer handshakes establishedThe Noise IK handshake (X25519, ChaCha20-Poly1305, BLAKE2s) completes per peer; from that moment every packet entering nexus0 leaves encrypted on whichever link the scheduler judges best, and probes are already measuring both paths.
5 — Verify the tunnel
$ ping -c 3 10.0.100.2
64 bytes from 10.0.100.2: icmp_seq=1 time=2.1 ms
$ atlasd status
nexus0 10.0.100.1/24 strategy lowest_delivery_time 2/2 links alive 1 peer established
$ atlasd stats -i nexus0 -f json-pretty | head
{ "interface": "nexus0", "links": [ { "name": "wired", "alive": true, "rtt_ms": 0.4, … } ] }Three more windows into the same state, pick your favourite: atlasd monitor -i nexus0 is a live terminal dashboard; atlasd web -i nexus0 serves the browser dashboard and REST API on :9800; and the snapshot file /tmp/atlas/nexus0.json is rewritten every second for anything that wants to scrape it.
6 — Now kill a link
Start a continuous ping, then take the wired path down — unplug the cable, or:
$ ping -i 0.2 10.0.100.2
64 bytes from 10.0.100.2: icmp_seq=38 time=1.9 ms
$ sudo ip link set eth0 down
64 bytes from 10.0.100.2: icmp_seq=39 time=4.6 ms # now riding "wireless"
64 bytes from 10.0.100.2: icmp_seq=40 time=4.4 msThe scheduler was shifting traffic toward the healthier link before the failure finished happening — reweighting is continuous, not a binary failover — and after five missed probes the dead link is formally removed. Bring eth0 back up and watch it rejoin the bond the same way: gradually, as its measurements recover. Your SSH session, your video stream, your ping never dropped.
Where to go next
- Multi-link scheduling — the eight strategies, probes, and per-link tuning for asymmetric bearers (a 250 ms probe on 4G next to a 1000 ms probe on VSAT).
- Mesh & multi-hop routing — three or more nodes, link-state routing, and relaying through your own fleet.
- NAT traversal & relays — when the two machines are behind carrier NAT and can't address each other at all.
- Deploying with systemd — turn the bench run into a hardened, watchdog-supervised service.