ROS 2 Protected Execution¶
Audience: operators bringing up a protected actuator path on ROS 2 — #1310 Phase 4. This is the ROS 2 concrete instance of the domain-neutral Protected Execution Boundary model: it gives the robot execution exclusivity on top of the logical authority + faithfulness the governed bridge and enactment receipts already provide.
Untrusted autonomy nodes Governed ROS bridge Motor controller
(proposal DDS domain) ──▶ (the ONLY dual-domain member) ──▶ (execution DDS domain)
The bridge subscribes proposals on the untrusted domain, consults ADK, and republishes only authorized commands on the execution domain. The boundary makes that the only path: no autonomy node can publish the actuator topic on the execution domain directly.
The gap it closes¶
The SROS 2 bridge already gives you the mechanism —
distinct DDS domains and signed per-enclave permissions. But nothing validates
that you assembled it correctly: two enclaves could both hold --publish /cmd_vel, or governance could allow unauthenticated participants, and the graph
would still come up. The builtin.sros2 boundary provider turns the intended
exclusivity into a checked, fail-closed contract: a bundle declaring the boundary
refuses to activate unless exactly one enclave (the enforcer) can publish the
actuator topic on the execution domain.
Step 1 — Declare the boundary (deployment manifest, schema v1.8)¶
schema_version: "1.8"
execution_boundaries:
- id: rover_motion
action_kinds: [tool.ros2.topic.publish]
adapter:
id: governed_ros2_bridge
identity: /governed_ros2_bridge_real # the enforcer SROS 2 enclave
target:
kind: ros2.dds_domain
resource: /cmd_vel # the actuator topic
schema: geometry_msgs/msg/Twist
protection:
provider: builtin.sros2
provider_version: "1"
configuration:
mode: dds_domain_plus_sros2 # the strongest reference posture
proposal_domain: 99
execution_domain: 42
keystore_path: /etc/adk/keystore
enforcer_enclave: /governed_ros2_bridge_real
requirements:
exclusive_enforcement_principal: true
proposer_has_no_direct_access: true
preflight_validation: required # fail closed at activation
Modes: dds_domain_isolation (distinct domains only), sros2_permissions
(exclusive publish grant + no insecure fallback), or dds_domain_plus_sros2
(both — recommended). required preflight means a bundle whose boundary can’t be
validated is refused and the prior bundle stays live.
Step 2 — Provision the keystore so ONLY the enforcer publishes¶
Mint identities + permissions so the actuator publish grant on the execution domain (42) is held by the bridge enclave and nobody else:
autonomy ros2 keystore init --keystore /etc/adk/keystore --domain 42 --domain 99
autonomy ros2 keystore permissions --keystore /etc/adk/keystore \
--enclave /governed_ros2_bridge_real --domain 42 --domain 99 \
--publish /cmd_vel --subscribe /cmd_vel,/disable_safety
Every workload/robot enclave gets, at most, a subscribe grant on /cmd_vel
on domain 42 and its own publish grants on the proposal domain (99) — never a
publish grant on the actuator topic on the execution domain. That asymmetry is
exactly what the boundary validates. Governance for the execution domain must
positively protect it — a domain rule that covers domain 42 with
allow_unauthenticated_participants=false and enable_join_access_control=true
(the defaults from keystore init); an ungoverned domain or disabled join access
control is treated as an insecure fallback. Run participants under
ROS_SECURITY_STRATEGY=Enforce.
Step 3 — Verify (activation is the gate)¶
Install the manifest at runtime start (or via a verified release):
autonomy runtime start --policy /etc/adk/policy --deployment-manifest /etc/adk/deploy.yaml
If the keystore satisfies the boundary, startup proceeds. If it does not — a
second enclave can publish /cmd_vel on domain 42, the enforcer holds no grant,
the domains collide, or governance allows unauthenticated participants — startup
refuses fail-closed with an execution-boundary: reason naming the finding,
e.g.:
runtime start: execution boundary "rover_motion" [execution-boundary: bypass_detected]:
enforcement principal is not exclusive (bypass_publisher: non-enforcer enclave(s)
can publish the actuator topic "/cmd_vel" on domain 42: CN=/planner_node)
The bundle does not activate; the prior (valid) bundle keeps running. Each decision
on the protected kind then records execution_boundary_id + expected_adapter_identity
on the audit frame, and an enactment receipt from any
enforcer other than /governed_ros2_bridge_real is a deviation.
Failure modes¶
Symptom |
Cause |
Fix |
|---|---|---|
|
A non-enforcer enclave holds a |
Revoke that enclave’s actuator publish grant. |
|
The enforcer enclave has no |
|
|
|
Assign distinct |
|
Governance allows unauthenticated participants. |
|
|
Manifest pins a provider/version the runtime doesn’t register. |
Use |
Hardware validation¶
The end-to-end on-Orin scenario — the two-domain graph under Enforce, plus an
on-hardware rogue publisher that fails to reach the execution domain — is a HIL
procedure (the boundary provider is validated by host tests + keystore fixtures in
CI; live DDS-Security is not CI-gated). Run it on the Jetson Orin target under
harness/hil/ when validating a physical deployment.