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

Two Doors, Not One

~12 min · family-authority, firelink, pin-session, brain-control, machine-aware, v3

Level 0Cold Draft
0 XP0/43 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"The brain that talks to you and the authority that decides who may talk are two different planes. Give each its own door."

The One Door This Track Described

Every lesson so far drew a single line from the editor to cwkPippa: bind the note, push the context, stream the reply, and gate the remote tier with a PIN. That picture held through v4. On 2026-09-21 it split. The commit was titled Separate family authority from the Rekindle persona client, and a second one the same afternoon routed the iPhone's authentication and role reads the same way. Since then the client holds two doors, and every request knows which one it belongs to.

Pippa door   (cwkPippa, port 8000)
  margin conversation   /embed/rekindle in a web view
  prompt macros         POST /api/admin/utility-macros/apply-free
  Tab co-writing        POST /api/rekindle/co-writing/complete

Family door  (Firelink, port 9400; the iPhone uses the HTTPS twin 9401)
  the PIN session       POST /api/security/verify-pin  (bound to the caller's address)
  the editor slot       GET  /api/brain-control/assignments?app_id=rekindle

The Pippa door is where the conversation and the judgment happen; nothing about it changed. The family door answers two questions that were never Pippa's to answer: may this device act at all (the PIN session Firelink issues and binds to the address that presented it), and which brain, effort and model does the central editor_pippa slot name today (the brain-control assignment, which since 2026-09-23 also carries per-slot model overrides that the Mac and the iPhone both honor). The Mac's PippaService literally carries two fields, door and familyDoor, built from the same configuration.

One Resolver Names the Authority

The family door's address is never typed into the app. A kit resolver derives it from whichever route already reaches the brain: a loopback route on office gets Firelink on loopback, a TLS route gets the Tailscale Serve HTTPS twin, anything else gets Firelink over plain Tailscale. The iPhone's first separation build handed the resolver a literal http Pippa URL, so it got the plain-Tailscale answer and dialed cleartext port 9400; a cold review caught it. The family rule says an iOS surface reaches Firelink through its HTTPS twin, so the door now asks the resolver for the TLS answer instead of handing it a literal URL.

The resolver's answer also stamps a cache. Brain-control assignments are cached so an outage does not blank the editor slot, and the cache now records control_base, the authority that wrote it. A cache stamped by another plane counts as absent; one with no stamp may bridge an outage only as stale. The first stamped build on the iPhone (build 20) had stamped the Pippa base, port 8000, which is a plane that never answered those reads; the next build reads the assignment in full once and restamps it with the family authority. A cache is only as true as the authority whose name it carries.

The Machine Is an Input

The second half of the story is about defaults. Every Mac used to start on "Pippa Full – This Mac", which means 127.0.0.1:8000. On office that is cwkPippa. On the MacBook, port 8000 belongs to a different family app entirely (Cradle's local model server), so a fresh MacBook install dialed the wrong program, and brain control and the PIN route, both derived from that connection, followed it there. Dad's verdict on 2026-10-06: "다른 맥이면 알아서 주소가 달라져야지 native app도 마찬가지고" (on another Mac the address should change by itself, native app included).

The fix reads the machine instead of assuming it. The vendored kit leaf KitFamilyHome reads the fleet label, and three defaults now follow it:

  • Pippa connection: office keeps This Mac; any other Mac starts on Tailscale to office. A stored local, which every first launch used to write, reads back as Tailscale off office; a host Dad typed stays. This Mac is offered off office only while it is already selected.
  • Lantern (corpus completion and evidence): off office it uses office's HTTPS twin unless a Tailscale host is set.
  • Sync with Peer (2026-10-12): off office the peer is office's sync engine on its HTTPS twin; the legacy locator the cutover carried over, office's home mounted as a /Volumes/… share, is retired on load. On office the field stays empty, because the Mac app and its engine are one replica of one root.

The bind from lesson 1 and the tiers from lesson 3 are untouched. What changed is that "where is the brain" now has two answers, one per plane, and "which Mac am I on" is read rather than presumed.

Code

One resolver names the family authority for whatever route reaches the brain (sketch)·swift
// Shape of the kit's KitPinAuth.familyAuthorityURL(for:), simplified.
func familyAuthority(for executionBase: URL) -> URL {
  if isLoopback(executionBase.host) {
    return URL(string: "http://127.0.0.1:9400/")!       // on office, beside Firelink
  }
  if executionBase.scheme == "https" {
    return officeServeTwin(port: 9401)                  // a TLS caller (the iPhone)
  }
  return officeOverTailscale(port: 9400)                // a Mac over Tailscale
}

// Two doors, built from one configuration.
let door       = PippaDoor(endpoint: pippaExecutionBase)            // :8000
let familyDoor = PippaDoor(endpoint: familyAuthority(for: base))    // Firelink

// The brain-control cache is stamped with the plane that answered it.
// A cache stamped by any other plane is treated as absent.
Defaults by machine (KitFamilyHome reads the fleet label)·text
setting            office                     any other Mac
-----------------  -------------------------  -----------------------------------
Pippa connection   This Mac (127.0.0.1:8000)  Tailscale to office (:8000)
stored "local"     kept                       read back as Tailscale
Lantern            localhost:8400             office's HTTPS twin (:8401)
Sync with Peer     empty (app + engine are    office's sync engine, HTTPS twin
                   one replica)               (:7201); legacy /Volumes mount retired

# The local Ollama tier and every other stored choice are untouched.

External links

Exercise

Take a client app you know that talks to a backend. List every request it makes and sort them into two piles: requests about doing the work (fetch, generate, save) and requests about permission or configuration (log in, read my role, read which model or plan I have). Do both piles go to the same address and share one cache? Sketch what happens to each pile if only the permission service moves to another host.
Hint
The tell is a cache or a credential that 'belongs' to the work service but is really answered by the permission service. When the permission service moves, that cache keeps looking valid while pointing at the old plane — the exact case the control_base stamp exists to catch.

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.