Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

Talos is a terminal-native tool for observing and interacting with ROS 2 systems.

The main idea is to separate the developer workstation from the ROS 2 runtime. The robot runs talos-agent, which talks to ROS 2 through rclrs. Developer machines run talos-tui or talos-cli, which connect to the agent over Unix domain sockets for local use or QUIC for remote use.

flowchart LR
    subgraph Workstation["Developer machine"]
        TUI["talos-tui"]
        CLI["talos-cli"]
        NoROS["No ROS 2 runtime needed"]
    end

    subgraph Target["Target device"]
        Agent["talos-agent"]
        Rclrs["rclrs"]
        ROS["ROS 2 graph"]
    end

    TUI -->|"UDS or QUIC"| Agent
    CLI -->|"UDS or QUIC"| Agent
    Agent --> Rclrs
    Rclrs --> ROS

Only the agent depends on ROS 2. The shared protocol, CLI, and TUI build and run without a ROS 2 installation.

What Talos Provides

  • Topic observation with live message data rendered as generic trees.
  • ROS 2 node discovery and graph inspection.
  • A filterable /rosout log view.
  • URDF-aware joint state display and joint command publishing.
  • A small CLI for scripts and one-shot checks.

Current Scope

Talos currently focuses on observation, basic joint control, and terminal workflows. It does not try to replace RViz, provide full ROS 2 service/action proxying, or expose arbitrary dynamic message support beyond the compiled-in message conversions in the agent.

Documentation Status

This book is the canonical project documentation. Keep it aligned with current behavior and use the design history pages for context on important choices.

Getting Started

Talos is a Cargo workspace with four crates:

  • talos-common: protocol, config, transport, session, and URDF support.
  • talos-agent: ROS 2 bridge process that runs on the robot.
  • talos-cli: command-line client.
  • talos-tui: terminal UI client.

Build Without ROS 2

The client crates and shared library do not require a ROS 2 environment:

cargo check -p talos-common -p talos-cli -p talos-tui

Build Everything

The agent depends on rclrs. Install the Pixi environment — it provides the ROS 2 Lyrical runtime and the pre-generated Rust message bindings, which ros-env picks up from AMENT_PREFIX_PATH. Then build inside it:

pixi install                 # one time
pixi run check               # or: pixi shell, then cargo check --workspace

Note: The client crates build without ROS 2.

Known issue: topics without a compiled-in converter go through rclrs’s DynamicMessage fallback, which still panics on primitive or string sequence fields (e.g. sensor_msgs/PointCloud2) on Lyrical. Topics with a compiled-in converter are unaffected.

Enable QUIC

QUIC support is feature-gated:

cargo build --features quic

Without the quic feature, Talos builds UDS support only.

Run The Agent

With no --config argument, the agent looks for talos-agent.toml in the current directory. If the file is absent, it starts with default UDS transport and no configured topic subscriptions.

talos-agent --config talos-agent.toml

Run A Client

For local UDS access:

talos-cli list-topics
talos-cli echo /joint_states --count 5
talos-tui

For remote QUIC access, build with --features quic and pass --remote:

talos-cli --remote 192.168.1.50:4433 list-topics
talos-tui --remote 192.168.1.50:4433

Architecture Overview

Talos has a strict dependency boundary around ROS 2:

flowchart LR
    subgraph Clients["Workstation tools"]
        CLI["talos-cli"]
        TUI["talos-tui"]
    end

    Common["talos-common<br/>protocol, session, transport, config, URDF"]

    subgraph Runtime["Robot runtime"]
        Agent["talos-agent"]
        ROS["rclrs / ROS 2"]
    end

    CLI -->|"uses"| Common
    TUI -->|"uses"| Common
    Agent -->|"uses"| Common
    Agent -->|"links to"| ROS

talos-common is the shared base. It defines protocol schema and framing, the application-facing client session trait, transport endpoint setup, configuration data, and URDF parsing helpers. It has no ROS 2 dependency.

talos-agent is the only crate that links to ROS 2. It creates the ROS 2 node, subscribes to configured topics, converts ROS 2 messages into DynValue, and serves Talos clients.

talos-cli and talos-tui are workstation tools. They only need the Talos protocol and a reachable agent.

Data Flow

  1. The agent starts and loads configuration.
  2. The ROS 2 bridge subscribes to configured topics.
  3. Each ROS 2 message is converted to a transport-neutral DynValue.
  4. Client tools connect to the agent and explicitly subscribe to topics.
  5. The router forwards topic data only to clients subscribed to that topic.

Important Boundaries

The agent owns ROS 2 message typing and conversion. Clients receive generic Talos protocol data and do not need ROS 2 message definitions.

The session layer hides transport differences from the CLI and TUI through ProtocolClient. UDS uses a single framed connection. QUIC uses a bidirectional control stream plus server-initiated unidirectional data streams.

For a task-oriented guide to which source files own each change, see Source Map.

The terminal UI is a client of the protocol, not a special case inside the agent. It reconnects, lists topics, subscribes, and renders the latest data it has received.

Agent And Clients

Talos uses one robot-side agent and one or more developer-side clients.

Agent

talos-agent is responsible for:

  • Loading talos-agent.toml.
  • Starting configured UDS and QUIC listeners.
  • Creating and spinning the ROS 2 node.
  • Subscribing to configured ROS 2 topics.
  • Converting supported ROS 2 messages into DynValue.
  • Answering graph queries for topics and nodes.
  • Publishing joint commands when control is configured.
  • Tracking client subscriptions and routing topic data.

The agent runs the ROS 2 bridge and IPC servers as async tasks. ROS 2 callback paths send converted topic data through a channel into the router, avoiding blocking the callback on client I/O.

Clients

talos-cli and talos-tui both use the ProtocolClient trait from talos-common.

Clients send control requests such as ListTopics, ListNodes, Subscribe, Unsubscribe, SetJointPosition, and ExecutePose. Topic data is received separately through the session interface.

Subscription Model

Clients do not receive all topic data automatically. They must subscribe to the topics they want.

This matters for remote links and high-rate topics. A CLI command echoing one topic should not pay the cost of receiving every other topic. The TUI normally subscribes to all discovered topics because its purpose is broad observation.

Client Lifetime

Each client connection gets a router entry with its own subscription set. When the client disconnects, the agent deregisters it and drops the routing state for that session.

Source Map

This page maps common Talos changes to the first source files to inspect.

Shared Boundaries

talos-common is shared by the agent, CLI, and TUI. It does not depend on ROS 2.

AreaOwnsStart here
protocolWire schema, DynValue, request/response enums, topic frame types, and length-prefixed bincode framing.talos-common/src/protocol/
sessionApplication-facing client API used by talos-cli and talos-tui.talos-common/src/session/mod.rs
transportEndpoint setup, listeners, raw connections, UDS plumbing, QUIC plumbing, and certificates.talos-common/src/transport/
configShared TOML configuration structs and parsing.talos-common/src/config.rs
urdfShared URDF joint extraction helpers.talos-common/src/urdf.rs

The intended dependency direction is:

CLI/TUI application code -> session::ProtocolClient -> protocol + transport
agent server code        -> protocol + transport + router/bridge
ROS 2 conversions        -> talos-agent only

If a client feature needs to talk to the agent, add or use a protocol request through ProtocolClient. Do not make CLI or TUI code open transport endpoints directly unless the change is explicitly about transport plumbing.

Common Tasks

Add A Request Or Response

Start in talos-common/src/protocol/messages.rs. Add the schema variant there, then update protocol tests in talos-common/src/protocol/tests.rs if framing or serialization coverage should change.

After the shared schema exists, wire behavior through the agent request dispatch in talos-agent/src/server.rs, and call it from clients through the ProtocolClient::request API in talos-common/src/session/mod.rs.

Update CLI or TUI code only after the protocol and agent behavior are defined.

Add A ROS Message Conversion

Start in talos-agent/src/conversions.rs. ROS 2 message typing and conversion belong in the agent because talos-common, talos-cli, and talos-tui must remain free of ROS 2 dependencies.

Then update subscription setup in talos-agent/src/bridge.rs so the configured message type is subscribed and converted into DynValue.

If the new message type should be configurable, update docs/src/configuration/agent-config.md and the config examples that mention supported message types.

Change TUI Topic Behavior

Start in talos-tui/src/state.rs for subscription intent, topic catalog state, and reconnect behavior. Use talos-tui/src/client.rs for session command wiring, and talos-tui/src/ui/topics_tab.rs for rendering and input behavior specific to the Topics tab.

The TUI should continue to communicate through ProtocolClient; topic behavior changes should not bypass the session layer to talk to transports directly.

Change Transport Framing

Start in talos-common/src/protocol/codec.rs for the length-prefixed bincode frame format and frame limits.

Then inspect each transport path that uses the codec:

  • talos-common/src/session/uds.rs
  • talos-common/src/transport/uds.rs
  • talos-common/src/session/quic.rs
  • talos-common/src/transport/quic.rs

Keep schema changes in protocol/messages.rs or protocol/types.rs; keep endpoint/listener changes in transport.

Change Config

Start in talos-common/src/config.rs. Add or change the shared config model there, then update talos-common/src/config_tests.rs for TOML parsing behavior.

Agent behavior that consumes config usually belongs in talos-agent/src/main.rs or talos-agent/src/bridge.rs. User-facing config documentation belongs in docs/src/configuration/agent-config.md.

When In Doubt

Use the narrowest layer that owns the concept:

  • Schema or frame encoding belongs in protocol.
  • Client application calls belong in session.
  • Socket, stream, listener, certificate, or endpoint setup belongs in transport.
  • ROS 2 type knowledge belongs in talos-agent.
  • TUI presentation and interaction state belongs in talos-tui.

Protocol

The Talos protocol is defined in talos-common.

The protocol module owns schema and framing types: request/response enums, topic data types, DynValue, stream headers, and the length-prefixed bincode codec. The application-facing client API lives in session, while socket and stream setup lives in transport.

Control messages use a four-byte big-endian length prefix followed by a bincode-encoded payload. The codec is implemented around Tokio async I/O and is used by both UDS and QUIC control paths.

Requests

Clients send Request values:

  • ListTopics
  • ListNodes
  • ListPoses
  • Subscribe { topics }
  • Unsubscribe { topics }
  • SetJointPosition { joint, position }
  • ExecutePose { name }
  • ListParameters { node }
  • GetParameters { node, names }
  • SetParameter { node, name, value }
  • GetTopicStats
  • GetTopicEndpoints { topic }
  • GetLoggerLevel { node, logger }
  • SetLoggerLevel { node, logger, level }

Responses

The agent replies with Response values:

  • TopicList
  • NodeList
  • PoseList
  • Subscribed
  • Unsubscribed
  • TopicData
  • Parameters { node, parameters }
  • ParameterSet { node, name, successful, reason }
  • Ok
  • Error
  • TopicStats (list of { topic, rate_hz, bandwidth_bps, latency_ms })
  • TopicEndpoints { topic, publishers, subscribers }. Each endpoint has its node, type and QoS (reliability, durability, history, deadline).
  • LoggerLevel { node, logger, level }
  • LoggerLevelSet { node, logger, successful, reason }

UDS carries control responses and topic data on the same framed connection. Because that connection carries data for multiple topics, UDS topic frames keep the topic name and type in each TopicData response.

QUIC uses a bidirectional stream for control and server-initiated unidirectional streams for topic data. A topic stream starts with a StreamHeader containing the topic and type name, then carries TopicFrame values with timestamp and data.

DynValue

DynValue is the generic representation clients receive for ROS 2 message payloads. It can represent:

  • Booleans.
  • Signed and unsigned integer primitives.
  • f32 and f64.
  • Strings.
  • Byte arrays.
  • Arrays of other DynValue values.
  • Structs with an ordered field list.

The agent handles typed ROS 2 subscriptions and conversions. Clients render or print the resulting DynValue tree without knowing the original ROS 2 message type at compile time.

Frame Limits

Oversized frames are rejected by the codec. The default maximum frame size is intended to protect the agent and clients from unbounded buffering.

Transports

Talos supports two transports: Unix domain sockets and QUIC.

The transport module owns endpoint, listener, connection, and certificate plumbing. It does not own the request/response schema, and it is not the normal API used by CLI or TUI application code. Client applications use the ProtocolClient session API, which hides whether the underlying connection is UDS or QUIC.

Unix Domain Sockets

UDS is the default local transport. It is appropriate when the client and agent run on the same machine or inside an environment where the Unix socket path is shared.

The default socket path is:

/tmp/talos.sock

When the agent starts, it creates the socket listener. If a stale socket file is left behind from an earlier run, the UDS transport removes it before binding.

QUIC

QUIC is for remote observation and control across a network. It is built with quinn and is enabled with the Cargo quic feature.

cargo build --features quic

The default QUIC bind address is:

0.0.0.0:4433

QUIC uses one bidirectional control stream and server-initiated unidirectional streams for topic data. This gives each topic independent stream flow control and avoids mixing all topic data into one stream.

Security Posture

Current QUIC support is designed for trusted local networks. If no certificate paths are configured, the agent generates a self-signed certificate at startup. Clients use an insecure verifier so they can connect to that certificate.

Do not treat the current QUIC mode as suitable for untrusted networks. Proper certificate verification and authentication are future work.

Choosing A Transport

Use UDS for local development and single-machine workflows. Use QUIC when the agent runs on a robot and the client runs on a separate developer machine.

Agent

talos-agent runs on the machine that has ROS 2 access.

talos-agent --config talos-agent.toml

The --config argument is optional. Without it, the agent checks for talos-agent.toml in the current directory. If the file is not present, the agent uses defaults: UDS enabled at /tmp/talos.sock, QUIC disabled, no topic subscriptions, and no joint control.

Startup Behavior

On startup, the agent:

  1. Loads configuration.
  2. Starts configured transport listeners.
  3. Creates the ROS 2 bridge node.
  4. Subscribes to configured topics with supported message types.
  5. Waits for clients to connect and subscribe.

If neither UDS nor QUIC is configured, the agent logs an error and exits without serving clients.

ROS 2 Environment

The agent requires the ROS 2 Lyrical environment, managed by Pixi. It provides the ROS 2 runtime and the pre-generated Rust message bindings. Enter the environment before building or running:

pixi install
pixi shell        # or prefix commands with `pixi run`

Logging

The agent uses tracing. Set RUST_LOG to adjust log verbosity:

RUST_LOG=talos_agent=debug talos-agent --config talos-agent.toml

CLI

The CLI binary is named talos-cli.

List Topics

talos-cli list-topics

This asks the agent for the current topic list and prints topic name, type, publisher count, and subscriber count.

List Nodes

talos-cli list-nodes

This asks the agent for discovered ROS 2 nodes and prints their names and namespaces.

Echo Topic Data

talos-cli echo /joint_states --count 5

echo subscribes to the requested topic before waiting for data. A count of zero means unlimited output.

Topic Rate

talos-cli hz /scan --duration 10

hz polls the agent’s topic stats once a second and prints the rate, bandwidth and header-stamp latency (- for unstamped types) of one topic:

rate: 10.00 Hz  bandwidth: 11.3 KB/s  latency: 4.2 ms

The numbers are measured by the agent (see Topic Observation), so the topic must be in the agent’s [[subscriptions]]. Without --duration it runs until interrupted.

Parameters

View and set ROS 2 parameters on any node in the graph. Pass the fully-qualified node name (e.g. /talos_agent).

List a node’s parameters with their current values:

talos-cli list-params /talos_agent

Get specific parameter values:

talos-cli get-param /talos_agent use_sim_time rate

Set a parameter (the value type is inferred — true/false for bool, integers, floats, [1, 2, 3] for arrays, otherwise a string):

talos-cli set-param /talos_agent rate 50.0

set-param exits non-zero if the node rejects the change and prints the reason. The agent reaches each node through the standard rcl_interfaces parameter services, so the target node must be running.

JSON Output

The global --json flag switches to machine-readable output for piping into tools such as jq:

CommandOutput
list-topicsone array of {name, type_name, publisher_count, subscriber_count}
list-nodesone array of {name, namespace, publishers, subscribers, services}
list-params, get-paramone array of {name, type, value}
echoone {topic, stamp: {sec, nanosec}, data} object per line per message
hzone {topic, rate_hz, bandwidth_bps, latency_ms} object per line per second

set-param ignores --json; errors still go to stderr with a non-zero exit.

talos-cli --json echo /joint_states --count 1 | jq '.data.position'

Message data and parameter values are converted to plain JSON rather than Talos’ internal tagged form:

  • messages become objects with fields in message order (the type name is dropped), and arrays and sequences become arrays;
  • uint8[]/byte[] fields and byte-array parameters become base64 strings;
  • NaN and infinite floats become null, as JSON has no representation for them, and so do unset parameters and a missing latency_ms.

Logger Levels

Show a node’s logger level, or set it by passing a level (unset, debug, info, warn, error or fatal):

talos-cli log-level /talos_agent
talos-cli log-level /talos_agent debug

--logger <name> targets another logger, such as rclcpp, instead of the node’s own. See Logger Levels for what the node must enable.

Socket Selection

The default transport is UDS:

talos-cli --socket /tmp/talos.sock list-topics

With the quic feature enabled, --remote selects QUIC:

talos-cli --remote 192.168.1.50:4433 list-topics

--socket and --remote are mutually exclusive. If the binary was compiled without QUIC support, --remote returns an error.

TUI

talos-tui is the interactive terminal client.

talos-tui

By default it connects over UDS at /tmp/talos.sock. With the quic feature:

talos-tui --remote 192.168.1.50:4433

Views

The TUI has five tabs:

  • Topics
  • Nodes
  • Log
  • Joints
  • Params

Use number keys 1 through 5 to switch tabs. Tab switches focus between panes. r refreshes the topic and node lists immediately. q quits.

Filtering

Press / to filter the list on the current tab: topic names on Topics, node names (including namespace) on Nodes, parameter names on Params, and message text on Log. Matching is a case-insensitive substring and the list updates as you type. While the prompt is open every key goes to it: ←/→ move the cursor, Backspace deletes, Ctrl-U clears, Enter applies and Esc restores the previous filter. Applying an empty prompt clears the filter.

The active filter is shown in the pane title (on the Log tab, in the filter bar), and the selection moves to stay inside the filtered list.

Connection Behavior

The TUI reconnects when the agent connection is lost. After connecting, it asks for the topic list and subscribes to all discovered topics by default so it can receive live data. While connected, it re-fetches the topic and node lists every 2 seconds, so nodes and topics that appear or go away show up without a reconnect; the selection stays on the same item by name. If you manually toggle topic subscriptions, those choices are kept in the client state and re-applied after reconnect instead of subscribing to every topic again. Topics that disappear from the latest agent topic list are removed from the Topics pane and reconnect request until the agent advertises them again, which also drops their cached sample/count history.

Topics

The Topics tab shows topic names and current message rates. Selecting a topic shows the latest DynValue tree for that topic. Press s anywhere in the Topics tab to subscribe or unsubscribe the selected topic. The list shows the current subscription state, including pending changes and request errors.

Before you customize anything, newly discovered topics inherit the default behavior and are subscribed automatically. After you manually subscribe or unsubscribe any topic, the TUI switches to using your explicit desired set: newly discovered topics stay unsubscribed until you opt into them with s.

If a manual subscribe or unsubscribe request fails, the TUI keeps your desired choice in client state, shows the error on that topic, and retries the desired state after reconnect.

Nodes

The Nodes tab lists ROS 2 nodes and shows publishers, subscribers, and services for the selected node.

Press l to load the selected node’s logger level into the detail pane and L to set it to the next level (DEBUG, INFO, WARN, ERROR, FATAL, then back to DEBUG). See Logger Levels.

Logs

The Log tab displays /rosout entries with timestamp, severity, node, and message fields. It supports severity, node, and text filtering.

Joints

The Joints tab combines URDF joint definitions with live /joint_states data. It can display limits, current position, velocity, effort, and configured poses. When control is configured, it can send joint position and pose commands to the agent.

Params

The Params tab lists the nodes discovered in the graph in the left pane. Select a node and press Enter to load its parameters into the right pane, where each parameter is shown with its current value and type.

Switch focus to the parameter list with Tab, select a parameter, and press e to edit it. Type the new value and press Enter to apply it (the value type is inferred the same way as the CLI set-param command); Esc cancels. After a successful set the list refreshes so the new value is shown.

Agent Config

The agent reads TOML configuration from talos-agent.toml or the path passed to --config.

Minimal UDS Config

[transport.uds]
socket_path = "/tmp/talos.sock"

QUIC Config

QUIC requires building with the quic feature:

[transport.quic]
bind_addr = "0.0.0.0:4433"
# cert_path = "/path/to/cert.der"
# key_path = "/path/to/key.der"

If cert_path and key_path are omitted, the agent generates a self-signed certificate at startup.

Dual Transport Config

UDS and QUIC can be enabled together:

[transport.uds]
socket_path = "/tmp/talos.sock"

[transport.quic]
bind_addr = "0.0.0.0:4433"

At least one transport must be configured for the agent to serve clients.

Topic Subscriptions

The agent subscribes to configured topics with compiled-in ROS 2 message types. An optional qos field selects the QoS profile; when omitted it defaults to "default" (Reliable, Volatile, KeepLast — depth inherited from rclrs default).

[[subscriptions]]
topic = "/odom"
type = "nav_msgs/msg/Odometry"

[[subscriptions]]
topic = "/cmd_vel"
type = "geometry_msgs/msg/Twist"

[[subscriptions]]
topic = "/robot_description"
type = "std_msgs/msg/String"

[[subscriptions]]
topic = "/joint_states"
type = "sensor_msgs/msg/JointState"

[[subscriptions]]
topic = "/rosout"
type = "rcl_interfaces/msg/Log"

# Sensor topics: use sensor_data QoS (BestEffort, Volatile, KeepLast 5)
[[subscriptions]]
topic = "/scan"
type = "sensor_msgs/msg/LaserScan"
qos = "sensor_data"

[[subscriptions]]
topic = "/imu/data"
type = "sensor_msgs/msg/Imu"
qos = "sensor_data"

[[subscriptions]]
topic = "/pose"
type = "geometry_msgs/msg/PoseStamped"

Unknown message types are skipped by the agent.

Joint Control

Joint control is optional:

[control]
method = "topic"
topic = "/joint_commands"
type = "sensor_msgs/msg/JointState"

Named poses are stored under poses:

[poses.home]
shoulder_pan = 0.0
shoulder_lift = -1.57
elbow = 1.57

If control is not configured, joint command requests return an error.

Topic Observation

Talos observes ROS 2 topics through configured agent subscriptions.

The agent subscribes with concrete ROS 2 message types and converts received messages into DynValue. Clients receive the generic tree and can render it without ROS 2 type definitions.

Current Message Support

The current agent supports these message families:

  • nav_msgs/msg/Odometry
  • geometry_msgs/msg/Twist
  • geometry_msgs/msg/PoseStamped
  • std_msgs/msg/String
  • sensor_msgs/msg/JointState
  • sensor_msgs/msg/LaserScan
  • sensor_msgs/msg/Imu
  • rcl_interfaces/msg/Log

Support for arbitrary ROS 2 message definitions is future work.

QoS Profiles

Each subscription can specify a qos field in the config. Two profiles are available:

ProfileReliabilityDurabilityHistoryTypical use
defaultReliableVolatileKeepLast (rclrs default)Control, odometry
sensor_dataBestEffortVolatileKeepLast(5)Laser, IMU, camera

When qos is omitted the default profile is used, preserving existing behavior.

High-rate sensor topics (sensor_msgs/msg/LaserScan, sensor_msgs/msg/Imu) should generally use sensor_data QoS. A reliable publisher (e.g. a nav stack node) is compatible with a best-effort subscriber, so sensor_data is safe to use even when the publisher uses reliable QoS.

Endpoint QoS

When a topic delivers no data, the cause is usually a QoS mismatch. The topic detail pane lists every publisher and subscription on the selected topic (including the agent’s own) with its QoS, from the agent’s GetTopicEndpoints query. A subscriber that can’t match a publisher gets a red warning. These pairs are flagged, following the ROS 2 compatibility rules:

  • a best-effort publisher with a reliable subscriber;
  • a volatile publisher with a transient-local subscriber;
  • a publisher deadline longer than the subscriber’s.

A frequent case is a sensor publishing best-effort while the agent’s subscription uses the default (reliable) profile. Setting qos = "sensor_data" on that subscription fixes it.

Rates

The agent measures every bridged topic, whether or not any client is subscribed. Once a second it closes a window of message and byte counts and smooths the result with an exponential moving average. GetTopicStats returns:

  • Rate: messages per second.
  • Bandwidth: bytes per second, estimated from the decoded message size (roughly the CDR payload without length prefixes or padding).
  • Latency: agent receive time minus header.stamp, for stamped types. This uses the agent’s wall clock, so it has no meaning under simulated time.

These numbers come from the agent rather than from frames a client receives. A client that falls behind has frames dropped by the agent, but the stats stay accurate. The TUI polls the stats every second and shows them in the topic list and detail pane, with a 60-second rate sparkline. talos-cli hz <topic> prints them from the command line.

The TUI keeps the latest value for each topic and renders on a fixed tick loop. High-frequency topics are naturally deduplicated by display rate: the UI shows the most recent received value at render time rather than drawing every incoming message.

CLI Echo

The CLI echo command subscribes to a single topic and prints received DynValue trees until interrupted or until --count messages have been printed.

Node Introspection

Talos can list ROS 2 nodes through the agent.

The agent uses ROS 2 graph APIs to discover node names, namespaces, publishers, subscribers, and services. Clients request this data with ListNodes.

CLI

talos-cli list-nodes

TUI

The Nodes tab shows discovered nodes on the left. Selecting a node displays its namespace, publishers, subscribers, and services.

Logger Levels

Talos can show and set a node’s logger level through the node’s <node>/get_logger_levels and <node>/set_logger_levels services (rcl_interfaces/srv/GetLoggerLevels and SetLoggerLevels). Nodes only serve these when they opt in:

  • rclcpp: rclcpp::NodeOptions().enable_logger_service(true)
  • rclpy: Node(..., enable_logger_service=True)

Otherwise the agent reports that the logger services are not available.

Requests default to the node’s own logger, whose name is the fully-qualified node name without the leading / and with / replaced by . (/ns/foo is ns.foo). Levels are UNSET (0, inherit the default), DEBUG (10), INFO (20), WARN (30), ERROR (40) and FATAL (50).

Use talos-cli log-level <node> [level], or l / L on the TUI Nodes tab.

Limits

Node introspection reflects the ROS 2 graph state visible to the agent. Network or ROS domain configuration issues outside Talos can affect what the agent sees.

Logs

The Log view is based on /rosout.

To use it, configure the agent to subscribe to:

[[subscriptions]]
topic = "/rosout"
type = "rcl_interfaces/msg/Log"

The agent converts log messages into DynValue, including fields such as severity, node name, timestamp, and message text.

TUI Filtering

The TUI Log tab displays messages in a table and supports:

  • Severity filtering.
  • Node filtering.
  • Text search on the message (/, case-insensitive substring).

The UI keeps log interaction local to the client. The agent still routes only the /rosout topic data to clients that subscribed to it.

Joint Control

Talos can display joint state and send joint commands when the agent is configured for control.

Joint Data

The Joints tab combines two sources:

  • /robot_description, containing URDF XML.
  • /joint_states, containing live joint positions, velocities, and efforts.

The URDF parser extracts non-fixed joints, parent and child links, joint type, and limits. Live joint state data is merged onto those definitions.

Display

The TUI shows each joint with current state and limit context. For joints with limits, the detail pane displays the current position relative to the allowed range.

If the URDF cannot be parsed, the Joints tab reports the error instead of silently hiding the problem.

Commands

When [control] is configured, clients can send:

  • SetJointPosition { joint, position }
  • ExecutePose { name }

The agent publishes command messages to the configured control topic. If control is not configured, the agent returns an error for command requests.

Pose Presets

Pose presets are configured in TOML under [poses.<name>]. The TUI displays available poses and can request execution by name.

Parameters

Talos can view and set ROS 2 parameters on any node in the graph. Parameters belong to individual nodes, so all parameter operations are addressed to a fully-qualified node name (for example /talos_agent).

How It Works

The agent acts as a parameter service client. For a requested node it creates short-lived clients to that node’s standard rcl_interfaces parameter services:

  • <node>/list_parameters
  • <node>/get_parameters
  • <node>/set_parameters

These clients are serviced by the bridge node’s spinning executor. The agent waits briefly for the target services to become available, forwards the call, and converts the result into transport-agnostic protocol types so clients need no ROS 2 message definitions.

If the bridge node is not yet running, or the target node does not expose parameter services within the wait window, the agent returns an error rather than blocking indefinitely.

Values

Parameter values are carried as ParamValue, mirroring the variants of rcl_interfaces/msg/ParameterValue: Bool, Integer, Double, String, byte/bool/integer/double/string arrays, and NotSet for parameters that exist but have no value.

When setting a value from text (CLI argument or TUI edit field), the type is inferred:

  • true / false → bool
  • an integer literal → integer
  • a number with a decimal point → double
  • [ ... ] → an array (element type inferred from the contents)
  • anything else → string (surrounding quotes are stripped)

Clients

The CLI exposes list-params, get-param, and set-param. See CLI.

The TUI Params tab lists graph nodes, loads a selected node’s parameters, and can edit a parameter value in place. See TUI.

Setting a parameter reports the node’s per-parameter result; a rejected change includes the reason supplied by the node.

Design History

This section records high-level design decisions that explain why Talos is structured the way it is.

These pages are reference material, not a second source of truth. Current behavior belongs in the architecture, usage, configuration, and feature pages.

The records here intentionally avoid detailed implementation task lists. They capture context, decision, and consequences.

Initial System Design

Status: Accepted

Date: 2026-04-02

Context

Talos needed to let a developer inspect and interact with a ROS 2 system from a terminal without installing ROS 2 on every client machine.

The core constraint was keeping ROS 2 isolated to the robot-side process while still giving clients enough typed structure to inspect messages and send basic commands.

Decision

The system was split into four crates:

  • talos-common for protocol, config, transport, and shared data types.
  • talos-agent for ROS 2 integration.
  • talos-cli for scriptable command-line workflows.
  • talos-tui for interactive terminal observation and control.

Messages are converted in the agent into DynValue, a generic tree that preserves enough structure for clients to render and inspect data without ROS 2 message definitions.

The initial transport was UDS with length-prefixed bincode frames. Configuration uses TOML.

Consequences

Only the agent depends on ROS 2. The CLI, TUI, and common library can be built and tested separately from the ROS 2 runtime.

The agent must contain explicit conversion support for each ROS 2 message type Talos wants to bridge. That keeps clients simple but limits dynamic coverage until generic message support is added.

The terminal UI can focus on rendering current protocol data instead of talking directly to ROS 2.

QUIC Transport

Status: Accepted

Date: 2026-04-10

Context

UDS works well for local workflows but does not handle the common robotics setup where the robot and developer workstation are different machines.

Talos needed a remote transport that could support observation and control across a local network while preserving UDS for low-overhead local use.

Decision

QUIC was added as a feature-gated transport using quinn.

The protocol gained an explicit subscription model. Clients subscribe to topics before receiving topic data, and the agent routes each topic only to subscribed clients.

The session layer moved above raw transport details. Application code talks to ProtocolClient; UDS and QUIC implement the same client behavior with different stream layouts.

QUIC uses:

  • A client-opened bidirectional stream for control requests and responses.
  • Server-opened unidirectional streams for subscribed topic data.
  • A StreamHeader once per topic stream.
  • Repeated TopicFrame values after the header.

Consequences

Remote clients can use --remote <addr:port> when built with the quic feature. UDS remains the default path for local clients.

Per-topic streams give QUIC independent flow control for different topics and avoid repeating topic metadata on every data frame.

The initial QUIC security model is intentionally local-network only: the agent can generate a self-signed certificate and clients skip certificate verification. Authentication and verified TLS remain future work.

The agent configuration changed to separate [transport.uds] and [transport.quic] sections.

Roadmap

This page tracks likely future work. It is intentionally higher level than an issue tracker.

Near Term

  • Keep this mdBook as the canonical project documentation.
  • Improve examples for agent configuration and common ROS 2 setups.
  • Add more integration coverage for UDS and QUIC client behavior.

Protocol And Transport

  • Add authenticated QUIC connections for untrusted or shared networks.
  • Document and tune QUIC stream limits for large topic sets.
  • Improve reconnect behavior and error reporting for remote clients.

ROS 2 Coverage

  • Add support for more common ROS 2 message types.
  • Explore generic message conversion instead of compiled-in conversions only.
  • Consider service and action proxying once topic observation and control are stable.

User Experience

  • Improve TUI ergonomics for filtering, selection, and high-rate streams.
  • Add examples for scripting with the CLI.
  • Make joint control behavior clearer and safer around limits and command publishing.

Documentation

  • Link important Rust API items from this book where they clarify concepts.
  • Optionally publish Rustdoc separately from the mdBook.
  • Keep design history concise and focused on accepted decisions.

Open Questions

Security

What is the right authentication model for remote access? Current QUIC support is suitable for trusted local networks only.

Dynamic Message Support

Should Talos continue adding compiled-in conversions for common message types, or should it support generic ROS 2 message introspection?

Compiled-in conversions are simple and predictable. Generic conversion would cover more systems but adds complexity around type discovery, field traversal, and compatibility.

Services And Actions

Topic observation is the first priority. Service and action proxying could make Talos more capable, but they would expand the protocol and UI model significantly.

Documentation Versioning

The current plan is main/latest documentation only. If Talos starts shipping stable releases with incompatible behavior, versioned docs may become useful.

Rustdoc Publishing

Rustdoc should remain separate from this book. The open question is whether CI should also publish Rustdoc under a stable path, such as /api/, next to the mdBook output.

Development

Branching And Releases

main is the stable release branch. Use dev for ongoing integration work. Create feature branches from dev and open pull requests back into dev.

When dev is ready to release, open a pull request from dev to main. Merging that pull request runs the version bump workflow, promotes CHANGELOG.md entries from [Unreleased], and creates the GitHub release. Add version:minor or version:major to the dev -> main pull request when the release should be larger than a patch bump.

The bump moves the version in Cargo.toml, Cargo.lock and pixi.toml together. Merge the release pull request with a merge commit rather than a squash, so dev and main do not diverge. The repository deletes merged head branches automatically; a Keep dev repository ruleset blocks deleting dev, so it survives the release merge.

Workspace Checks

Without ROS 2:

cargo check -p talos-common -p talos-cli -p talos-tui

With the ROS 2 Lyrical environment (provided by Pixi):

pixi install
pixi run check          # or: pixi shell, then cargo check --workspace

With QUIC:

cargo check --workspace --features quic

Tests

cargo test --workspace
cargo test -p talos-common
cargo test -p talos-agent --test integration
cargo test -p talos-agent --test integration --features quic
cargo test -p talos-agent --test bridge_live

bridge_live is the only test that exercises a real ROS 2 topic: it publishes on one and asserts the bridge forwards the message to the router. The integration tests drive the IPC protocol with synthetic responses, so they stay green even if the bridge delivers nothing. Run it inside the Pixi environment.

Lints

CI enforces both of these, so run them before pushing:

cargo fmt --all --check
cargo clippy --workspace --all-targets --features quic -- -D warnings

The talos-agent job builds and tests inside the Pixi ROS 2 environment on both x86-64 and aarch64 Linux runners.

Coverage

The Coverage workflow reports LCOV output for the non-ROS crates (talos-common, talos-cli, and talos-tui) with the quic feature enabled. Pull requests compare that coverage against the base branch and fail if line coverage decreases.

Install cargo-llvm-cov before running the local coverage command:

cargo install cargo-llvm-cov --locked

cargo llvm-cov -p talos-common -p talos-cli -p talos-tui \
  --features quic \
  --lcov \
  --output-path coverage/lcov.info

python3 .github/scripts/coverage_report.py summarize coverage/lcov.info

On machines without the Pixi environment, temporarily remove talos-agent from the workspace members before running non-ROS coverage; the CI workflow does this because these packages do not need ROS 2.

Rustdoc

Rustdoc is API reference and stays separate from this book:

cargo doc --workspace --no-deps

Use this mdBook for concepts, workflows, architecture, and contributor guidance. Use Rustdoc for item-level API details.

Changelog

User-facing code, behavior, documentation, CI, or configuration changes should be recorded under [Unreleased] in CHANGELOG.md.

Documentation

The docs/ mdBook is the canonical documentation source.

Local Preview

mdbook serve docs

By default, mdBook serves at http://localhost:3000 and rebuilds when files change.

Build

mdbook build docs

The generated site is written to docs/book/.

Style

Write current behavior as prose. Avoid preserving formal requirement language unless it makes the behavior clearer.

Use design history pages for context on accepted decisions. Do not duplicate current behavior there.

Keep future plans in the future/ section so readers can distinguish planned work from implemented behavior.

When documenting Rust APIs, link or name the relevant type, trait, or module, but keep full API reference in Rustdoc.