microduck/docs/README.md
Pierre Rouanet aedddcbf8a docs: the WebRTC design, scoped to local signalling
architecture.md §5 states the requirement; this owns the mechanism. Local
signalling only: everything in it runs on the robot and works on a LAN with no
backend, because the remote case is defined in terms of the local one rather
than beside it.

The load-bearing choice is `webrtcsink` over `webrtcbin`. It brings a signalling
protocol, a session model and per-consumer encoder management, so what is left
to design is the control surface rather than the media plumbing — and that
protocol is what a remote bridge proxies, which is why the bridge is a relay and
not a translator. `run-signalling-server` (verified present in gst-plugins-rs
0.15.3, with host and port properties) means mediad runs the server in its own
process, so the separate gst-webrtc-signalling-server binary never has to be
built or shipped.

Four things it decides rather than defers:

  - Authorisation reuses `system.authenticate`. The signalling port is open on
    the LAN by necessity — loopback-only would force every session through a
    bridge — so the control channel serves that one method and refuses the rest
    until it passes, exactly as a BLE session does. Two schemes for two
    transports is how the newer one ends up weaker.
  - The control channel is a pipe to the JSON-RPC that already exists. btd is
    the precedent and three of its four files are transport-independent; the
    routing table should be lifted so the exhaustive match over proto::Call
    holds *per transport*, or WebRTC becomes the hole in it. Replies stay
    uncorrelated, so the pipe needs no per-method work.
  - Teleop carries sequence numbers. intents.rs takes last-writer-wins, which is
    correct over a unix socket and wrong over SCTP with maxRetransmits=0 — a
    twist from 80 ms ago can land after a fresher one and win. Transport
    property, so it belongs in mediad.
  - update.* mutations are refused, and the refusal says why: preflight already
    declines an update during a remote session, and restarting mediad drops the
    session the client is watching progress on.

And the thing this feature breaks. intents.rs says its slots are "single-writer
in practice"; a pad and a remote peer both driving at 50 Hz makes that false,
and the result is not a contest but two writers interleaving into one slot.
architecture.md §6 already calls for priority and handoff and the roadmap defers
it to M6 — WebRTC is what makes it due. That is the one part touching a file
robotd owns, and the part most worth arguing about before it is written.

Assisted-by: Claude:claude-opus-5[1m]
2026-08-24 18:19:16 +02:00

3.6 KiB

Docs

Start at the README if you have a robot and want to use it.

robot/ — you have a robot

cheatsheet.md Every robotctl command.
pair-a-gamepad.md Once per pad: pairing mode, pad pair, and what to do when it will not bond.
cheatsheet-dev.md The commands that need a dev board: branch builds, candidates, dev pushes.
dev-push.md Build on your machine and install on the board over ssh, with no CI run.
duck-btctl.md Every duck-btctl command — the robot over Bluetooth, from a laptop.
install-dev.md Setting up a board for development, from nothing.
install-by-hand.md The same install as separate commands, for testing one step at a time.

design/ — you are changing the daemon

How it works and why. These change rarely; when behaviour and a design doc disagree, the doc is the bug.

One page owns a mechanism, and the others link to it. The table below is that assignment: if a fact belongs to a page listed here, every other page says one sentence and points, rather than explaining it again. A fact written down in six places drifts in six directions, each of them locally reasonable — which is how six documents came to promise that updaterd and btd kept their old binaries until the next reboot, two releases after they stopped doing so, including the two pages someone reads while diagnosing exactly that. So when two documents disagree, the one that does not own the mechanism is the bug.

architecture.md The service split, the IPC contract, state ownership, safety and authority.
robotd-design.md The control loop: the Dynamixel bus and who owns the port, the model, sensing, observations, policy, safety.
updater-design.md The update engine: verification, atomic swap, health gate, rollback, release format.
restart-order.md Which unit restarts, at which step, on every path that moves current — and at boot.
app-path-design.md btd and configd — how a phone configures a robot over BLE.
remote-webrtc.md WebRTC sessions, signalling, and the control channel — how a peer drives and observes the robot.
boot-recovery-net.md Falling back to golden when the release that booted cannot start its daemons.

project/ — you are running the project

Dated records rather than reference. They describe a moment, and go stale on purpose.

roadmap.md Milestones, and what works today versus what is designed.
ci-setup.md One-time setup for the release pipeline: keys, secrets, rotation.
install-path-gap.md Why four install-path bugs reached a board, and what closed it. Closed.
slice-2-bringup.md What a real Radxa Zero 3W did with slice 2.
update-over-ble.md Driving the update path from a phone: what it turned up, and what rollback over a radio was decided on.

Elsewhere

../CONTRIBUTING.md Building, testing, repo layout, conventions, releasing.
../deploy/README.md What a robot image is configured with, and what provisioning actually does.