Skip to content

Fleet

Stage 5, a week: several robots on one mesh, a dashboard that sees them all, one e-stop that stops them all, and the postures that keep a stranger off the wire.

At the end of this rung two or more robots on two machines see each other on the mesh, an agent drives any of them through one tool with approvals in place, a dashboard shows the fleet live, and one e-stop stops every robot and keeps it stopped until the operator's signed resume says otherwise.

Left, a dashed layer, one LAN with mTLS by default, holding two dashed hosts. Host A: so101 real, Robot("so101", mode="real", mesh=True); so101_sim, Robot("so101"); the router chip tcp/127.0.0.1:7447, the first process listens and the rest connect. Host B: the dashboard, one more peer with the fleet page, the consent card and e-stop; g1 real, Robot("unitree_g1", mode="real"); the chip ZENOH_CONNECT=tcp/10.0.0.1:7447, how a second host joins. A two-headed wire between the hosts carries presence, state and RPC. Under the LAN the one green element, the safety topics: strands/safety/estop locks every peer that hears it, strands/safety/resume carries a proof. Right, across sites: a bridge peer, STRANDS_MESH_BACKEND=bridge, relays the same topics to AWS IoT Core, mTLS and a policy per thing; a wire claim never becomes a local fact. Footnote: the mesh is enrichment.Left, a dashed layer, one LAN with mTLS by default, holding two dashed hosts. Host A: so101 real, Robot("so101", mode="real", mesh=True); so101_sim, Robot("so101"); the router chip tcp/127.0.0.1:7447, the first process listens and the rest connect. Host B: the dashboard, one more peer with the fleet page, the consent card and e-stop; g1 real, Robot("unitree_g1", mode="real"); the chip ZENOH_CONNECT=tcp/10.0.0.1:7447, how a second host joins. A two-headed wire between the hosts carries presence, state and RPC. Under the LAN the one green element, the safety topics: strands/safety/estop locks every peer that hears it, strands/safety/resume carries a proof. Right, across sites: a bridge peer, STRANDS_MESH_BACKEND=bridge, relays the same topics to AWS IoT Core, mTLS and a policy per thing; a wire claim never becomes a local fact. Footnote: the mesh is enrichment.

1. Two peers on one machine

Mesh. STRANDS_MESH_LOCAL_DEV=true and two Robot(..., mesh=True) in two processes: the first listens on tcp/127.0.0.1:7447, the second connects, each sees the other in mesh.peers. The mesh is enrichment; a session that fails to open leaves the robot working without it.

2. Command the fleet

Fleet. send one peer an action, tell it a policy task, broadcast to all, subscribe to a peer's state topic. From an agent, the robot_mesh tool carries the same verbs with the gate in front of every one that moves a real robot. The wire vocabulary is a closed list; an action off it is refused before it is routed.

3. Second machine, real posture

Mesh again, postures. ZENOH_CONNECT=tcp/<host>:7447 joins another host. Turn STRANDS_MESH_LOCAL_DEV off and the default posture is mTLS: the CA, certificate and key trio, refused when any is missing, and an ACL file with default_permission: "deny" for production. strands-robots doctor says which posture you are in and what it refuses.

4. Stop everything

Safety and e-stop. emergency_stop() on any peer, or the dashboard's button, locks the local robot, stops it, broadcasts stop, publishes strands/safety/estop so every peer locks itself, and audits. Under lockout a peer answers only status, resume and stop. Set STRANDS_MESH_RESUME_PUBLIC_KEY (the operator key's public half) before you need it: a peer without one stays locked until someone restarts it.

Left, five steps in order: engage the local lockout, recording the time; stop this robot through the same dispatch a peer would use; broadcast stop and collect replies for three seconds, a peer that cannot stop is listed, not counted; publish strands/safety/estop, the one green element, so every peer that hears it locks itself; write the audit row. Right, under lockout a peer still answers status, stop and resume; everything else is refused and the refusal audited; stopping is never gated. Resume needs an assertion signed by the operator key; every peer holds only the public half, STRANDS_MESH_RESUME_PUBLIC_KEY, and refusals are throttled after five failures. The assertion names the lockout epoch and its target peers, and strands/safety/resume relays it so each named peer checks it itself. Footnote: a reply counts as stopped only if it says so.Left, five steps in order: engage the local lockout, recording the time; stop this robot through the same dispatch a peer would use; broadcast stop and collect replies for three seconds, a peer that cannot stop is listed, not counted; publish strands/safety/estop, the one green element, so every peer that hears it locks itself; write the audit row. Right, under lockout a peer still answers status, stop and resume; everything else is refused and the refusal audited; stopping is never gated. Resume needs an assertion signed by the operator key; every peer holds only the public half, STRANDS_MESH_RESUME_PUBLIC_KEY, and refusals are throttled after five failures. The assertion names the lockout epoch and its target peers, and strands/safety/resume relays it so each named peer checks it itself. Footnote: a reply counts as stopped only if it says so.

5. See it

Dashboard. Every peer as a card with its state, cameras and freshness, the consent card where the operator answers the gate, the e-stop that reaches the whole fleet. Behind a bridge, bridges relays the same topics to AWS IoT Core for a second site.

You now have a fleet: several robots on one wire, one tool that drives any of them with a person in the loop, and one stop that holds. Everything past this point is the reference: tools, configuration, refusal codes.

Edit page