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

A Play Session Is Dad's Word

~17 min · sessions, consent, local-network-privacy, ssh-forced-command, stop-paths

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

From Pressing Buttons to Playing

The one-shot hands of the first lessons could press a button and report what changed. After a demo where that was all Pippa could do, Dad pointed out the gap: if this is a game Pippa plays on her own, she should be able to really play it, with his approval, and the only reason she could not was that nobody had wired it yet. The result is the play session, a second mode owned by the engine: one Remote Play connection held open for a whole span, the whole controller available, and a clear beginning and end.

Opened Only on Dad's Word

open_play is the same kind of action as open_eyes: it runs only with publish=true and Dad's own words as the instruction, found by cwkPippa's door in his recent messages. It names the console and the game title Dad approved (the console's discovery answer does not report the running title reliably, so the title is taken from his word), and it lasts 60 minutes by default, at most 480. It refuses while an Oriel watch session is recording, while the console sleeps (never woken), and while the stop switch on the Pippa Home page is on or cannot be read. Every open and every refusal goes into the activity log with his words.

There is one other way in, and it is his too. His Solo press on one of his own screens (the Pippa app, a Cordelia face), each of which has already verified his PIN session on its own side, opens the very same session: the code treats his press as his word.

Once open, the session stays attached: no reconnect per input. Pippa sends a sequence of steps, each holding buttons, sticks and triggers for a duration, gliding sticks over a ramp. When a sequence ends the client lets go of every input, unless the sequence asked to keep its last step a moment longer (at most 5 seconds, after which the client lets go by itself, a dead man's grip) or to hold it until Pippa's next input. A newer sequence takes over an older one within a tick, so a walk can carry on while Pippa looks and decides, and a change of mind is immediate.

Spoken Words Never Stop It

This is the rule that surprises people. A play session is not stopped by Dad saying 그만. He explained why before a broadcast: he may say "stop" meaning nothing in particular, dictation can mishear him, and a single wrong stop cuts a broadcast in half. His coaching is conversation, never a control. So the session ends only on mechanical signals:

  • On Air turning off after it was on (read every half second on a thread of its own, no brain in the path);
  • Dad's Stop now on Oriel's page (stopping is always safe, so any page or local tool may);
  • the session's expiry;
  • the stop switch on the Pippa Home page (read every 2 seconds; unread for 30 seconds ends it too);
  • the client's own session ending, or the engine stopping;
  • close_play, when Pippa's own play is done.

In every case the client lets go of every input first. And while a session is open, the page pins an amber Pippa holds the console strip with the title, the expiry, Dad's words and the measured latencies, and the deploy guard refuses a restart under it.

The Relay Around the Local Network Gate

Building play turned up one of macOS's sharper edges. On recent macOS, access to the local network is a privacy permission per process, and Oriel's engine is a launchd agent that never got through the consent prompt. Probed on the home Mac: the hands client started under launchd got "No route to host" looking for the console, while the same binary started over ssh found it at once. A process born under sshd passes the gate.

The house rule is never to "fix" this in System Settings (a grant there is per Mac, per binary, invisible to the repository, and lost on reinstall). Instead the engine starts the client through a loopback ssh relay: ssh pippa@127.0.0.1 -- session, with a dedicated key whose line in authorized_keys allows only connections from loopback, restricts everything, and forces one command, a relay script that runs nothing but the client's session and discover. Whatever the key asks for, sshd runs only that. Secrets still ride stdin. If the engine dies, ssh closes, the client reads end-of-input and lets go of the controller. Adding the key line changes her account's authorized keys, so it is run only on Dad's yes, and the installer proves it: discovery runs through the relay, and a plain id is refused.

Measured in the first field run (20 inputs in a live game): input to first changed stream frame 130.6 ms median, p95 206 ms; the look's frame 29 ms old. With a model deciding each step, the loop is the model's turn (seconds), so menus and exploration are playable as they are, and combat runs as planned sequences and holds rather than frame-by-frame reactions.

Code

A forced-command key line: loopback only, restricted, one command whatever is asked (the shape of Oriel's relay key; the path is illustrative)·text
from="127.0.0.1,::1",restrict,command="$APPS/OrielHands/oriel-hands-relay" ssh-ed25519 AAAA... oriel-hands-relay

# sshd ignores the command the client asked for and runs the forced one;
# the relay reads SSH_ORIGINAL_COMMAND and allows only 'session' and 'discover'.
A relay script that allows exactly two verbs (illustrative, in the spirit of oriel-hands-relay)·bash
#!/bin/zsh
set -eu
# Invoked by sshd as the forced command. Anything else the caller asked for is refused.
set -- ${=SSH_ORIGINAL_COMMAND:-}
case "${1:-}" in
  session)  shift; exec "$APPS/OrielHands/oriel-hands" session "$@" ;;
  discover) shift; exec "$APPS/OrielHands/oriel-hands" discover "$@" ;;
  *)        print -u2 "refused: only session and discover run here"; exit 7 ;;
esac
Stop on mechanical signals only, and let go of everything first (runnable sketch)·python
def should_end(state):
    """Every way a play session ends; spoken words are not on the list."""
    reasons = []
    if state["on_air_was"] is True and state["on_air_now"] is False:
        reasons.append("On Air turned off")
    if state["stop_pressed"]:
        reasons.append("Dad pressed Stop now")
    if state["now"] >= state["expires_at"]:
        reasons.append("the session expired")
    if state["home_switch"] is True or state["home_switch_unread_s"] >= 30:
        reasons.append("the Pippa Home stop switch")
    return reasons


state = {"on_air_was": True, "on_air_now": None, "stop_pressed": False, "now": 100, "expires_at": 3600,
         "home_switch": False, "home_switch_unread_s": 0, "heard": "그만"}
print(should_end(state) or "keep playing (an unread On Air and a spoken word end nothing)")
state["on_air_now"] = False
print(should_end(state))

External links

Exercise

Create a throwaway SSH key and add it to your own authorized_keys with from="127.0.0.1",restrict,command="/path/to/relay.sh", using a relay script that allows exactly one harmless verb (say, uptime). Then prove both halves: ssh -i key you@127.0.0.1 uptime works, and ssh -i key you@127.0.0.1 id is refused. Remove the key line when you are done.
Hint
Inside the relay, the command the client asked for is in SSH_ORIGINAL_COMMAND. Match it against an allow list and exit non-zero with a message for anything else. Test from a non-loopback address too, if you can: the from= option should refuse it before the relay even runs.

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.