Skip to content

Mesh And Zenoh

truST uses a Zenoh-backed mesh surface for explicit multi-runtime sharing.

Mesh Fits

  • you want publish/subscribe-style data sharing between runtimes
  • you need explicit control over what a runtime publishes or subscribes to
  • discovery alone is not enough because you need live data exchange

Core config surface

Mesh is configured in [runtime.mesh] in runtime.toml.

Typical keys:

  • enabled
  • role
  • listen
  • connect
  • tls
  • auth_token
  • zenohd_version
  • publish
  • [runtime.mesh.subscribe]

Example:

[runtime.mesh]
enabled = true
role = "peer"
listen = "127.0.0.1:5212"
connect = []
tls = false
auth_token = ""
zenohd_version = "1.7.2"
publish = ["Status.PLCState"]

[runtime.mesh.subscribe]
"LineB:Status.PLCState" = "Local.Status.RemoteState"

When auth_token is configured, peers authenticate with that shared mesh token. Peers with missing or different tokens cannot join the authenticated link. tls = true uses the certificate, key and CA configured under [runtime.tls] and negotiates TLS on mesh sockets; it does not fall back to plaintext. Certificates must cover the hostnames used to connect.

How to think about it

  • discovery finds peers
  • pairing establishes trust/access workflow
  • mesh moves selected runtime values between trusted peers

For same-host deterministic transport, use Realtime T0. Mesh is not the HardRT path.

Worked tutorial

This tutorial runs two runtimes and walks through:

  1. network discovery,
  2. optional pairing,
  3. mesh data sharing.

Why this tutorial exists

Single-PLC workflows are only the start. Real plants often split logic across multiple runtimes. You need a repeatable method to discover peers, trust peers, and exchange selected data safely.

What you will learn

  • how to configure two runtimes so they can be discovered
  • why service names and unique ports matter
  • how to enable mesh publish/subscribe with explicit mappings

Prerequisites

  • complete Tutorial 13 first
  • one machine is enough (we will run two runtimes with different ports)

Step 1: Create PLC-A and PLC-B project copies

Why: separate project folders simulate real independent PLC nodes.

rm -rf /tmp/trust-plc-a /tmp/trust-plc-b
cp -R /tmp/trust-tutorial-13 /tmp/trust-plc-a
cp -R /tmp/trust-tutorial-13 /tmp/trust-plc-b

Expected result: - two independent project trees

Step 2: Configure unique runtime endpoints

Why: two runtimes on one host need unique web and mesh ports. Each project gets its own control endpoint by default, so this step leaves the control endpoint to that default.

Set PLC-A runtime config:

cat > /tmp/trust-plc-a/runtime.toml <<'TOML'
[bundle]
version = 1

[resource]
name = "PlcA"
cycle_interval_ms = 100

[runtime.control]
mode = "production"
debug_enabled = false

[runtime.web]
enabled = true
listen = "127.0.0.1:18101"
auth = "local"

[runtime.discovery]
enabled = true
service_name = "LineA"
advertise = true
interfaces = []

[runtime.mesh]
enabled = true
listen = "127.0.0.1:5211"
auth_token = ""
publish = []
subscribe = {}

[runtime.log]
level = "info"

[runtime.retain]
mode = "none"
save_interval_ms = 1000

[runtime.watchdog]
enabled = false
timeout_ms = 5000
action = "halt"

[runtime.fault]
policy = "halt"
TOML

Set PLC-B runtime config:

cat > /tmp/trust-plc-b/runtime.toml <<'TOML'
[bundle]
version = 1

[resource]
name = "PlcB"
cycle_interval_ms = 100

[runtime.control]
mode = "production"
debug_enabled = false

[runtime.web]
enabled = true
listen = "127.0.0.1:18102"
auth = "local"

[runtime.discovery]
enabled = true
service_name = "LineB"
advertise = true
interfaces = []

[runtime.mesh]
enabled = true
listen = "127.0.0.1:5212"
auth_token = ""
publish = ["Status.PLCState"]
subscribe = {}

[runtime.log]
level = "info"

[runtime.retain]
mode = "none"
save_interval_ms = 1000

[runtime.watchdog]
enabled = false
timeout_ms = 5000
action = "halt"

[runtime.fault]
policy = "halt"
TOML

Expected result: - both runtimes are discoverable - PLC-B publishes one mesh point (Status.PLCState)

Step 3: Build both projects

Why: each node still needs its compiled program before it can run.

trust-runtime build --project /tmp/trust-plc-a --sources /tmp/trust-plc-a/src
trust-runtime build --project /tmp/trust-plc-b --sources /tmp/trust-plc-b/src

Expected result: - program.tpi exists in both folders

Step 4: Start both runtimes

Why: discovery and mesh only work when both nodes are online.

Terminal A:

trust-runtime run --project /tmp/trust-plc-a

Terminal B:

trust-runtime run --project /tmp/trust-plc-b

Expected result: - PLC-A Web UI on http://127.0.0.1:18101 - PLC-B Web UI on http://127.0.0.1:18102

Step 5: Verify discovery from Web UI

Why: discovery is the first proof that nodes can see each other on the network.

  1. Open PLC-A Web UI (http://127.0.0.1:18101).
  2. Go to Network -> Discovery.
  3. Confirm LineB appears.

Expected result: - PLC-B is listed with reachable link

If discovery fails: - verify both runtimes are running - verify runtime.discovery.enabled = true - use manual add with PLC-B URL (127.0.0.1:18102)

Why: pairing is a safer trust workflow than sharing static credentials manually.

  1. In PLC-B, open Network -> Pairing and generate a code.
  2. In PLC-A, claim PLC-B using that code.

Expected result: - PLC-A can access PLC-B without repeated token prompts

Step 7: Configure mesh subscription on PLC-A

Why: publish/subscribe must be explicit so only intended data flows between runtimes.

Edit /tmp/trust-plc-a/runtime.toml and replace subscribe = {} with:

[runtime.mesh.subscribe]
"LineB:Status.PLCState" = "Local.Status.RemoteState"

Restart PLC-A after edit.

Expected result: - PLC-A subscribes to PLC-B status

Step 8: Verify mesh connection

Why: operational confidence requires observing active mesh links, not just configuration files.

  1. Open PLC-A Web UI.
  2. Go to Network -> Mesh connections.
  3. Confirm connection to LineB is present.

Expected result: - mesh link appears as connected/healthy

Common mistakes

  • same web or mesh ports for both PLCs
  • service name mismatch (LineB in config vs actual runtime name)
  • editing runtime.toml without restarting runtime

Completion checklist

  • [ ] PLC-A and PLC-B both running
  • [ ] discovery shows peer runtime
  • [ ] optional pairing completed
  • [ ] mesh publish/subscription configured
  • [ ] mesh connection observed in UI

Published names may also select a nested program or equipment member, such as Main.Tank.Level, or an array element such as Main.Levels[2]. Only the named values are included; missing members and out-of-bounds elements are omitted. Identifiers match without case sensitivity. This does not by itself enable HMI remote writes.