Architecture

juist is a mesh VPN without a coordination server. This page explains how it is built, what it takes from WireGuard and Tailscale, and in what sense it is zero trust.

In short #

Most mesh VPNs have a coordination server. It knows every device and its keys, tells each device which others it may talk to, and is where an admin adds or removes devices. Whoever runs that server, or breaks into it, decides who belongs to the network.

juist has no such server. Its place is taken by the membership log, which your admins sign and every device checks itself. Everything else, finding devices, getting through NAT and carrying the traffic, may go through paths and helpers that juist does not trust.

The terms used here are explained in How juist works; how juist differs from other mesh VPNs is on juist compared.

Zero trust #

Zero trust is a security model in which nothing is trusted because of where it sits in the network. Inside the LAN or outside it, every access is authenticated first. NIST describes the model in SP 800-207; Google’s BeyondCorp is a well-known example of it.

juist follows it in these points:

  • No address is trusted. A packet counts only if it arrives through a WireGuard tunnel from a key the membership log admits. A device on the same LAN, or one that claims a member’s address, gets nothing through.
  • No server and no path is trusted. Every device checks every change to the network against the admins’ signatures, however it arrived.
  • No single key decides. With a quorum of two or more, one stolen admin key changes nothing alone, and juistd never holds an admin key at all.
  • Trust ends. A removed device loses its tunnels as soon as members hear of it, and freshness bounds that to 48 hours for members that are cut off.

Where juist falls short of the model:

  • Without an access policy, an admitted device reaches every port of every other member. The policy names devices, not users, so services on members should still check who connects.
  • juist knows devices, not users. It does not check who sits at a device or whether the device is up to date.

The layers #

juist is built in four layers. A layer relies on the ones below it to deliver packets, but never to decide who belongs to the network.

LayerWhat it doesTrusted forBuilt from
TrustThe membership log, the quorum, the group secret, freshness and invitesWho belongs to the networkjuist
SyncCarries log entries and addresses: from member to member, on the LAN and through the DHTNothing; every entry is signedjuist, BitTorrent DHT
ConnectivityFinds a path between two devices, through NAT or over a relayNothing; it can delay a connection, never admit oneTailscale: magicsock, DERP
Data planeEncrypts and authenticates every packet between two devicesKeeping traffic secret and unalteredTailscale: wireguard-go

The layers below the trust layer can delay or prevent a connection. They cannot change who is a member, and they cannot read or alter traffic. WireGuard enforces that at the bottom: juistd configures its peers from the membership log and from nothing else.

juistd turns the state of the log into everything the operating system sees: the WireGuard peers, the packet filter, the routes and the names. When a new change arrives, it does so again.

WireGuard #

WireGuard is a VPN protocol by Jason A. Donenfeld, described in his paper WireGuard: Next Generation Kernel Network Tunnel (NDSS 2017). Its handshake is built on the Noise protocol framework, with Curve25519, ChaCha20-Poly1305 and BLAKE2s; the protocol page has the details.

juist relies on what WireGuard calls cryptokey routing. Each peer is a public key together with the addresses it may use. A packet from a peer is accepted only if its source address belongs to that key, and a packet to an address goes only to the key that owns it. juist fills that table from the membership log alone, so:

  • a key the log does not admit cannot complete a handshake;
  • a member cannot send from another member’s address;
  • an address in the network is bound to a key, not to anything a router or a relay says.

On top of that, juist’s packet filter drops every incoming packet it was not told to accept.

juist runs WireGuard in user space on every platform, in the version Tailscale maintains of wireguard-go. Tailscale’s NAT traversal, below, needs it there. Kernel WireGuard would be faster on Linux and FreeBSD.

Every WireGuard handshake mixes in a preshared key that a future quantum computer cannot recover, so traffic recorded today stays secret. Two devices start on a key derived from the group secret below; in the log sync between them each then encapsulates a secret to the other’s ML-KEM-768 key, and they move to a key the two alone hold, renewed with every change to the members.

What juist takes from Tailscale #

Tailscale is a mesh VPN on WireGuard, with a coordination server run by Tailscale Inc. Its client is open source under the BSD-3-Clause licence. juist builds parts of that client in, pinned to version 1.104.0:

  • magicsock and disco find a direct path between two devices and get through NAT. Tailscale explains how in How NAT traversal works.
  • DERP passes traffic where no direct path forms; see DERP servers. In juist it runs on a member granted relay and serves only members of the log, where Tailscale runs DERP servers for all its users.
  • wgengine connects WireGuard to the operating system: the network device, the packet filter, routes and DNS.
  • STUN lets a device learn its public address.

juist leaves out the part that talks to a coordination server. Where Tailscale’s client takes its peers and their keys from that server, juist gives the same code peers built from the membership log. You need no Tailscale account, and no traffic goes to Tailscale.

A bug in the reused connectivity code can cost connections, or show addresses, but cannot admit a device: WireGuard below it checks every packet against keys from the log.

Tailnet Lock #

Tailnet Lock is Tailscale’s own answer to a coordination server that should not be trusted with keys: trusted devices sign which keys may join. The membership log takes its design from it, a chain of signed records, but is juist’s own code. It differs in three ways:

  • the log holds the members themselves, not only the keys that sign them;
  • every change needs the quorum, not one trusted signature;
  • the devices order and spread the changes among themselves, where Tailnet Lock relies on the coordination server for that.

The membership log #

Each change, such as “admit nas”, is a record. It names the record before it by hash, and it counts only when enough admins have signed it with their Ed25519 keys. Every device replays the log from the network’s first record and checks each signature itself.

Changes made at the same time on different devices are put in one order that every device works out the same way. Changes that contradict each other are never settled silently, with one exception: a removal wins. A real fork, two histories that disagree about who is a member, raises an alarm that an admin has to settle.

Every change to the members replaces the network’s group secret. It is sealed to each member with HPKE, using X25519 together with ML-KEM-768, so that a removed device cannot read what is protected with the new one.

Finding devices without a server #

The group secret protects what devices publish to find each other:

  • On the LAN, devices answer multicast DNS under a name derived from the group secret, with an encrypted answer. Others on the LAN learn that juist is in use, and nothing more.
  • In the public BitTorrent DHT, each member publishes its signed address as a mutable item (BEP 44), under a key only members can compute and encrypted so only members can read it.
  • Members pass on to each other where they last reached a device.

An invite works before the new device holds any secret. Both sides run SPAKE2, a key exchange based on a short password, so that the code or link cannot be guessed offline and a device in between gives itself away in the four words both sides compare.

Sources #

Zero trust

WireGuard

Tailscale

Other standards

The design in full, with the reasons for each decision, is in the repository’s architecture document and threat model.