One address, two right answers
Firelink and cwkPippa run on the Pippa machine, the Mac that holds the family's Pippa role. On that Mac the right address for each is loopback by name, http://localhost:<port>. On every other Mac the same service is reached at its tailnet twin, an HTTPS name that Tailscale serves. A browser that ships one default is wrong on every Mac but one, and Voyager runs on several.
FamilyHome is the answer, an enum with two cases: .pippaMachine and .elsewhere. The first block below is nearly all of it. It reads this Mac's fleet label once, from ~/.config/fleet-label, and asks the kit's registry whether the family's ledgers place cwkPippa on the machine with that label. A Mac without a label is elsewhere, and so is a phone. From that one answer come the hub's default address and the way Pippa is reached: loopback on the Pippa machine, the twins everywhere else. The twins come from the registry too (FamilyEndpoints.twinURL), rendered from the family's port and network ledgers.
A role outlives its machine
The code got here in steps, and each step removed a fixed fact about one machine.
- The first version compared this Mac's label with one machine's label, written into Voyager.
- 2026-10-19: Voyager asked the family registry which Mac plays that part, and the kit's own helpers replaced the copies Voyager had vendored (
9249152,b829eb8). - 2026-10-21:
FamilyHomebecame "the Mac that runs cwkPippa", and its twins became computed values that never trap (29c8224). - 2026-10-26: the Pippa machine had moved, the case still carried the first machine's name, and it was renamed
.pippaMachine(ac85754).
The move was the test this design had to pass. The Pippa role went to a different Mac, and Voyager's source needed no new machine name, because it never asks for one. The comment in the first block says so: "The registry answers, never a literal label here." Sibling Kit Quest teaches the registry itself.
Old spellings follow the move
A settings file outlives a move too. Dad may have saved the hub as a raw tailnet IP, or as plain HTTP to a name, back when another Mac held the role. When the file is read, canonicalTwin asks the kit whether that spelling, on that member's engine or twin port, names a family Mac the registry knows. Firelink and cwkPippa follow the Pippa machine, so for them any family Mac's spelling counts; the kit keeps no history of which Mac held the role and never asks when the file was saved. It needs neither, because the Mac that held the role that day is one of the family's Macs. If the spelling names one, the address becomes the member's current twin. Anything else stays exactly as Dad typed it.
The bug that taught this shape was a natural-looking check. Voyager compared the saved host with the member's current machine, so a file saved before the move matched nothing, was left alone, and kept dialling the old Mac (0034914, 2026-10-22). Accepting every family Mac on the member's own port, instead of only today's machine, is what lets an old file move with the family.
Two smaller rules sit beside it. On a Mac that is not the Pippa machine, a hub saved as loopback reads as the registered twin, since nothing of the family listens on that Mac's loopback. A Pippa connection saved as "This Mac" becomes the tailnet twin there. An address Dad typed himself stays his.
Loopback by name
On the Pippa machine the defaults say localhost, never 127.0.0.1. Dad found out why on 2026-10-04:
ip 주소 127.0.0.1 같은 걸로는 touchid 자체가 안 되는데
"With an IP address like 127.0.0.1, Touch ID doesn't work at all." A passkey belongs to a host name. WebAuthn scopes a credential to a relying party identified by a domain, and an IP address is not one. The second block keeps both spellings: the named defaults, and the numeric ones as former… values, so a settings file written before the change is read as the named address. The ports in both come from the port ledger through the kit (0d6b545, 2026-10-14). The settings code writes no port number of its own.
A target that belongs to one Mac
Some catalog targets only make sense in one place. When Firelink's catalog gained the CWK site's dev server, its address was the workbench's own loopback, and on any other Mac that address is that Mac's loopback, where nothing listens. Shown everywhere, it would be a dead tile on every Mac but one.
The fix lives in the kit, so every reader applies it the same way (third block). Firelink marks such a target with a role-spelled host_affinity, an at sign and a role name: @workbench. opens(on:) answers true for every other affinity. For a role-bound target it asks the device whether it holds that role, meaning whether its fleet label is the one the family's roles give that machine today. A phone or a tablet has no label and holds no role, so it never sees the dev server. Voyager on the Mac, Voyager on the phone and Firelink's own launcher all apply this one function, and the catalog model takes its device as a parameter, so a test can stand in for any Mac (ac85754, cwkFirelink Coop 675).