Summary
When zenoh-plugin-ros2dds and zenoh-plugin-remote-api are loaded in the same zenohd v1.9.0 router, DDS-originated publications bridged into the Zenoh key-space by the ros2dds plugin never reach subscribers declared via the remote_api WebSocket interface. The reverse direction (WS → Zenoh → ros2dds → DDS) works correctly.
Environment
| Component |
Version |
| zenohd |
1.9.0 (rustc 1.93.0) |
| zenoh-plugin-ros2dds |
bundled .so from eclipse/zenoh:1.9.0 image |
| zenoh-plugin-remote-api |
bundled .so from same image |
| @eclipse-zenoh/zenoh-ts |
1.9.0 (npm) |
| ROS 2 distro |
Jazzy |
| RMW |
rmw_cyclonedds_cpp |
| Platform |
NVIDIA Jetson AGX Thor, Linux 6.8 aarch64 |
| Deployment |
Docker Compose (zenohd, audio/ROS2 node, web/Node.js containers) |
zenohd configuration (zenohd.json5)
{
mode: "router",
listen: { endpoints: ["tcp/0.0.0.0:7447"] },
scouting: { multicast: { enabled: false }, gossip: { enabled: false } },
plugins: {
ros2dds: {
__path__: "/libzenoh_plugin_ros2dds.so",
domain: 0,
namespace: "/",
allow: {
publishers: ["/chip/.*"],
subscribers: ["/chip/.*"],
service_servers: ["/chip/.*"],
service_clients: ["/chip/.*"],
action_servers: ["/chip/.*"],
action_clients: ["/chip/.*"],
},
},
remote_api: {
__path__: "/libzenoh_plugin_remote_api.so",
websocket_port: "0.0.0.0:10000",
},
},
}
What works
| Path |
Direction |
Status |
| WS client A publishes → WS client A subscriber |
same-session |
works |
| WS client B publishes → WS client A subscriber |
cross-session |
works |
WS client publishes chip/audio/config → ros2dds Route Subscriber → ROS2 node receives |
Zenoh → DDS |
works |
What does NOT work
| Path |
Direction |
Status |
ROS2 node publishes /chip/audio/utterance → ros2dds Route Publisher → Zenoh key chip/audio/utterance → remote_api WS subscriber |
DDS → Zenoh → WS |
broken — handler never fires |
This applies to all topics bridged by ros2dds, not just one. A wildcard subscriber on chip/audio/** receives zero DDS-bridged samples while receiving self-published and cross-session WS-published samples correctly.
zenohd logs confirm routes are created
INFO zenoh_plugin_ros2dds: Node /audio_node declares Publisher /chip/audio/utterance: std_msgs/msg/String - Allowed
INFO zenoh_plugin_ros2dds::routes_mgr: Route Publisher (ROS:/chip/audio/utterance -> Zenoh:chip/audio/utterance) created
The ROS2 node is actively publishing (confirmed via application logs showing wake-word fires and utterance storage), but zero samples reach any WS subscriber.
Minimal reproduction script (zenoh-ts, runs inside the web container)
import { Config, Session } from '@eclipse-zenoh/zenoh-ts';
// Session A: subscriber
const sessionA = await Session.open(new Config('ws/zenohd:10000'));
let fromDDS = 0, fromWS = 0;
await sessionA.declareSubscriber('chip/audio/**', {
handler: (sample) => {
fromDDS++; // should increment when ROS2 node publishes
console.log('GOT SAMPLE, payload size:', sample.payload().toBytes().length);
}
});
// Session B: publish via WS (simulates non-DDS traffic)
const sessionB = await Session.open(new Config('ws/zenohd:10000'));
const pub = await sessionB.declarePublisher('chip/audio/utterance');
await pub.put(new TextEncoder().encode('{"test":"from_ws"}'));
fromWS++; // will be received by Session A
await new Promise(r => setTimeout(r, 15000));
// During this 15s window, the ROS2 node published multiple messages on
// /chip/audio/utterance (confirmed in its logs).
console.log('From WS:', fromWS, 'From DDS bridge:', fromDDS);
// Output: From WS: 1 From DDS bridge: 0
// Expected: From DDS bridge: >0
Result: fromDDS stays 0 despite active ROS2 publishers. fromWS correctly increments from the cross-session WS publish.
Hypothesis
The ros2dds plugin's internal Zenoh publisher (created for each Route Publisher) either:
- Does not propagate its publications through the router to
remote_api plugin subscribers, or
- The
remote_api plugin's subscription declarations don't register as "matching" with the ros2dds plugin's publishers, so the plugin never forwards DDS data into the Zenoh key-space
The asymmetry (Zenoh→DDS works but DDS→Zenoh→WS doesn't) suggests the issue is in how the ros2dds plugin's publications interact with the router's forwarding to the remote_api plugin, rather than a DDS discovery or QoS problem.
Workaround
Direct HTTP POST from the ROS2 container to the WebSocket client's HTTP backend, bypassing the Zenoh data plane for this message type.
Cross-reference
The remote_api plugin lives in eclipse-zenoh/zenoh-ts. The bug may originate there rather than in ros2dds — filing here since the ros2dds plugin is the publication source.
Summary
When
zenoh-plugin-ros2ddsandzenoh-plugin-remote-apiare loaded in the samezenohdv1.9.0 router, DDS-originated publications bridged into the Zenoh key-space by the ros2dds plugin never reach subscribers declared via the remote_api WebSocket interface. The reverse direction (WS → Zenoh → ros2dds → DDS) works correctly.Environment
rustc 1.93.0).sofromeclipse/zenoh:1.9.0image.sofrom same imagezenohd configuration (zenohd.json5)
What works
chip/audio/config→ ros2dds Route Subscriber → ROS2 node receivesWhat does NOT work
/chip/audio/utterance→ ros2dds Route Publisher → Zenoh keychip/audio/utterance→ remote_api WS subscriberThis applies to all topics bridged by ros2dds, not just one. A wildcard subscriber on
chip/audio/**receives zero DDS-bridged samples while receiving self-published and cross-session WS-published samples correctly.zenohd logs confirm routes are created
The ROS2 node is actively publishing (confirmed via application logs showing wake-word fires and utterance storage), but zero samples reach any WS subscriber.
Minimal reproduction script (zenoh-ts, runs inside the web container)
Result:
fromDDSstays 0 despite active ROS2 publishers.fromWScorrectly increments from the cross-session WS publish.Hypothesis
The ros2dds plugin's internal Zenoh publisher (created for each
Route Publisher) either:remote_apiplugin subscribers, orremote_apiplugin's subscription declarations don't register as "matching" with the ros2dds plugin's publishers, so the plugin never forwards DDS data into the Zenoh key-spaceThe asymmetry (Zenoh→DDS works but DDS→Zenoh→WS doesn't) suggests the issue is in how the ros2dds plugin's publications interact with the router's forwarding to the remote_api plugin, rather than a DDS discovery or QoS problem.
Workaround
Direct HTTP POST from the ROS2 container to the WebSocket client's HTTP backend, bypassing the Zenoh data plane for this message type.
Cross-reference
The
remote_apiplugin lives in eclipse-zenoh/zenoh-ts. The bug may originate there rather than in ros2dds — filing here since the ros2dds plugin is the publication source.