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

Closed Until Dad Says So

~15 min · consent, camera-gate, approvals, privacy-by-design, audit-log

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

A Rule That Had to Be Code

One day after Oriel was born, before its eyes had looked at anything that mattered, Dad set the rule they would live under:

아빠가 보라고 승인할 때만 봐야 해. 아무 때나 보면 안 돼. 이건 게이트로 걸어둬. 명시적으로 카메라 사용을 승인할 때만 보도록. — 아빠 (2026-11-02)

Look only when Dad approves the look. Not whenever. Put it behind a gate. Only on explicit approval of camera use. The last two sentences are the engineering instruction: not a habit Pippa promises to keep, a gate, in code, that refuses when the approval is not there. A camera in a family home is watching people in their own space, and a promise held only in a model's instructions is exactly as strong as the model's attention on that turn. A gate is as strong as the code path it sits on.

Since 2026-11-07 this original gate is one of three eyes modes (the next lesson), called Manual, and it is still the rule for every network camera and every camera on another Mac in every mode. Understanding it first makes the modes easy.

Closed by Default, for Every Camera

Every input that is a real device is gated. Only the synthetic test pattern is not a camera. A device seen for the very first time is gated before it has a role, so plugging something new in never opens anything. Even the deck that usually carries the game console is gated, because its other input can carry a desk camera when the external recorder switches to it: the gate does not trust a label to say what a device shows.

What the gate guards is everything that turns a device into a picture or a movement: every look, replay, crop and frame on a gated input; every recording (and a running session whose cover ends is stopped); every peek; every gimbal read or move; every bookmark save. And it stands on the recorder channel itself: RecorderLink asks the gate before it writes a want, a peek or a move into desired.json, and a link built without a gate refuses everything. A command-line tool is refused exactly the way a brain is.

What Opens It, and Only This

  • Dad's words, verified elsewhere. The app action open_eyes runs only with publish=true and Dad's own words as the instruction. Those words are not taken on Pippa's say-so: cwkPippa's app door has already found them in one of his last twelve messages, in his own voice, the same check every publish in the family uses. The approval names its inputs (all cameras when none are named) and lasts 30 minutes by default, at most 240, or until the session it was opened for ends.
  • Dad's hand on Oriel's page, signed in with his family PIN session: "Open 30 min", or starting a session there, which opens that session's inputs until it ends.
  • His On Air, a standing word he gave: from the broadcast's Prep stage until On Air goes off, the decks' recording holds the decks open. The look-around cameras are never in it.
  • His tap on a phone body's face: the camera button on a Cordelia face opens that body's two cameras for five minutes, recorded as his own approval, and a second tap closes them (track nine).

Nothing else opens it: no brain's judgment that Dad "probably wants" a look, no script, no test fixture in production code. Inference never counts as approval.

What Closes It

An expiry, the end of the session an approval was bound to, the page's "Close now", the close_eyes action, or the kill switch: when Dad says 그만 or 닫아, Pippa calls kill_eyes, which closes everything and also drops the mode to Manual (lesson four). Closing needs no words from anyone: anyone may make the gate tighter.

Every Refusal Teaches the Caller

A refusal does not just say no. It names the input and its device ID, says why it is closed in the current mode, and ends with how to open it: ask Dad, and on his yes call open_eyes with his words. Every open, every close and every refusal is a row in the activity log, with Dad's words where there were any. The gate is not a black box that sometimes says no; it is a door with a sign on it.

Code

Every read and move asks this first (trimmed excerpt, backend/gate.py Gate.check)·python
def check(self, row, purpose: str) -> None:
    if not self.gated(row):                     # only the test pattern is not a camera
        return
    uid = row.get("device_uid") or row["id"]
    if self.is_halting():                      # the kill switch is closing everything
        raise GateClosed(f"eyes closing on the kill switch: ... {purpose} refused")
    if self.is_closing(str(row["id"])):         # parked before the gate closes
        raise GateClosed(f"eyes gate closing: ... {purpose} refused")
    if self.covering(str(row["id"])) is not None:   # a live approval with Dad's words
        self.used(row, purpose)
        return
    why = self.mode_covers(row)                # Auto, or Broadcast while On Air is on
    if why is not None:
        self.used(row, purpose, why=why)
        return
    self.store.note("oriel", "gate_refused", input=row["id"], uid=uid, purpose=purpose,
                    mode=self.mode_name())
    raise GateClosed(f"eyes gate closed: {row['name']} ({uid}) {self.closed_because(row)}; "
                     f"{purpose} refused. {HOW_TO_OPEN}")
An approval needs words, inputs and an end (trimmed excerpt, backend/gate.py Gate.open)·python
DEFAULT_MINUTES = 30
MAX_MINUTES = 240
HOW_TO_OPEN = ("ask Dad; on his yes, app do cwkOriel open_eyes with publish=true and his words as instruction")


def open(self, *, input_ids, words, source, actor, minutes=None,
         until_session_ends=False, session_id=None, **who):
    words = " ".join((words or "").split())[:WORDS_MAX]
    if len(words) < 2:
        raise GateClosed("the eyes gate opens only with Dad's own words")
    if not input_ids:
        raise ValueError("name the inputs the approval opens")
    now = self.clock()
    expires = None
    if session_id is None:
        minutes = float(minutes if minutes is not None else DEFAULT_MINUTES)
        if not 1 <= minutes <= MAX_MINUTES:
            raise ValueError(f"an approval lasts 1 to {MAX_MINUTES} minutes")
        expires = now + minutes * 60
    approval = self.store.add_approval(opened_at=now, expires_at=expires, until_session_ends=until_session_ends,
                                       session_id=session_id, inputs=sorted(set(input_ids)), words=words,
                                       source=source, actor=actor)  # and who asked
    self.store.note(actor, "gate_opened", approval=approval["id"], inputs=approval["inputs"],
                    words=words, source=source, expires_at=expires)
    return approval

External links

Exercise

Write a minimal gate for a pretend camera: open(inputs, words, minutes) that refuses empty words and an out-of-range duration, check(input, purpose) that raises with the reason and how to open when no live approval covers the input, and close() that needs no words. Log every open, close and refusal. Then try to read a frame through a function that does not call check, and write the test that would catch it.
Hint
The last part is the important one. Oriel's answer is structural: the only object that can ask the device for anything (its RecorderLink) calls the gate itself, and a test scans the codebase for any other path to a camera. Your test can grep your own source for the device-opening call outside the one allowed module.

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.