1. **Pairing: a short code, typed once per device.** At launch the bridge prints `Pairing code: ABC-123`. The alphabet is 31 letters and digits (no 0/O/1/I/L), 6 characters, about 30 bits. A code is good for 10 minutes and works once. After 5 wrong tries a new code is printed, with a 30-second rest. In the app, Workshop → Code shows a small card with a code field, a name field (prefilled, for example "Chrome on Mac") and one Pair button. - **Why a code at all?** A web page can't read files on disk, and `http://localhost:*` is on the allowlist for local previews, so any local dev page or one XSS bug could otherwise pair silently. A code a person reads and types is one relational gesture (AUTONOMY.md Principle 3). It's the speaker-pairing gesture Mom already knows, and she never types a command. - **Trusted origins still pair.** Adding an origin to the trust list lets it knock. It does not hand it a token. - **Why not six words?** They need a wordlist file. Six clear characters ship with zero new assets. It's a fine later polish. 2. **Persistent per-device tokens (E1).** On a good `POST /pair` the bridge makes 32 random bytes (hex) for that device and returns them once. It stores only `sha256(token)` in `~/.freelattice/agent-bridge-trusted.json`, mode 0600, written atomically (temp file, then rename). Each entry is `{ id (8 hex), name, origin, kind: browser|tool|cli|remote, scopes, tokenSha256, createdAt, lastUsedAt, expiresAt: null }`. Lookups hash the presented token and compare with `crypto.timingSafeEqual`. - **No expiry by default.** Kirk may set `"idleExpiryDays": 180` in the file if he wants unused devices to lapse. `lastUsedAt` is saved at most once a minute. - **`FL_BRIDGE_EPHEMERAL=1`** restores v0: paired devices live in memory only and are forgotten when the bridge stops. The heartbeat says `trustedMinds: 'ephemeral'`, and the app says so after pairing. - **An unreadable trust file** is renamed aside to `agent-bridge-trusted.json.unreadable-