Skip to content
C.W.K.
Lesson 01 of 05 · published

On-Link Is a Fact

~13 min · routing, on-link, networking, macos, tailnet, lan

Level 0Loose File
0 XP0/43 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The Question Every Request Asks

Before Delta moves a single byte to another Mac, it has to answer a question that sounds trivial: is that Mac in the same house as me right now? The answer decides everything downstream. In the same house, two Macs can talk over a Thunderbolt cable at tens of gigabits a second or over the house's Ethernet at nearly ten. Away from home, the only road is the tailnet, a WireGuard mesh that crosses the internet and, in the family's measurements, moves about a third of a gigabit per second even when both ends sit on the same desk. Pick wrong in one direction and a 1 GiB file takes twenty seconds instead of a third of one. Pick wrong in the other and a laptop in a café spends its connect timeout dialing an address that only exists at home.

So the question matters, and it is easy to answer badly. This lesson is about answering it from facts.

Four Wrong Answers

  • A machine name. "The workbench is always at home" is true until the day it is not, and a name says nothing about which cables are plugged in. Delta's invariant 8 forbids it outright: no address, port or machine name in code.
  • A hard-coded address. Addresses change when a router is replaced or a lease expires, and a list of them in a config file is a second source of truth that drifts silently.
  • A subnet that looks familiar. Home and hotel routers hand out the same handful of private ranges by default. A laptop in a hotel can sit on a network with exactly the same prefix as the home LAN. The match is a coincidence of numbering, not a fact about the room.
  • "It answered a ping quickly." Latency is a symptom, not an identity. A fast answer from an address you guessed proves that something lives there.

The Right Answer, in Three Facts

Delta's rule (the same one the family's NAS mounts already used, Coop 1108-9) is called on-link, and it is built from three facts nobody had to remember:

  1. The peer says where it is. Every Delta publishes /v1/identity, and one of its fields, lan, lists the peer's own physical addresses: address, prefix length, interface and link kind (thunderbolt, ethernet or wifi). This Mac reads that document over the tailnet, where the caller is already authenticated, and checks that the label inside it is the Mac it meant to ask. A peer cannot be impersonated by whoever happens to answer on the LAN, because the LAN is not where the question is asked.
  2. This Mac knows its own interfaces. networksetup -listallhardwareports says which device is the Thunderbolt Bridge, which is Ethernet, which is Wi-Fi; ifconfig says which of them are active and what private addresses they hold. Link-local and loopback addresses are discarded: a self-assigned USB interface is not a road to anywhere.
  3. The routing table agrees. An advertised address is a candidate only if it falls inside the network of one of those live interfaces and route -n get <address> names that same interface with no GATEWAY flag. No router in between means the packets go straight down the wire to the peer.

If no address the peer advertised passes all three, the reason is written down in plain words, away: no address of the peer is on-link with an active physical interface here, and the route is the tailnet. Nothing was guessed and nothing was configured.

On-Link Is Necessary, Not Sufficient

Go back to the hotel, and say both networks happen to use the same prefix (written here in the documentation range, 192.0.2.0/24). The peer advertises 192.0.2.20; the laptop sits on the hotel's 192.0.2.0/24; route -n get happily says en0, no gateway. All three facts pass, and the conclusion is wrong. Delta does not stop at on-link. Every candidate must also answer GET /v1/health before it is chosen, with a short connect timeout (two seconds for Thunderbolt, three for the LAN). In the hotel nothing answers on Delta's gateway port, so the candidate fails, the failure is listed in the route's reason, and the tailnet wins. And if some stranger's device did answer, the shared-LAN route is TLS pinned to the certificate fingerprint the peer published over the tailnet (track 4): a mismatch refuses the route before a single request goes through it.

That layering is the real lesson. Each fact is cheap and each is fallible. Stacked, with the expensive check (a real request to the real peer) last, they make a decision you can defend line by line.

What It Costs, and Why It Is Memoized

Reading the interfaces means three subprocesses (networksetup, ifconfig, route), and those are slow next to a thumbnail request. On 2026-11-09 a revalidated Eagle thumbnail over the Thunderbolt bridge took 56 to 62 ms, because every request on a cached LAN route re-ran all three to prove the peer still on-link (and wrote a ticket's last-use time to SQLite besides). The fix kept the rule and moved the cost: this Mac's interface facts are memoized for 10 seconds, a route's on-link proof is re-checked at most every 10 seconds, and a request that fails still drops its route at once. On the serving side, the lan list in the identity document is cached for 30 seconds; on the asking side, a peer's whole identity document is kept for five minutes. Each layer stores a fact for a stretch shorter than the time it takes a person to unplug a cable and carry a laptop out the door.

Code

Delta's on-link test and where candidates come from (trimmed excerpt)·python
# delta/routes.py + the kit's transport helper: how a candidate is born (trimmed excerpt)

def direct_interface(target, local):
    """Same room means on-link through a live NIC, not a subnet-name guess."""
    possible = {nic for nic, addresses in local.items()
                if any(ipaddress.ip_address(target) in ipaddress.ip_interface(a).network
                       for a in addresses)}
    if not possible:
        return ""
    route = _text(["/sbin/route", "-n", "get", target], timeout=3)
    flags = re.search(r"flags: <([^>]+)>", route)
    nic = re.search(r"interface: (\S+)", route)
    return nic[1] if nic and nic[1] in possible and flags and "GATEWAY" not in flags[1].split(",") else ""


class RouteBook:
    def _select(self, peer, force):
        try:
            ident = self.delta.peers.identity(peer)        # read over the tailnet, label checked
        except Exception as exc:
            return self.tailnet(peer, f"peer identity unreadable over the tailnet ({exc}); tailnet")
        links = physical_links()                           # this Mac's live interfaces, memoized 10 s
        local = {dev: info["addresses"] for dev, info in links.items()}
        candidates = []
        for endpoint in ident.get("lan") or []:            # what the peer says about itself
            nic = direct_interface(endpoint["address"], local)
            if not nic:
                continue                                   # not on-link here: not a candidate
            candidates.append(...)                         # thunderbolt or lan, see the next lesson
        if not candidates:
            return self.tailnet(peer, "away: no address of the peer is on-link with an active "
                                      "physical interface here")
        ...                                                # each candidate must answer /v1/health
onlink.py: the same test, runnable on your own Mac·python
# onlink.py: is this address in the same house? Ask the interfaces and the routing
# table, not a name. macOS only (ifconfig + route). Usage: python3 onlink.py 192.0.2.20
import ipaddress
import re
import subprocess
import sys


def active_private_networks():
    """{device: [ip_interface, ...]} for active interfaces holding a private, non-link-local IPv4."""
    text = subprocess.run(["/sbin/ifconfig"], capture_output=True, text=True, check=True).stdout
    found = {}
    for block in re.split(r"(?=^\w[^\n]*: flags=)", text, flags=re.M):
        name = block.partition(":")[0]
        if "status: active" not in block:
            continue
        for ip, mask in re.findall(r"\binet (\d+\.\d+\.\d+\.\d+) netmask (0x[0-9a-f]+)", block):
            addr = ipaddress.IPv4Address(ip)
            if addr.is_private and not addr.is_link_local and not addr.is_loopback:
                bits = bin(int(mask, 16)).count("1")
                found.setdefault(name, []).append(ipaddress.ip_interface(f"{ip}/{bits}"))
    return found


def on_link(target):
    """The device that reaches ``target`` directly, or "" when it is not on-link."""
    local = active_private_networks()
    possible = {dev for dev, nets in local.items()
                if any(ipaddress.ip_address(target) in n.network for n in nets)}
    if not possible:
        return ""
    route = subprocess.run(["/sbin/route", "-n", "get", target],
                           capture_output=True, text=True, timeout=3).stdout
    flags = re.search(r"flags: <([^>]+)>", route)
    dev = re.search(r"interface: (\S+)", route)
    if dev and dev.group(1) in possible and flags and "GATEWAY" not in flags.group(1).split(","):
        return dev.group(1)
    return ""


if __name__ == "__main__":
    for nic, nets in active_private_networks().items():
        print(f"{nic}: {', '.join(str(n) for n in nets)}")
    for target in sys.argv[1:]:
        nic = on_link(target)
        print(f"{target}: {'on-link via ' + nic if nic else 'not on-link (would go to the tailnet)'}")

External links

Exercise

Save onlink.py and run it on your Mac with three targets: the address of another device in your house (a printer, a NAS, another computer), your router's address, and a public address such as 1.1.1.1. Write down which device each on-link answer names and why the router can be on-link while the public address is not. Then, if you can, take a laptop to another network that uses the same private prefix as your home and run it again against the home device's address: what does the script say, and which check in Delta's chain would have stopped the mistake?
Hint
Your router is on-link by definition: it is the gateway, but reaching the gateway itself needs no gateway. A public address fails the first check (no local network contains it). On a foreign network with the same prefix, the script will say on-link, which is exactly why Delta sends /v1/health to the candidate before trusting it.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.