npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@robotops/rosql

v0.3.2

Published

The query language for ROS2 telemetry data

Readme


ROSQL (pronounced "RAW-skul") is Robot Ops Structured Query Language — a SQL-like language and driver purpose-built for ROS2 telemetry data stored via OpenTelemetry. It lets robotics engineers query traces, logs, and metrics using familiar SQL-like syntax with first-class support for ROS2 concepts — nodes, actions, topics, and message causality.

Robot observability is hard. ROS2 systems generate a firehose of traces, logs, and sensor data across dozens of nodes, but general-purpose query languages have no awareness of topics, action graphs, or message causality. ROSQL closes that gap: write queries in the language of your robot, not your database.

Built in Rust and available as a library, CLI, gRPC server, and WASM package, ROSQL is created and used by Robot Ops, Inc. to power the Robot Ops observability platform.

Docs, cookbook, and live demo → rosql.org

ROSQL is a trademark of Robot Ops, Inc.

Architecture

  ROS2 System
       │
       │  OTel attributes (ros.node, ros.action.*, ros.topic, ParentSpanId)
       ▼
  Robot Ops Agent (robotops.com) or OTel Collector (community)
       │
       │  OTLP gRPC
       ▼
  Datastore (e.g. PostgreSQL + TimescaleDB, ClickHouse, or any SQL-compatible DB)
       │
       │  OTel standard schema
       ▼
  rosql (parse + compile + execute)
       │
       ▼
  Query results

Usage modes

  ┌─────────────────────────────────────────────────────┐
  │ Mode 1: Parse + Execute (library)                   │
  │   ROSQL text → parser → AST → ROSQLBackend → DB    │
  ├─────────────────────────────────────────────────────┤
  │ Mode 2: CLI + gRPC Server                           │
  │   rosql parse / compile / query / serve             │
  ├─────────────────────────────────────────────────────┤
  │ Mode 3: WASM (frontend editor)                      │
  │   parse() / validate() / get_completions()          │
  └─────────────────────────────────────────────────────┘

Quick start

What ROSQL looks like

Express in one sentence what would take a page of SQL: find every navigation failure that happened while the battery was critically low.

SELECT trace_id, span_name_col, 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
)
SINCE 6 hours ago
{
  "columns": ["trace_id", "span_name_col", "service_name", "duration", "status_code", "span_attributes"],
  "rows": [
    ["a3f1c9d2e8b04f7a", "navigate_to_pose", "bt_navigator", 1423187000, "ERROR",
      {"ros.node": "/bt_navigator", "ros.action.name": "/navigate_to_pose", "ros.action.status": "aborted"}],
    ["c7d2f1a3b9e54c2b", "navigate_to_pose", "bt_navigator",  873456000, "ERROR",
      {"ros.node": "/bt_navigator", "ros.action.name": "/navigate_to_pose", "ros.action.status": "aborted"}]
  ],
  "metadata": { "rows_returned": 2, "elapsed_ms": 14 }
}

duration is stored as nanoseconds (BIGINT). 1423187000 ns ≈ 1.42 s.

Or trace the full message causality chain from a single span — something SQL has no primitive for. MESSAGE JOURNEY walks parent_span_id → span_id recursively and returns all columns from otel_traces:

MESSAGE JOURNEY FOR TRACE 'a3f1c9d2e8b04f7a'
{
  "columns": ["timestamp", "trace_id", "span_id", "parent_span_id", "span_name_col", "span_kind", "service_name", "duration", "status_code", "span_attributes", "resource_attributes"],
  "rows": [
    ["2025-03-25T14:32:11.012Z", "a3f1c9d2e8b04f7a", "f0e1d2c3b4a59687", "",                 "goal_received",     "INTERNAL", "bt_navigator",       0,         "OK",    {"ros.node": "/bt_navigator"},                                       {"host.name": "robot_03"}],
    ["2025-03-25T14:32:11.013Z", "a3f1c9d2e8b04f7a", "1a2b3c4d5e6f7089", "f0e1d2c3b4a59687", "compute_path",      "INTERNAL", "planner_server",    38000000,  "OK",    {"ros.node": "/planner_server",    "ros.topic": "/plan"},              {"host.name": "robot_03"}],
    ["2025-03-25T14:32:11.051Z", "a3f1c9d2e8b04f7a", "9876543210abcdef", "1a2b3c4d5e6f7089", "follow_path",       "INTERNAL", "controller_server", 412000000, "ERROR", {"ros.node": "/controller_server", "ros.topic": "/cmd_vel"},            {"host.name": "robot_03"}],
    ["2025-03-25T14:32:11.463Z", "a3f1c9d2e8b04f7a", "fedcba9876543210", "9876543210abcdef", "obstacle_detected", "INTERNAL", "local_costmap",       6000000, "OK",    {"ros.node": "/local_costmap",     "ros.topic": "/costmap_update"},    {"host.name": "robot_03"}]
  ],
  "metadata": { "rows_returned": 4, "elapsed_ms": 3 }
}

As a CLI

# Install
cargo install rosql --features server,postgres

# Execute a query against your database
rosql query "FROM traces WHERE status = 'ERROR' SINCE 1 hour ago" \
  --backend postgres --url postgresql://user:pass@localhost:5432/telemetry

# Compile to SQL (inspect what ROSQL generates — no DB needed)
rosql compile "FROM traces WHERE duration > 500 ms" --backend postgres

# Trace a message causality chain
rosql query "MESSAGE JOURNEY FOR TRACE 'a3f1c9d2e8b04f7a'" \
  --backend postgres --url postgresql://user:pass@localhost:5432/telemetry

As a library

cargo add rosql
use rosql::{parse, drivers::{SqlBackend, ExecOptions}};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let backend = SqlBackend::new("postgresql://user:pass@localhost/telemetry").await?;
    let query = parse("
        SELECT trace_id, span_name_col, 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
        )
        SINCE 6 hours ago
    ")?;
    let result = backend.execute(&query, &ExecOptions::default()).await?;
    println!("{}", serde_json::to_string_pretty(&result)?);
    Ok(())
}

Driver support

| Backend | Feature flag | Status | |---------|-------------|--------| | PostgreSQL / TimescaleDB | postgres | v0.1 | | MySQL / MariaDB | mysql | v0.1 | | DuckDB (embedded) | duckdb | v0.2 | | AWS Athena | athena | Future | | Google BigQuery | bigquery | Future |

Feature flags

| Feature | What it enables | Dependencies | |---------|----------------|--------------| | (default) | Parser, AST, unit system, SQL compiler, proto types | logos, serde, prost | | postgres | PostgreSQL / TimescaleDB driver | sqlx, tokio | | mysql | MySQL / MariaDB driver | sqlx, tokio | | duckdb | DuckDB embedded driver | duckdb (bundled), tokio | | server | rosql CLI binary + gRPC server | tonic, tokio, clap | | wasm | WASM exports for frontend editors | wasm-bindgen |

CLI

# Parse → JSON AST
rosql parse "FROM traces WHERE duration > 500 ms SINCE 1 hour ago"

# Compile → SQL (shows what SQL ROSQL generates)
rosql compile "FROM traces WHERE duration > 500 ms" --backend postgres

# Execute → query results as JSON
rosql query "FROM traces WHERE status = 'ERROR'" \
  --backend postgres --url postgresql://user:pass@localhost:5432/db

# Validate syntax
rosql validate "SELECT * FROM logs"

# Autocomplete suggestions at cursor position
rosql completions "FROM " 5

# Start gRPC server on Unix socket
rosql serve --socket /tmp/rosql.sock

# Schema profiles (match your OTel Collector exporter):
#   --schema otel-postgres    (lowercase columns, default)
#   --schema otel-clickhouse  (PascalCase columns)

Examples

-- Find slow navigation actions
SELECT span_name, duration
FROM traces
WHERE action_name = '/navigate_to_pose' AND duration > 500 ms
SINCE 1 hour ago
ORDER BY duration DESC
LIMIT 20

-- Cross-signal correlation: errors during low battery
FROM traces WHERE status = 'ERROR'
DURING(
  FROM topics WHERE topic_name = '/battery_state'
  AND fields['percentage'] < 20
)
SINCE yesterday

-- Message causality graph
MESSAGE JOURNEY FOR TRACE 'abc123def456'

-- Robot health assessment
HEALTH() FOR ROBOT 'robot_42' SINCE 30 minutes ago

-- Pipeline syntax
FROM traces
| WHERE duration > 500 ms
| FACET robot_id
| COMPARE TO last week

See examples/ for a full walkthrough with Docker Compose, PostgreSQL fixture data, and runnable queries.

WASM API

The @robotops/rosql npm package exposes parsing and validation for browser editors:

import init, { parse, validate, get_completions } from '@robotops/rosql';

await init();

const result = parse('FROM traces WHERE duration > 500 ms SINCE 1 hour ago');
console.log(result);

const errors = validate('INSERT INTO logs');
console.log(errors); // { valid: false, errors: [...] }

const completions = get_completions('FROM ', 5);
console.log(completions); // [{ label: 'logs', ... }, { label: 'traces', ... }, ...]

Schema

ROSQL expects telemetry data in the OpenTelemetry schema conventions for ROS2. This includes standard OTel tables (otel_traces, otel_logs, otel_metrics) with ROS2-specific span attributes (ros.node, ros.action.*, ros.topic).

Performance

See BENCHMARKS.md for performance data (coming soon — #35).

Pre-built binaries

| Platform | Architecture | Download | |----------|-------------|----------| | Linux | x86_64 | GitHub Releases | | Linux | aarch64 | GitHub Releases |

Other platforms: build from source.

Building from source

git clone https://github.com/RobotOpsInc/rosql
cd rosql

# Library only (default)
cargo build --release

# CLI binary
cargo build --release --features server --bin rosql

# CLI with PostgreSQL query execution
cargo build --release --features server,postgres --bin rosql

Local development

git clone https://github.com/RobotOpsInc/rosql
cd rosql
just build       # build default features
just test        # run tests
just build-wasm  # build WASM package
just check       # full CI: build + test + clippy + fmt + buf-lint

Prerequisites: Rust (stable, 1.80+), protoc, buf (optional). See CONTRIBUTING.md for details.

Contributing

ROSQL is in early development (v0.1) and contributions are welcome.

Robot Ops platform

For fleet-scale telemetry with managed ingestion, storage, and dashboards — including lifecycle anchors, fleet-wide anomaly detection, and ClickHouse performance — see the Robot Ops platform.

License

Apache 2.0 — see LICENSE.