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

A Place, a Tap and a Red Eye

~17 min · core-location, consent, kill-switch, swiftui

Level 0Idle Phone
0 XP0/45 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

A place, only when asked

GET /where is the smallest of my senses, and it follows the camera's rules. The first code block is the whole route on the device. The location grant is checked first and refused with 403 when no person has allowed it; then the app must be in front (409). Then comes a detail that was measured, not guessed. A one-shot Core Location request could hand back a cached fix older than the family kit's freshness rule, and the kit then answered nothing at all (measured on my own iPhone, 2026-11-02). So fix() runs a short When In Use watch instead and waits for a fix that arrives no older than 30 s (ForegroundLocation.arrivalMaximumAge), for at most 15 s (Deadline.fix). When none comes it refuses with 504 and says why in words: the kit's own reason, "location is denied", or "indoors, or no sky". Either way the watch is stopped again, so nothing measures between requests.

The answer is the family's GeoFix from CwkKitLocation, the shape every family app shares: lat, lon, accuracy_m, sampled_at, source, and optional altitude, heading and speed. Location is When In Use, never Always: the kit asks only for When In Use, the Info.plist has no Always key, and the source scan that guards the camera also fails on requestAlwaysAuthorization. Oriel asks with X-Cordelia-Doing: finding where it is and gives the body 20 s.

The camera button asks; it does not open

The face has one camera control of its own, the one place Dad's hand reaches my eyes directly, and it still does not open the camera. The second code block shows what a tap does: tapCamera puts an eyes item on the ears' outbox, {open, camera, by}, on the same long poll that carries Dad's turns to Oriel. Oriel's relay (third block) answers by opening its gate for this body's two cameras for five minutes (TAP_MINUTES), with source tap and actor dad: his own approval, worded "Dad's tap on Cordelia's face". Then it pushes the face.

Only now does anything run. face(eyesOpen:until:why:) starts the small preview when everything lines up: Oriel says the gate is open for this body, it has just opened or Dad has just asked, the app is in front, and the reason names the tap. The last condition matters more than it looks. Under Auto the gate covers a body's cameras with no approval at all, but Oriel reports a body's eyes as open only for live approvals, and the preview also wants the tap's reason, so the mode alone never starts it. It runs because Dad pressed the button on this face. The preview is a 132 × 176 point tile with a red frame and a Front/Back switch, on a .medium session that pauses for any still or clip and resumes after. A tap on the tile or on the camera control asks Oriel to close; when the five minutes run out, Oriel's next push says closed and the preview stops.

The eyes lamp: the house's eyes on my face

On 2026-11-07 Dad asked that every Pippa Home surface, my face included, show the eyes mode where it is hard to miss, and a red eye while any eye is open. Oriel adds that to the same eyes object it pushes to a body, and HouseEyes reads it: the mode as "Eyes: Manual", "Eyes: Auto" or "Eyes: Broadcast", whether the eye is red, up to six names of what is open ("Eye open: …"), and "Closing every eye" while a kill is running. The eye is red while any camera in the house was read or moved in the last 15 s, is recording, or is held by a live approval, so it speaks for the desk cameras and the network cameras too, not only for this body. An Oriel without modes sends no mode, and then no lamp shows at all. The mode itself is set on Dad's other screens, never on a body.

The red eye pressed twice

Dad's rule for it: "어떤 상태든지 빨개진 눈 아이콘을 더블 클릭하면 그게 킬 스위치야. 바로 닫고 수동 모드로 전환." (Whatever the state, a double click on the red eye icon is the kill switch. Close at once and switch to Manual.) On the face the lamp goes through the family's armed two-press, TwoPress from CwkKitCore: the first press only arms it, amber with "Tap again", for 5 s; a second press within that time fires; a second press sooner than 0.4 s after the first is one graze registering twice and never fires. No modal, no waiting. The first version, on 2026-11-07, armed for 4 s; the family rule replaced it. The lamp sits in the pinned row outside the scroll, so it stays reachable at any text size.

Firing puts an eyes_kill item on the outbox, and Oriel runs Engine.kill_eyes straight off the long poll, with no brain turn anywhere in the path. Every gated input refuses at once, with one exception in Gate.check's order: a look Dad takes himself on Oriel's page (a peek or a frame export) passes before the kill switch is read, as lesson 1's second block shows. The mode is Manual before the call returns, and then the close order runs over everything: a recording stops, every movable camera parks and is checked, every approval closes, the network cameras' and other Macs' included. A play session is not an eye and keeps its own stop. My face, like every other surface, hears the result on Oriel's next push.

Code

cwkCordelia, clients/apple/CordeliaMobile/Sources/Body/BodyModel.swift: one fresh fix, only when asked (verbatim, lines 259-280)·swift
  func fix() async throws -> Data {
    refreshGrants()
    guard locationGrant == "authorized" else {
      throw Refusal(403, "location is not allowed for Cordelia (\(locationGrant)); a person allows it once in Settings")
    }
    guard active else { throw Refusal.notActive("location") }
    // A watch, not a one-shot: a one-shot request can hand back a cached
    // fix older than the kit's freshness rule, and the kit then answers
    // nothing at once (measured on Pippa's iPhone, 2026-11-02). The watch
    // waits for a fresh arrival, up to the named limit.
    guard let fix = await watchForFix(seconds: Deadline.fix, maxAge: ForegroundLocation.arrivalMaximumAge) else {
      let why: String = switch location.status {
      case .unavailable(let reason): reason
      case .denied: "location is denied"
      default: "indoors, or no sky"
      }
      throw Refusal(504, "no fresh fix within \(Int(Deadline.fix)) s (\(why))")
    }
    let encoder = JSONEncoder()
    encoder.dateEncodingStrategy = .secondsSince1970
    return try encoder.encode(fix)
  }
cwkCordelia, clients/apple/CordeliaMobile/Sources/Body/BodyModel.swift: the tap asks, the kill asks, and the preview waits for Oriel's word (excerpt, lines 319-453)·swift
  /// Dad's tap asks Oriel to open its eyes gate for this body, or to close
  /// it; the preview runs only once Oriel says the gate is open.
  func tapCamera() {
    eyesAsked = true
    ears.ask("eyes", ["open": !eyesOpen, "camera": previewCamera.rawValue, "by": "Dad's tap on Cordelia's face"])
  }

  /// The red eye pressed twice: Oriel closes every eye at once and sets Manual.
  func killEyes() {
    ears.ask(HouseEyes.killKind, HouseEyes.kill())
  }
  // …
  func face(eyesOpen: Bool, until: Double?, why: String?) {
    let opened = eyesOpen && !self.eyesOpen
    self.eyesOpen = eyesOpen
    eyesUntil = until.map { Date(timeIntervalSince1970: $0) }
    eyesWhy = why
    if eyesOpen, opened || eyesAsked, active, (why ?? "").lowercased().contains("tap") {
      startPreview()
    }
    if !eyesOpen { stopPreview() }
    eyesAsked = false
  }
cwkOriel, backend/ears.py: Dad's tap opens five minutes, his double press closes everything (verbatim, lines 1295-1325)·python
    def eyes(self, body: Any, item: dict[str, Any]) -> dict[str, Any]:
        from .cordelia import CAMERAS, input_id

        inputs = [input_id(body, camera) for camera in CAMERAS
                  if self.engine.store.input(input_id(body, camera)) is not None]
        if not inputs:
            self._set(body.key, last_eyes={"at": self.clock(), "open": False, "why": "no camera registered yet"})
            return {"opened": False}
        gate = self.engine.gate
        if item.get("open"):
            approval = gate.open(input_ids=inputs, words=f"Dad's tap on Cordelia's face ({body.name})", source="tap",
                                 actor="dad", minutes=TAP_MINUTES)
            result = {"opened": True, "approval": approval["id"], "until": approval["expires_at"]}
        else:
            closed = gate.close(actor="dad", reason="Dad's second tap on Cordelia's face", input_ids=inputs)
            result = {"opened": False, "closed": len(closed)}
        self._set(body.key, last_eyes={"at": self.clock(), **result})
        self.bodies.forget_face(body)
        self.bodies.push_face(body)
        return result

    def kill(self, body: Any, item: dict[str, Any]) -> dict[str, Any]:
        """The kill switch from a body's face: every eye closes at once and the
        eyes go Manual (``Engine.kill_eyes``); the face hears it on its next push."""
        by = " ".join(str(item.get("by") or "Dad's double tap on Cordelia's face").split())[:120]
        result = self.engine.kill_eyes("dad", f"{by} ({body.name})", wait=False)
        self._set(body.key, last_eyes={"at": self.clock(), "opened": False, "killed": True})
        for other in self.bodies.configured().values():
            self.bodies.forget_face(other)
        self.bodies.push_face(body)
        return {"killed": True, "mode": result["mode"]["mode"]}

External links

Exercise

Give a device app of yours a sensor button that asks instead of acting: a tap sends a request to the component that owns the policy, and the sensor starts only when that owner's next state push says it is open, for a reason that names the tap. Then add a kill control that closes everything through the same channel behind an armed two-press (arm for a few seconds, ignore a second press within 0.4 s, fire on a deliberate second press). Test both with a fake owner that answers late, answers no, and never answers.
Hint
Keep no open flag of the device's own: show whatever the owner last said, and treat the tap only as a question. For the two-press, pass the clock in as a value so a test can press at t = 0, t = 0.2 and t = 1.0 without sleeping.

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.