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

A Satellite Is a Part, Not a Second Oriel

~16 min · distributed-systems, fail-closed, outbound-only, bearer-tokens, single-authority

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

A Camera on Another Mac

Sooner or later a camera Pippa should be able to look through is plugged into a different Mac. The tempting design is a second Oriel on that machine: its own engine, its own store, its own eyes gate, its own page. That design gives the house two gates to keep in step, two places where "closed" might mean different things, and two records of who looked. Dad's framing (2026-11-02) was the opposite: Pippa looks through that camera the way she looks through the cameras at home, with one engine and one eyes gate. Macs are parts, like cameras (backend/satellites.py).

What a Satellite Is, and Is Not

A satellite is another Mac with two things on it:

  • its own signed OrielRecorder, still the only process there that opens a camera, under that Mac's own Camera grant;
  • an agent (backend/satellite_agent.py) that relays between that recorder and the engine.

It has no engine, no store, no gate and no page. Every decision stays on the engine's Mac. The engine keeps a channel folder per satellite, laid out like the home recorder's, and a RecorderLink with the same gate writes it. One object, RecorderLinks, puts the home link and every satellite's behind a single face and sends each want, peek and move to the link of the Mac its device lives on. The consequence is the important part: a satellite camera is closed by default, opened only by an approval that names it, and parked when that approval closes, the way Manual treats a camera at home. The eyes modes never open it: a satellite's unique ID carries its Mac's name, so the gate counts it as outside home hardware and keeps it Manual in every mode. That one prefix is all the gate had to learn about satellites.

The Agent Dials Out. Nothing Listens There.

The agent opens no port. It long-polls the engine's HTTPS twin for the desired state, writes its recorder's desired.json with the engine's folder mapped to its own, posts the recorder's status.json once a second, and hands back each peek and move result. That is the same file channel from track two, stretched across a network with the direction fixed: the satellite asks, the engine answers.

The agent proves who it is with a bearer token minted on the engine's Mac. The engine keeps only its SHA-256, so a copy of the engine's config holds no usable token; the satellite keeps the token itself in a file readable only by its own account. A leaked config therefore cannot impersonate a satellite, and a stolen satellite token can be revoked by minting a new one.

Fail Closed Across a Network

A network adds a failure the home Mac never had: the engine can close the gate while the satellite cannot hear it. The agent's answer is to treat silence as a closed gate. When the engine has not answered for 15 seconds, the agent empties its recorder's desired state, so a gate that closes while the network is down leaves nothing open. Two more rules narrow what can leave the satellite at all:

  • The agent drops any recording from what it relays. Satellites do looks and moves, not recordings, for now. A session that names a satellite camera is refused with that reason: its files would live on that Mac's disk, and nothing indexes them yet.
  • Virtual cameras (screen-sharing or streaming software's fake devices) never leave the satellite. A real USB camera's unique ID is a USB location; a virtual camera's is a UUID, and the agent forwards only the first kind.

An ID That Says Where

Track two taught that a camera's unique ID is a place on a USB bus. Across Macs, the same place exists on every machine, so a satellite's IDs carry its name: <name>/<local unique ID>. The engine adds the prefix to everything the agent reports and strips it from what the agent fetches. Because of it, a camera on another Mac is never mistaken for an absent home camera of the same model, and a port move there is recognized by the serial number the agent read from its own Mac's device registry.

One small habit from the first install is worth copying. After the Camera grant was given to the recorder, Dad checked the privacy list on that Mac for anything else holding the camera (a media converter, a terminal, the ssh daemon). An outbound agent and a single gate mean little if some other process on the satellite can open the device on its own.

Code

Fail closed when the engine goes quiet, and forward only real USB cameras (trimmed excerpt, backend/satellite_agent.py)·python
LOST_SECONDS = 15.0


def real_camera(device: dict[str, Any]) -> bool:
    """A USB camera (its unique ID is a USB location, vendor and product);
    virtual cameras have UUIDs and stay here."""
    return usb.parse_uid(str(device.get("uid") or "")) is not None


class Agent:
    def guard_lost(self) -> bool:
        """Empty the recorder's desired state when the engine is out of reach."""
        if self.emptied or self.clock() - self.contact < LOST_SECONDS:
            return False
        self._write_local({"version": 1, "written_at": self.clock(), "inputs": {}, "peeks": [], "moves": [],
                           "note": "the engine has not answered; nothing stays open"})
        self.emptied = True
        log.warning("no answer from the engine for %.0f s: the recorder's desired state is empty",
                    self.clock() - self.contact)
        return True
Keep a hash, not the token, and prefix every remote ID with its Mac (runnable sketch)·python
import hashlib
import hmac
import secrets

SEP = "/"
registry = {}                                   # name -> {"token_sha256": ...}


def mint(name):
    token = secrets.token_urlsafe(32)
    registry[name] = {"token_sha256": hashlib.sha256(token.encode()).hexdigest()}
    return token                                 # shown once, never stored here


def name_of(token):
    digest = hashlib.sha256(token.encode()).hexdigest()
    for name, row in registry.items():
        if hmac.compare_digest(row["token_sha256"], digest):
            return name
    raise PermissionError("unknown satellite token")


def join_uid(name, local):
    return f"{name}{SEP}{local}" if name else local


token = mint("studio")
print(name_of(token))
print(join_uid("studio", "0x1200000abcd1234"))   # an invented ID
try:
    name_of("guessed")
except PermissionError as exc:
    print("refused:", exc)

External links

Exercise

Extend the sketch into a tiny agent loop: a fake engine returns a desired state (one open camera) for the first three polls and then stops answering. Each tick, the agent records the time of the last answer, and when 15 simulated seconds pass without one it writes an empty desired state. Print what the recorder would have open on every tick. Then add a second satellite and show that one satellite's token cannot fetch the other's desired state.
Hint
Use a fake clock you advance by hand, not time.sleep. The authentication step should return a satellite name, and every later read should be scoped to that name, never to a name the caller passes in.

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.