Skip to content

feat: add Device Connect integration (on top of main)#370

Open
kavya-chennoju wants to merge 11 commits into
strands-labs:mainfrom
kavya-chennoju:feat/device-connect-on-main
Open

feat: add Device Connect integration (on top of main)#370
kavya-chennoju wants to merge 11 commits into
strands-labs:mainfrom
kavya-chennoju:feat/device-connect-on-main

Conversation

@kavya-chennoju

Copy link
Copy Markdown

Summary

Re-applies the Device Connect integration onto main's architecture, as an alternative to #52 (which targets dev). main and dev diverged structurally — main carries the productionized mesh/ subsystem and a Robot() factory in robot.py — so this branch ports the integration onto main rather than merging the dev-based branch wholesale (a direct merge produced a 367-file diff with broken robot.py).

What's included

  • strands_robots/device_connect/DeviceDriver adapters wrapping a Simulation / HardwareRobot (sim_driver, robot_driver, reachy_*), plus init_device_connect[_sync], GUIDE.md, setup.sh, and a control-loop smoke test. The drivers target main's Simulation API unchanged (start_policy, get_features, get_state, step, reset, _world / SimRobot fields).
  • robot.pyRobot("…").run() server hook attached by the factory. run() stops the auto-started mesh and brings the device online via Device Connect (the primary networking layer in server mode).
  • tools/robot_mesh.py — Device Connect dispatch layered underneath the existing safety gates. robot_mesh() still runs its rate limit, HITL approval (emergency_stop / broadcast), command validation, and audit first; only then does it dispatch via Device Connect, falling back to the built-in mesh when DC is unavailable or has no devices. DC dispatch re-applies the per-action validation/audit so it inherits the same safety. Gated by STRANDS_ROBOT_MESH_DC (default on; disabled in the test suite so mesh unit tests stay hermetic).
  • __init__.py / pyproject.toml — lazy DC exports + device-connect-edge / device-connect-agent-tools core deps.

Behavior change

emergency_stop / broadcast are HITL-gated and fail closed when called as a bare script (no operator to approve); they run from within a Strands agent loop on approval. GUIDE.md documents this.

Testing

  • 1236 passed / 3 skipped — the Device Connect tests together with main's robot_mesh safety, deep-mesh, and factory tests.
  • GUIDE D2D demo (peers / tell / emergency_stop) verified end-to-end against the latest arm/device-connect packages.

Notes

Test-isolation fixes: the DC test stubs prefer the real strands / device_connect_agent_tools packages instead of leaving leaking sys.modules mocks, and no longer delete strands_robots.tools.robot_mesh (which created a second module object and broke sibling tests' _resolve_mesh patches).

Re-applies the Device Connect integration from strands-labs#52
onto main's architecture (mesh/ subsystem, robot.py Robot factory),
rather than merging the divergent dev branch wholesale.

What lands:
- strands_robots/device_connect/: DeviceDriver adapters wrapping a
  Simulation / HardwareRobot (sim_driver, robot_driver, reachy_*),
  init_device_connect[_sync], GUIDE.md, setup.sh, smoke test. The
  drivers target main's Simulation API unchanged (start_policy,
  get_features, get_state, step, reset, _world/SimRobot fields).
- robot.py: Robot("...").run() server hook attached by the factory.
  run() stops the auto-started mesh and brings the device online via
  Device Connect (the primary networking layer in server mode).
- tools/robot_mesh.py: Device Connect dispatch layered UNDERNEATH main's
  existing safety gates. robot_mesh() still runs its rate limit, HITL
  approval (emergency_stop/broadcast), command validation, and audit
  first; only then does it try Device Connect, falling back to the
  built-in mesh when DC is unavailable or has no devices. DC dispatch
  re-applies the per-action validation/audit so it inherits the same
  safety. Gated by STRANDS_ROBOT_MESH_DC (default on; conftest turns it
  off so mesh unit tests stay hermetic and deterministic).
- __init__.py / pyproject.toml: lazy DC exports + device-connect-edge /
  device-connect-agent-tools core deps.

Tests:
- Ports test_device_connect_{drivers,all_robots,integration}.py. Their
  stubs prefer the real strands / device_connect_agent_tools packages
  (installed here) instead of leaving leaking sys.modules mocks, and no
  longer delete strands_robots.tools.robot_mesh (which created a second
  module object and broke sibling tests' _resolve_mesh patches).
- conftest.py disables the DC dispatch path during the suite.

Behavior change documented in GUIDE.md: emergency_stop / broadcast are
HITL-gated and fail closed when called as a bare script (no operator to
approve); they run from within a Strands agent loop on approval.

Verified locally: 1236 passed / 3 skipped (DC tests + main's robot_mesh
safety, deep-mesh, and factory tests together); GUIDE D2D demo
(peers / tell / emergency_stop) works end-to-end against device-connect
PR strands-labs#52 packages.

Local working branch for review; not pushed.
Comment thread pyproject.toml Outdated
Comment thread strands_robots/device_connect/GUIDE.md Outdated

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

cagataycali and others added 2 commits June 8, 2026 16:27
…UIDE

Addresses review feedback on strands-labs#370:
- pyproject: move device-connect-{edge,agent-tools} out of core dependencies into a new [device-connect] extra, and include it in [all]
- setup.sh: install [sim,device-connect] so the D2D demo still pulls in DC
- README: document the new device-connect extra in the install table
- GUIDE: clone strands-labs/robots.git instead of the fork feature branch
Clears the 29 code-scanning alerts on the new DC code:
- __init__: add the 6 lazy DC exports to the TYPE_CHECKING block so __all__ names are statically defined (py/undefined-export)
- reachy_transport + integration tests: document intentional empty excepts (py/empty-except)
- drop unused imports (json, math) and unused 'result' locals in driver tests (py/unused-import, py/unused-local-variable)
- all_robots test: probe optional deps via importlib.util.find_spec instead of side-effecting imports
- robot_mesh: replace the global _dc_connected flag with a _dc_state cell (py/unused-global-variable)
cagataycali and others added 2 commits June 9, 2026 14:05
robot_mesh could discover a Device Connect peer's advertised functions
(e.g. the Reachy's nod/look/playMove) but tell/send rejected them: every
command was forced through mesh.security.validate_command, whose
ALLOWED_ACTIONS allowlist describes the SO-100/SO-101 policy-dispatch
surface, not an arbitrary device's own function set. This blocked the
Device Connect PRs from driving non-policy devices.

Changes:
- security.validate_device_rpc(function, params): a dedicated validator
  for device-native RPC. Enforces an identifier charset on the function
  name (no shell metacharacters/dots/slashes/control bytes), bounds the
  params object (identifier-safe keys, JSON-serialisable, <=64 KB), but
  deliberately does NOT apply ALLOWED_ACTIONS.
- robot_mesh action='rpc' (+ function= param): validates via
  validate_device_rpc, then conn.invoke(target, function, params)
  directly. Inherits the existing rate-limit + audit machinery; falls
  back with a clear error when Device Connect is unavailable.
- tests/test_device_rpc_validation.py: 18 unit tests covering accept,
  not-gated-by-allowlist, charset/size/serialisation rejections, and
  param-copy semantics.

Verified live against a Reachy on the beta tenant (nod -> success) and
that a malicious function name (e.g. 'rm -rf /') is rejected.
lint clean (ruff check + format), mypy clean (only pre-existing
device_connect_agent_tools import-untyped note), 94 security tests pass.
feat(mesh): device-native RPC path for robot_mesh (action='rpc')
@cagataycali cagataycali added enhancement New feature or request iot labels Jun 11, 2026
strands-agent and others added 2 commits June 11, 2026 16:57
…ation

Harden the Device Connect integration across the agent dispatch path,
the device drivers, and the Reachy transport. All changes are
defence-in-depth and preserve existing behaviour on the trusted-path
defaults.

- robot_mesh broadcast: dispatch the validated, operator-approved
  command through Device Connect instead of re-parsing the raw caller
  string, so the executed action cannot differ from the approved one.
- robot_mesh rpc: route device-native rpc through the human-in-the-loop
  approval gate (surfacing the function name) so device-native
  functions are not invoked without explicit approval.
- drivers: validate policy_provider against the existing allowlist in
  both robot and sim execute() before starting a policy, preventing
  inference from being pointed at an arbitrary endpoint.
- drivers: add per-call caller authorization on state-mutating RPCs
  (execute/stop/step/reset) and validate the source of emergencyStop
  against an allowlist. Env-driven (DEVICE_CONNECT_RPC_ALLOW /
  DEVICE_CONNECT_ESTOP_ALLOW); permissive with a warning when unset,
  fail-closed when set. Uses get_rpc_source_device() from
  device-connect-edge.
- reachy: validate playMove move_name against a strict charset before
  interpolating it into the daemon REST path (prevents path traversal /
  query injection).
- reachy: support an optional bearer token (REACHY_DAEMON_TOKEN) on the
  daemon WebSocket and REST interfaces and warn loudly when the link is
  unauthenticated.
- transport: make Device Connect secure by default. allow_insecure now
  defaults to False and must be opted into explicitly (arg or
  DEVICE_CONNECT_ALLOW_INSECURE); a warning is logged whenever insecure
  mode is active. Removed the agent-side setdefault that forced insecure
  mode process-wide.
- GUIDE.md: document secure-by-default and the explicit insecure opt-in
  for local D2D trials.
- CI: when a PR touches the Device Connect integration, pin
  device-connect-edge / agent-tools to source via UV_OVERRIDE so the
  matching framework version is exercised.
- tests: add tests/test_device_connect_hardening.py covering all of the
  above.
The per-call caller ACL (DEVICE_CONNECT_RPC_ALLOW) was effectively
all-or-nothing on the agent path: device_connect_agent_tools clients are
anonymous, so the device always saw caller=None. Setting an allowlist
therefore denied every agent call (no allow-path was reachable), and the
behaviour wasn't documented.

Layer 1 — caller identity propagation (robot_mesh):
  - _agent_identity()/_with_identity() stamp _dc_meta.source_device into the
    DC command envelope from STRANDS_ROBOT_MESH_AGENT_ID (or
    DEVICE_CONNECT_CLIENT_ID). Wired into tell/send/rpc/stop/broadcast/
    emergency_stop. Anonymous callers unchanged (still fail-closed).

Layer 2 — honest authz semantics (_authz):
  - Document that the caller id is only a hard boundary under authenticated
    transport; under DEVICE_CONNECT_ALLOW_INSECURE it is self-asserted.
  - Log a one-time advisory when an allowlist is enforced over insecure
    transport.

Layer 3 — docs + test honesty:
  - Extract resolve_allow_insecure() and fix the stale "defaults to True in
    D2D" docstring (it defaults to False / secure).
  - GUIDE.md: drop the contradictory DEVICE_CONNECT_ALLOW_INSECURE=true from
    the mTLS Full-Infrastructure section; document the allowlist's agent-id
    requirement and advisory-under-insecure semantics.
  - Replace the tautological test_allow_insecure_defaults_false (it
    re-implemented the logic) with tests that call the real resolver and
    init_device_connect. Add coverage for the reachable anonymous-deny, the
    insecure advisory, and _dc_meta identity propagation. 21 -> 29 tests.
@cagataycali cagataycali moved this from Backlog to In review in Strands Labs - Robots Jun 12, 2026
…ardening

fix(device-connect): security hardening for the Device Connect integration
…y-hardening

fix(device-connect): make RPC caller-allowlist usable + honest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request iot

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants