feat(protocol): add restart_daemon DataChannel command#1131
Open
tfrere wants to merge 1 commit into
Open
Conversation
Recovery hatch for a degraded daemon (e.g. closed motor controller asserting on every command) over the same typed WebRTC transport already used for control, with no SSH or LAN-only HTTP access needed. Same effect as POST /api/daemon/restart, but reachable from a remote (Central-routed) peer. The dispatcher acks the caller before scheduling the restart so the client can distinguish "request accepted" from a transport failure (stopping the daemon tears down the media pipeline, which closes the data channel - the ack would otherwise never reach the caller). Daemon parameters (sim mode, serial port, kinematics, audio, localhost_only, ...) are preserved from the previous start(). The restart is conservative by default: the robot is NOT put to sleep before stopping (goto_sleep_on_stop=False), and the wake-up trajectory is NOT replayed after (wake_up_on_start=False). Both flags can be set to opt in. Wiring is decoupled: Backend exposes set_daemon_restart_handler() and Daemon.start() passes Daemon.restart in. This avoids a circular daemon -> backend -> daemon import and lets non-daemon backends (tests, future transports) leave the hook unset, in which case restart_daemon returns a clear error to the caller. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
restart_daemoncommand to the WebRTC DataChannel protocol so a remote peer can recover a degraded daemon (e.g. closed motor controller asserting on every command) without SSH or LAN-only HTTP access. Same effect asPOST /api/daemon/restart, but reachable from a Central-routed peer using the typed transport already used for robot control.Why
Today, when the motor controller dies mid-session (
Motor controller not initialized or already closed.repeating in journalctl), the daemon keeps reportingmotor_mode: \"enabled\"while every command silently asserts. The only recovery paths are:sudo systemctl restart reachy_miniover SSH, orPOST /api/daemon/restartfrom localhost.Neither is reachable from a browser running in a Hugging Face Space (or any remote app routed through the Central signaling relay). This adds a recovery hatch over the same WebRTC channel the app is already using.
Design
Three small changes, decoupled via a callback so the backend doesn't need to import the daemon (avoids the circular
daemon → backend → daemonimport).1. Protocol (
io/protocol.py)New Pydantic command:
Defaults are conservative: robot stays where it is (no sleep before stop, no wake-up trajectory replay). Both flags can be set explicitly to opt in.
2. Backend (
daemon/backend/abstract.py)Backend.set_daemon_restart_handler(handler): setter wired byDaemon.start().process_command:3. Daemon (
daemon/daemon.py)After
_setup_backend():Re-wired on every
start()because each start builds a new backend instance. Daemon parameters (sim mode, serial port, kinematics, audio, localhost_only, ...) are preserved automatically byDaemon.restart()from_start_params.Behaviour
start().goto_sleep_on_stop=False).wake_up_on_start=False)./api/ws): also dropped along with the daemon teardown - same as aPOST /api/daemon/restart.Test plan
Smoke-tested locally:
command_adapter.validate_python({\"type\": \"restart_daemon\", \"wake_up_on_start\": true})parses correctly with the right defaults.{\"error\": \"restart_daemon: no daemon lifecycle handler wired\", \"command\": \"restart_daemon\"}.{\"status\": \"ok\", \"command\": \"restart_daemon\", \"scheduled\": true}synchronously, then the handler is invoked with the requested kwargs on the next event-loop tick.Pre-merge checklist:
restart_daemonfrom a browser peer, confirm the daemon restarts, motor controller comes back, and a fresh WebRTC session reconnects cleanly.reachyMini.restartDaemon()is intentionally not in this PR; should be a follow-up that lands onjs/reachy-mini.jsonce the protocol is merged here).Out of scope
reachyMini.restartDaemon()): follow-up PR.process_command(). WS peers can already usePOST /api/daemon/restartlocally.Made with Cursor