Clean out the Pi Zero leftovers, rewrite the README, and add the one installer

Wi-Fi-off acceptance passed: with Wi-Fi blocked, live and after a reboot, the Pi
came up on the USB link alone, every node ran, and hermes mcp test robot and look()
worked. (A temporary guard that would have unblocked Wi-Fi was the safety net and is
removed; Wi-Fi is back on.)

Delete what the ROS design superseded: the Picamera2 MCP server and its unit, the old
installer and requirements, and the duplicate platform check. Move omarchy/ to
host/omarchy/. Rewrite README.md for this build: parts, physical steps (including the
Audio switch), setup, daily use, the troubleshooting learned the hard way, and the
security model.

pi/install.sh is now the single installer for a fresh Ubuntu 24.04 image, built from the
steps done by hand: ROS, the libcamera fork at a pinned commit, camera_ros, the venv and
models, the workspace, USB gadget, audio overlay, firewall (before PipeWire opens a
port), PipeWire, mixer and services. Run twice on the live robot, the second run changed
nothing and everything stayed healthy. It has not been run on a blank card.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
2026-10-05 00:49:21 -07:00
co-authored by Claude Sonnet 5.5
parent 841ddc3573
commit 9952c92bc5
11 changed files with 330 additions and 645 deletions
+29 -27
View File
@@ -223,8 +223,8 @@ returns a JPEG in 0.08 s, a wrong token is refused, the Wi-Fi path is dropped by
the face went `looking` -> `idle` on connect and `agent gone, sleeping` about 40 s after the client
left. The token is in `/etc/robot-mcp.env` (root:hermes, mode 640), generated by
`pi/install-robot-service.sh` and never printed; read it with `sudo cat`. The MCP SDK is in `~/venv`
(`pip install "mcp>=2"`). The old `pi/robot_mcp_server.py`, `pi/robot-mcp.service` and `pi/install.sh`
are superseded (package 5 rewrites the installer). **Not yet verified:** a real Hermes client, and
(`pip install "mcp>=2"`). The old Pi Zero files (`pi/robot_mcp_server.py`, `pi/robot-mcp.service`,
`pi/requirements.txt`, the old `pi/install.sh`, and `tools/`) were deleted (git has them). **Not yet verified:** a real Hermes client, and
whether Hermes passes MCP image content to the model (HANDOFF package 6).
**Hermes on talos is wired to it (2026-10-05, with the owner's authorisation).** Hermes Agent v0.21.5 is
@@ -264,34 +264,36 @@ unrelated photo scored 0.16. **Nobody is enrolled yet**; the owner enrols themse
## Next
1. Finish package 0b: Wi-Fi-off boot test, replug recovery, serial login check, NixOS snippet in
`host/nixos/`. A new Omarchy host with a GPU (runs the local
model "ornith") arrives this week: it needs only `host/omarchy/robot-usb-link.sh` and its SSH key
added to the Pi.
2. `hermes_head` and the servos when they arrive (then `head: true`), echo cancel and the `robot_mic`
naming once speakers arrive, a fan before any enclosure, the Wi-Fi-off boot test, a real Hermes
client against `hermes_mcp`, and the rest of `HANDOFF.md`.
3. Confirm the retuned listening detector live (the flicker fix is unit-tested only); a second person
in frame for the recognition test (not possible on 2026-10-04).
1. **Camera calibration** (needs the owner and a flat checkerboard, printed or on a screen, with the
square size measured): collect ~20 views at different poses, solve with OpenCV, write the
camera_info YAML so `camera_ros` stops logging the missing-file error. V2's perception needs it.
2. Waiting on hardware: the servos (`hermes_head`, the pan/tilt URDF, then `head: true`), the speakers
(playback, echo cancel, the `robot_mic` naming on the host, Hermes talking aloud), the fan (before
any enclosure), the splitter, the printed enclosure (measure first, see `enclosure/README.md`).
3. Still open: replug and second-host recovery of the USB link, the serial console login, an
`agent_timeout_s` check against a long idle Hermes session, the retuned listening detector live
(unit-tested only), a second person in frame, running `pi/install.sh` on a blank card, and the
NixOS host config (skipped for now by the owner).
4. A new Omarchy host with a GPU (it runs the local `ornith` model) is arriving: it needs only
`host/omarchy/robot-usb-link.sh` and its SSH key added to the Pi.
## Repository
Branch `master`. `.gitignore` covers `.remember/`, caches, `.venv/`, `*.orig`, `*.rej` and
`pi/50-cloud-init.yaml`. `tools/check-pi-platform.sh` duplicates `host/check-pi-platform.sh` and can
probably go.
`pi/50-cloud-init.yaml`. Layout: `ros/` (five packages), `pi/` (everything installed on the Pi),
`host/` (the owner's machines: `host/omarchy/` has the USB link script, Hermes and PipeWire config and
the launcher; `host/check-pi-platform.sh`), `enclosure/`, `docs/face-prototype.html`. `README.md` is
rewritten for this build. **`pi/install.sh` is the one installer** (re-runnable: run twice on the live
robot, the second run changed nothing, exit 0): it installs ROS, builds the libcamera fork (pinned
commit) and `camera_ros`, sets up the venv, models, workspace, USB gadget, audio overlay, firewall,
PipeWire, mixer and services. It has not been run on a blank card (no spare card). HANDOFF.md is the
original brief and has drifted; where they differ, this file wins.
## Security review notes (parked until the USB link is up)
## Security notes (all resolved)
An automated review of the initial commit flagged three items. All stem from the code predating
the USB-gadget design (HANDOFF.md package 0b); none is exposed until the Pi runs the services.
- `pi/robot_mcp_server.py`: plain HTTP, `ROBOT_MCP_BIND` defaults to `0.0.0.0`. Plan: default to
`10.55.0.2`, start after `usb0`.
- `pi/pipewire/50-network-server.conf`: TCP 4713 on all interfaces, unrestricted. Plan: keep,
comment that the firewall is the protection.
- `pi/nftables-robot.nft`: still the Pi Zero version (single host IP, any interface). Package 0b
rewrites it to accept only on the USB interface from `10.55.0.1`.
- `pi/install.sh` permissions looked fine (`/etc/robot-mcp.env` is `root:$USER`, 640).
The owner wants to confirm the real IPs on the link before changing any of this. If the USB
link doesn't work, revisit: TLS or a Tailscale-only bind for MCP and audio.
The automated review of the initial commit flagged three items, all from the Pi Zero design:
`ROBOT_MCP_BIND` defaulting to `0.0.0.0` (now the USB address only, with a bearer token), PipeWire on
TCP 4713 on every interface (now protected by the boot-time firewall, which loads first), and the
single-IP any-interface nftables file (rewritten: usb0 from 10.55.0.1 only). Later reviews added:
DDS is blocked on every interface and discovery is local-only; recognised names are not logged; node
parameters are clamped. SSH stays open on all interfaces (key-only) by the owner's choice, for V2.