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
/rosoutlog 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’sDynamicMessagefallback, 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
- The agent starts and loads configuration.
- The ROS 2 bridge subscribes to configured topics.
- Each ROS 2 message is converted to a transport-neutral
DynValue. - Client tools connect to the agent and explicitly subscribe to topics.
- 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.
| Area | Owns | Start here |
|---|---|---|
protocol | Wire schema, DynValue, request/response enums, topic frame types, and length-prefixed bincode framing. | talos-common/src/protocol/ |
session | Application-facing client API used by talos-cli and talos-tui. | talos-common/src/session/mod.rs |
transport | Endpoint setup, listeners, raw connections, UDS plumbing, QUIC plumbing, and certificates. | talos-common/src/transport/ |
config | Shared TOML configuration structs and parsing. | talos-common/src/config.rs |
urdf | Shared 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.rstalos-common/src/transport/uds.rstalos-common/src/session/quic.rstalos-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:
ListTopicsListNodesListPosesSubscribe { topics }Unsubscribe { topics }SetJointPosition { joint, position }ExecutePose { name }ListParameters { node }GetParameters { node, names }SetParameter { node, name, value }GetTopicStatsGetTopicEndpoints { topic }GetLoggerLevel { node, logger }SetLoggerLevel { node, logger, level }
Responses
The agent replies with Response values:
TopicListNodeListPoseListSubscribedUnsubscribedTopicDataParameters { node, parameters }ParameterSet { node, name, successful, reason }OkErrorTopicStats(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.
f32andf64.- Strings.
- Byte arrays.
- Arrays of other
DynValuevalues. - 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:
- Loads configuration.
- Starts configured transport listeners.
- Creates the ROS 2 bridge node.
- Subscribes to configured topics with supported message types.
- 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:
| Command | Output |
|---|---|
list-topics | one array of {name, type_name, publisher_count, subscriber_count} |
list-nodes | one array of {name, namespace, publishers, subscribers, services} |
list-params, get-param | one array of {name, type, value} |
echo | one {topic, stamp: {sec, nanosec}, data} object per line per message |
hz | one {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 missinglatency_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/Odometrygeometry_msgs/msg/Twistgeometry_msgs/msg/PoseStampedstd_msgs/msg/Stringsensor_msgs/msg/JointStatesensor_msgs/msg/LaserScansensor_msgs/msg/Imurcl_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:
| Profile | Reliability | Durability | History | Typical use |
|---|---|---|---|---|
default | Reliable | Volatile | KeepLast (rclrs default) | Control, odometry |
sensor_data | BestEffort | Volatile | KeepLast(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-commonfor protocol, config, transport, and shared data types.talos-agentfor ROS 2 integration.talos-clifor scriptable command-line workflows.talos-tuifor 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
StreamHeaderonce per topic stream. - Repeated
TopicFramevalues 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.