Quickstart
Get ROSQL running against your ROS2 telemetry data in under 5 minutes.
Install the CLI
- curl | sh
- Homebrew (macOS)
- cargo install
- Build from source
curl -fsSL https://rosql.org/install.sh | sh
Linux x86_64, Linux arm64, macOS Intel, and macOS Apple Silicon. Installs to ~/.local/bin/ with the Parquet backend included.
brew install robotopsinc/tap/rosql
Intel and Apple Silicon. Supports brew upgrade rosql and brew uninstall rosql.
# Parquet backend + CLI (recommended — no external database required)
cargo install rosql --features server,duckdb
# PostgreSQL + CLI
cargo install rosql --features server,postgres
# Both backends
cargo install rosql --features server,duckdb,postgres
All platforms including Windows. Requires Rust stable — rustup.rs.
git clone https://github.com/RobotOpsInc/rosql
cd rosql
# CLI with Parquet backend
cargo build --release --features server,duckdb --bin rosql
# CLI with PostgreSQL
cargo build --release --features server,postgres --bin rosql
Your first query — no setup required
Query our public demo dataset instantly. No data source, no credentials, no configuration:
rosql query "FROM traces WHERE status = 'ERROR' LIMIT 5" \
--backend parquet \
--url s3://robotops-production-rosql-demo/data
╭──────────────────┬────────────────┬──────────────────┬────────────────────────────────┬──────────────┬────────────┬─────────────╮
│ timestamp │ trace_id │ span_id │ span_name │ service_name │ duration │ status_code │
├──────────────────┼────────────────┼──────────────────┼────────────────────────────────┼──────────────┼────────────┼─────────────┤
│ 1776452455875402 │ trace-amr01-m1 │ span-a01-m1-root │ /navigate_to_pose │ robot-amr-01 │ 7000000000 │ OK │
│ 1776452455975402 │ trace-amr01-m1 │ span-a01-m1-bt │ /bt_navigator/navigate │ robot-amr-01 │ 6900000000 │ OK │
│ 1776452456075402 │ trace-amr01-m1 │ span-a01-m1-ctrl │ /controller_server/follow_path │ robot-amr-01 │ 6800000000 │ OK │
╰──────────────────┴────────────────┴──────────────────┴────────────────────────────────┴──────────────┴────────────┴─────────────╯
The demo dataset is a simulated AMR (robot-amr-01) running three navigation missions — one successful, one battery-critical abort, one timeout. Updated on every ROSQL release.
Use --format json for programmatic consumption, or --format csv for export.
Query your own data — local Parquet files
Point ROSQL at a directory of Parquet files from the demo-agent or any OTel exporter:
rosql query "FROM traces WHERE status = 'ERROR' SINCE 1 hour ago" \
--backend parquet \
--url ./telemetry/robotops_demo_agent/20260403-141530/
The --url directory must follow the demo-agent output layout:
<url>/
traces/ *.parquet → otel_traces
logs/ *.parquet → otel_logs
metrics/ *.parquet → otel_metrics
topic_messages/ *.parquet → topic_messages
mcap_metadata/ *.parquet → mcap_metadata
Query a private S3 bucket
rosql query "FROM traces WHERE status = 'ERROR' SINCE 1 hour ago" \
--backend parquet \
--url s3://my-bucket/robot-01/robotops_demo_agent/20260403-141530/
Set standard AWS environment variables for credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION). See Driver Support → for the full credential reference.
Your first query — PostgreSQL
If you have an OTel-compatible PostgreSQL database:
rosql query "FROM traces WHERE status = 'ERROR' SINCE 1 hour ago" \
--backend postgres \
--url postgresql://user:pass@localhost:5432/telemetry
Cross-signal correlation
DURING correlates events across data sources in a single query.
Find navigation failures that happened while the battery was critically low:
SELECT trace_id, span_name, service_name, duration, status_code, span_attributes
FROM traces
WHERE status = 'ERROR' AND action_name = '/navigate_to_pose'
DURING(
FROM topics WHERE topic_name = '/battery_state'
AND fields['percentage'] < 15
)
rosql query "SELECT trace_id, span_name, service_name, duration, status_code, span_attributes
FROM traces
WHERE status = 'ERROR' AND action_name = '/navigate_to_pose'
DURING(
FROM topics WHERE topic_name = '/battery_state'
AND fields['percentage'] < 15
)" \
--backend parquet \
--url s3://robotops-production-rosql-demo/data
Message causality
Trace the full causality chain from any span ID:
TRACE 'trace-001'
This walks parent_span_id → span_id recursively via a CTE and returns all spans in the causality chain in traversal order — something plain SQL has no primitive for.
[
{ "span_id": "span-001-root", "span_name": "/navigate_to_pose", "ros.node": "/bt_navigator", "duration": 8000000000 },
{ "span_id": "span-001-bt", "span_name": "/bt_navigator/navigate", "ros.node": "/bt_navigator", "duration": 7900000000 },
{ "span_id": "span-001-ctrl", "span_name": "/controller_server/follow_path","ros.node": "/controller_server", "duration": 7800000000 },
{ "span_id": "span-001-costmap", "span_name": "/local_costmap_node/update", "ros.node": "/local_costmap_node", "duration": 500000000 }
]
Compile to SQL
Inspect what ROSQL generates (no DB or --url needed):
rosql compile "FROM traces WHERE duration > 500 ms" --backend parquet
Output:
{
"backend": "parquet",
"ok": true,
"sql": "SELECT * FROM \"otel_traces\" WHERE \"duration\" > 500000000"
}
As a Rust library
cargo add rosql
use rosql::{parse, drivers::{sql::SqlBackend, ExecOptions}};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Parquet backend — local or S3
let backend = SqlBackend::from_parquet("s3://robotops-production-rosql-demo/data").await?;
// Or PostgreSQL
// let backend = SqlBackend::new("postgresql://user:pass@localhost/telemetry").await?;
let query = parse("
SELECT trace_id, span_name, service_name, duration, status_code
FROM traces WHERE status = 'ERROR' AND action_name = '/navigate_to_pose'
DURING(
FROM topics WHERE topic_name = '/battery_state' AND fields['percentage'] < 15
)
")?;
let result = backend.execute(&query, &ExecOptions::default()).await?;
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
Next steps
- Driver Support → — Parquet backend, S3 credentials, PostgreSQL, MySQL
- CLI reference → — all commands and flags
- Cookbook → — more example queries
- Schema reference → — required table structure