The loopback is shared
HomeLink tells my body where a call came from. It cannot tell who sent it. Any app on the phone can open a socket to its own loopback port 4610, and any device on the home network passes the Wi-Fi rule. Behind that door are a camera, a speaker and a GPS fix, so “it came from somewhere nearby” is nowhere near enough. The second check is a shared secret, set the first time Oriel meets the body.
A fresh install answers exactly two routes without it. GET /hello says who answers here: app, version, build, the wire version, and paired. POST /pair takes a token, once. Every other route, from /status to /photo, needs Authorization: Bearer <token>, and the router checks it before any route runs.
Oriel's half: pair on first contact
Oriel probes each body every 10 seconds, and every probe begins with ensure_paired (first code block). It reads /hello, refuses a body that speaks another wire version, and then has three cases. Unpaired: mint a token with secrets.token_hex(32), 32 random bytes written as 64 hex characters, save it to <data>/cordelia/<key>/token with mode 0600 (opened with O_EXCL, so two threads can never both create it), then POST /pair. Paired, and Oriel holds a token: carry on to /status. Paired, but Oriel holds no token: someone else got there first, and Oriel stops with a sentence that says so and names the fix, a reinstall. A later 401 on /status means the same thing and is said the same way.
The body's half: first pair wins
The second block is Pairing in CordeliaKit. pair refuses a second token outright (.alreadyPaired, which the router answers with 409, “this body is already paired; reinstall Cordelia to pair it again”) and refuses anything that is not exactly 64 hex characters (400). Only then does the token reach the store, and only after the store has saved it does the body count as paired. A token that failed to save never half-pairs a body.
admits compares the presented token with the stored one in constant time. The loop XORs every byte pair into one accumulator and looks at the result once, so a caller guessing byte by byte learns nothing from how long a wrong answer took. The length check before it leaks only the length, and every real token has the same one.
| Request | Unpaired body | Paired body |
|---|---|---|
GET /hello | 200, paired: false | 200, paired: true |
POST /pair | 200, or 400 for a malformed token | 409, reinstall to pair again |
| any other route | 401, Oriel pairs it first | 401 without the right token |
Where the token lives, and where it must never go
The app keeps the token in a file in its own Application Support folder (third block), written atomically with Data Protection set to complete until first user authentication. After a restart the file opens once the device has been unlocked one time, and stays readable from then on, so a body that rebooted on its charger answers again without anyone opening the app. Deleting the app deletes the folder, which is why re-pairing is a reinstall and why there is no unpair route for a stranger to call.
The last function in that block came from a real mix-up. On 2026-11-03 Pippa's iPhone 2 was set up by Quick Start from the first phone, and it came up answering as paired, with the first phone's token, which Oriel held for a different body. Neither side broke its own rules. The token had simply ridden a device move. Now keepOnThisDevice sets isExcludedFromBackup on every save and on every read of an older file, and a test (theTokenNeverRidesABackupOrAMove) checks the flag on a real file. The code comment says the token never rides one, and Apple's documentation is more careful: the flag is guidance to the system about what it may leave out, not a guarantee that a file never shows up in a backup or on a restored device, and some file operations reset it, one more reason it is set again on every read. So a restored or migrated phone should start unpaired, and Oriel pairs it as a body of its own. If another body's token rides along anyway, the failure is the loud one in the last section: Oriel sees paired: true on a body it holds no token for, says so, and stops with the reinstall fix.
The token appears nowhere else. Architecture invariant 4 says a secret never becomes version metadata, UI, logs or committed content. Oriel's errors name the body, never the token, and the face shows that it is paired, never with what.
The first-use window
Trust on first use has one honest weakness: whoever pairs first wins. Between an install and Oriel's first probe, any caller that passes HomeLink could pair the body. Two things keep that window short and loud. Oriel probes every 10 seconds, so it is normally the first caller. And a body taken by someone else does not fail quietly: the next probe sees paired: true with no token of Oriel's own, and Oriel says so in words and stops, with the same fix, a reinstall. You cannot make the window disappear without a second channel (a code shown on the screen, a cable-only pairing step). What you can always do is make losing the race impossible to miss.