# Research: Zenoh Version Mismatch — Root Cause & Solution

**Date:** 2026-04-13 (session 88)
**Status:** Solution found — build rmw_zenoh from source (Jazzy branch = zenoh 1.7.1)

## Problem

Native Python (`zenoh_ros2_sdk` + `eclipse-zenoh` 1.9.0) cannot communicate with Docker ROS2 (`rmw_zenoh_cpp` from apt, bundles zenoh 0.x). Messages published by one are invisible to the other — incompatible wire protocols.

## Root Cause

The **apt package is stale**. The Jazzy branch on GitHub has been updated to zenoh 1.7.1, but the apt repo (`ros-jazzy-rmw-zenoh-cpp` 0.2.9, built 2026-01-22) still ships zenoh 0.x.

| Component | Version | Zenoh wire protocol |
|-----------|---------|-------------------|
| `eclipse-zenoh` (pip, native) | 1.9.0 | v1 |
| `rmw_zenoh_cpp` (apt, Docker) | 0.2.9 (2026-01-22) | v0 (stale!) |
| `rmw_zenoh` (GitHub jazzy branch HEAD) | pinned to zenoh 1.7.1 | v1 |
| `zenoh_ros2_sdk` (pip) | 0.1.7, requires `eclipse-zenoh>=0.10.0` | follows installed zenoh |

## Evidence

### GitHub jazzy branch = zenoh 1.7.1
From `zenoh_cpp_vendor/CMakeLists.txt` on jazzy branch:
- zenoh-c commit: `73e7a0bdcf78b8263a02b7aee453ee6de8187589`
- zenoh-cpp commit: `47a80d345d74ebef16f3783423bf3bbd89cd6c30`
- Comment: "Set VCS_VERSION to 1.7.1 commits"

### Version bumps were backported to Jazzy
- PR #405 (Jan 10 2025): "Update Zenoh version" — merged to rolling, backported to jazzy+humble
- PR #424 (Jan 17 2025): "Bump zenoh-c and zenoh-cpp to 1.1.1" — merged to rolling, backported to jazzy (#431) and humble (#430)
- Subsequent PRs bumped to 1.2.1, 1.3.2, and eventually 1.7.1

### apt package hasn't been rebuilt
`ros-jazzy-zenoh-cpp-vendor 0.2.9-1noble.20260122.154203` — this was built on 2026-01-22, before the 1.7.1 bump landed. The Jazzy apt repo hasn't synced the latest source.

### zenoh_ros2_sdk supports both 0.x and 1.x
From `pyproject.toml`: `"eclipse-zenoh>=0.10.0"` — no upper bound. The SDK works with whatever zenoh version is installed. The `default_session_config.json5` uses the 1.x config format (has `open` field), which is why it fails with `eclipse-zenoh==0.11.0`.

## Solution: Build rmw_zenoh from source in Docker

Replace `apt install ros-jazzy-rmw-zenoh-cpp` with a colcon build from the jazzy branch. This gives zenoh 1.7.1, compatible with the native `eclipse-zenoh` 1.9.0.

### Modified Dockerfile approach

```dockerfile
FROM ros:jazzy-ros-base

# Install build tools + SLAM stack (everything EXCEPT rmw_zenoh_cpp from apt)
RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-jazzy-slam-toolbox \
    ros-jazzy-robot-localization \
    ros-jazzy-tf2-ros \
    python3-pip \
    python3-colcon-common-extensions \
    git \
    curl \
    && rm -rf /var/lib/apt/lists/*

# Install Rust (needed by zenoh_cpp_vendor to compile zenoh-c from source)
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# Build rmw_zenoh from source (jazzy branch = zenoh 1.7.1)
RUN mkdir -p /rmw_ws/src && cd /rmw_ws/src && \
    git clone --depth 1 -b jazzy https://github.com/ros2/rmw_zenoh.git && \
    cd /rmw_ws && \
    . /opt/ros/jazzy/setup.sh && \
    rosdep install --from-paths src --ignore-src -y && \
    colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release && \
    rm -rf /rmw_ws/src /rmw_ws/build /rmw_ws/log

# Build rf2o from source (not in Jazzy arm64 apt)
RUN mkdir -p /ros2_ws/src && cd /ros2_ws/src && \
    git clone --depth 1 https://github.com/MAPIRlab/rf2o_laser_odometry.git && \
    cd /ros2_ws && \
    . /opt/ros/jazzy/setup.sh && \
    colcon build --packages-select rf2o_laser_odometry --cmake-args -DCMAKE_BUILD_TYPE=Release && \
    rm -rf /ros2_ws/src /ros2_ws/build /ros2_ws/log

ENV RMW_IMPLEMENTATION=rmw_zenoh_cpp
```

### Entrypoint must source both overlays

```bash
source /opt/ros/jazzy/setup.bash
source /rmw_ws/install/setup.bash    # rmw_zenoh from source (zenoh 1.7.1)
source /ros2_ws/install/setup.bash   # rf2o from source
```

### zenohd router must also use zenoh 1.x

The zenohd container should use the rmw_zenohd from the source-built rmw_zenoh:
```yaml
zenohd:
  build: .  # Same image as ros2-slam (has source-built rmw_zenoh)
  command: >
    bash -c "source /opt/ros/jazzy/setup.bash &&
             source /rmw_ws/install/setup.bash &&
             ros2 run rmw_zenoh_cpp rmw_zenohd"
```

### Build time estimate

On Pi 5 (arm64):
- Rust install: ~1 min
- zenoh_cpp_vendor (compiles zenoh-c from Rust source): ~10-15 min
- rmw_zenoh_cpp: ~2-3 min
- rf2o: ~1-2 min
- Total: ~15-20 min first build, cached after that

### Alternative: Pre-build on a faster machine

Use Docker buildx with QEMU emulation on Titan (DGX Spark, much faster):
```bash
docker buildx build --platform linux/arm64 -t ros2-slam:latest --load .
docker save ros2-slam:latest | ssh pi "docker load"
```
This would take ~5 min on Titan vs ~20 min on Pi.

## What doesn't change

- `slam_bridge.py` — no changes needed, the explicit `type_hash` values are from Jazzy and won't change
- `zenoh_ros2_sdk` — works with both 0.x and 1.x, so 1.9.0 native + 1.7.1 Docker = compatible
- All tests — still pass, no code changes
- Docker-compose structure — same services, same volumes, just Dockerfile changes
- `start.sh`/`stop.sh` — no changes needed

## References

- [rmw_zenoh GitHub (jazzy branch)](https://github.com/ros2/rmw_zenoh/tree/jazzy) — zenoh_cpp_vendor/CMakeLists.txt pins zenoh 1.7.1
- [PR #424: Bump zenoh-c/cpp to 1.1.1](https://github.com/ros2/rmw_zenoh/pull/424) — backported to jazzy (#431)
- [PR #405: Update Zenoh version](https://github.com/ros2/rmw_zenoh/pull/405) — backported to jazzy
- [zenoh_ros2_sdk (ROBOTIS)](https://github.com/ROBOTIS-GIT/zenoh_ros2_sdk) — pyproject.toml: `eclipse-zenoh>=0.10.0`
- [ROS2 Jazzy Zenoh docs](https://docs.ros.org/en/jazzy/Installation/RMW-Implementations/Non-DDS-Implementations/Working-with-Zenoh.html) — build from source instructions
- [Arm Learning Path: Zenoh multi-node on Pi](https://learn.arm.com/learning-paths/cross-platform/zenoh-multinode-ros2/3_zenoh-multinode/) — Docker + Rust build on arm64
- [zenoh-plugin-ros2dds](https://github.com/eclipse-zenoh/zenoh-plugin-ros2dds) — alternative DDS bridge (not needed if building from source)
