Skip to main content

ros2inspector

Static ROS 2 architecture analysis, visualization, and policy checks.

ros2inspector scans supported Python, C++, interface, and launch-file patterns in a ROS 2 workspace. It builds an evidence-backed architecture graph, renders that graph in several formats, and enforces architecture policies in CI.

The current alpha ships eleven commands: scan, packages, nodes, graph, viz, audit, validate, docs, watch, doctor, and design.

Website and documentation: ros2-inspector-theta.vercel.app

Features

  • Static node and endpoint discovery with Python AST and Tree-sitter C++ parsers
  • Separate source-node definitions and launch deployment instances in the architecture graph
  • Python console_scripts/entry-point mapping for evidence-based launch executable matching
  • .msg, .srv, and .action interface parsing and type resolution
  • Python, XML, and YAML launch-file analysis with per-node remapping
  • NetworkX Unified Architecture Model for packages, nodes, topics, services, actions, and interfaces
  • Content-fingerprinted incremental analysis cache backed by diskcache
  • Rich table, JSON, YAML, Mermaid, DOT, and interactive HTML output
  • Built-in connectivity audit and YAML policy enforcement

Analysis scope

ROS 2 Inspector analyzes source files without executing or building the workspace. Unsupported or ambiguous constructs are reported through evidence, confidence, dynamic-name flags, or diagnostics rather than presented as complete architectural truth. Launch substitutions and unresolved interface types remain explicitly unresolved instead of being guessed from similar names.

Python source decoding follows encoding cookies and BOMs. Parser resource limits and invalid source produce per-file source_parse_failed diagnostics; other files remain available, including on cached runs. Nodes prints these diagnostics on stderr and audit JSON includes them. A publisher, subscription, service, client or action endpoint that cannot be attributed to a node hides only the missing-peer findings it could explain: same kind and direction, and the same name when the name is a literal. Files that fail to parse do not switch connectivity checks off; each missing-peer finding instead notes how many files could not be read. Package-local node inheritance and literal procedural Node()/rclpy.create_node() assignments are supported; unresolved receivers or bases carry incomplete-analysis notes.

C++ source scanning, cache invalidation and watch share these extensions: .c++, .cpp, .cxx, .cc, .C, .hpp, .hh, .hxx, .h++, .h, .ipp, .tpp.

Installation

Requires Python 3.10 or newer. A ROS 2 installation is not required for source analysis.

For a globally available CLI in an isolated environment, use pipx:

pipx install ros2inspector

Run it once without keeping it installed:

pipx run ros2inspector --help

Inside an existing Python virtual environment, use pip:

pip install ros2inspector

Install from source for development:

git clone https://github.com/aminebensaid66/ros2_inspector
cd ros2_inspector
pip install -e ".[dev]"

Quickstart

# Workspace summary
ros2inspector scan

# Package metadata and dependencies
ros2inspector packages --show-deps

# Nodes and their connections
ros2inspector nodes --show-connections

# Communication graph as Mermaid
ros2inspector graph comms --format mermaid

# Interactive HTML graph
ros2inspector viz --no-open

# Built-in architecture checks
ros2inspector audit

# Policy enforcement
ros2inspector validate --policy ros2inspector_policy.yaml

Commands

scan

ros2inspector scan [PATH] [--format table|json|yaml]

Discovers ROS 2 packages and displays a health-scored workspace summary.

packages

ros2inspector packages [-C PATH] [--filter cpp|python|meta] [--sort name|score|version] [--show-deps] [--format table|json|yaml]

Shows package metadata, type, license, maintainers, health score, and optional dependency details.

nodes

ros2inspector nodes [-C PATH] [--format table|json|yaml] [-p PACKAGE] [-o FILE] [--show-connections] [--no-cache]

Lists discovered nodes and their publishers, subscriptions, services, clients, action servers, and action clients. --show-connections includes resolved peer connections and interface types.

graph

ros2inspector graph [deps|comms|full] [-C PATH] [--format mermaid|dot|json] [-p PACKAGE] [-o FILE] [--no-cache]

Graph views:

View Contents
deps Package dependencies
comms Node-to-topic, service, and action communication
full Dependency and communication relationships together

In Mermaid, DOT and viz output, arrows follow the data or request: publisher → topic → subscriber, and client → service/action → provider. JSON output keeps the model's orientation, where every endpoint edge points from the node to the topic, service or action.

Examples:

ros2inspector graph deps --format json
ros2inspector graph comms --format mermaid -o architecture.mmd
ros2inspector graph full --format dot | dot -Tsvg -o architecture.svg

viz

ros2inspector viz [-C PATH] [-o FILE] [--open|--no-open] [--no-cache]

Generates a self-contained interactive Cytoscape.js HTML graph.

audit

ros2inspector audit [PATH] [--format table|json|yaml] [--fail-on error|warning|info] [--strict] [--exclude NAME] [--no-cache]

Runs built-in connectivity checks for orphan topics, dead outputs, isolated nodes, and unmatched service or action endpoints.

validate

ros2inspector validate [PATH] --policy FILE [--fail-on error|warning|info] [--format table|json|yaml] [--no-cache]

Validates the workspace against a YAML policy file.

docs

ros2inspector docs [PATH | -C PATH] [-o DIR] [--format markdown|html] [--force] [--no-cache]

Writes architecture documentation: an index with summary, package and node tables, and one page per package with dependencies and endpoints. Markdown output includes Mermaid dependency and communication diagrams. Output defaults to <workspace>/ros2inspector_docs. Pages for removed packages are deleted on the next run, Markdown and HTML pages can share one directory, and the command refuses to write into a non-empty directory it did not create unless --force is given. Set SOURCE_DATE_EPOCH for reproducible output.

watch

ros2inspector watch [PATH | -C PATH] [--policy FILE | --audit] [-i SECONDS] [--fail-on error|warning|info] [--once] [--verbose] [--no-cache]

Re-runs policy validation (or the built-in audit with --audit) whenever a source, interface, launch, config or build file changes, or a README or test file that affects a package's health score. After the first run it prints only new and resolved findings. Editing the policy file reloads it. The default policy is <workspace>/ros2inspector_policy.yaml. --once runs a single validation for scripts. Exit codes: 0 clean, 1 findings at or above --fail-on, 2 invalid policy, 3 workspace or analysis error, 130 stopped before the first run finished.

doctor

ros2inspector doctor [PATH | -C PATH] [--json]

Read-only diagnostics: versions, workspace and source roots, package count, unparsable or duplicate package.xml files, ROS_DISTRO, required modules, the bundled visualization asset and whether the cache is writable. Exits 1 when any check fails; warnings are informational.

design

ros2inspector design [-o DIR] [-p PORT] [--open|--no-open]

Opens a local browser-based package designer (bound to 127.0.0.1, protected by a per-session token). Add packages and nodes, give them publishers, subscribers, services, clients and action servers or clients, wire publishers to subscribers, then Generate to write ready-to-build ament_python packages into DIR (use your workspace's src/). The design is saved in DIR/.ros2inspector_design.json.

Renaming an edited node updates only its actual constructor name literal and the relevant registration and launch entries. Other strings, comments and docstrings stay intact. If a constructor or edited metadata cannot be identified safely, Generate refuses with HTTP 409 before moving or writing files; its message explains what to update by hand.

Regenerating is safe: a file is rewritten only if you have not edited it since the last generation, files you added are never touched, and renaming a package moves its directory. Directories that were not created by the designer are never modified.

Missing-peer findings affected by a failed or incomplete source analysis have confidence: "uncertain" and severity info. They describe an unproved absence and do not fail --fail-on warning or --fail-on error. A file that could not be inspected can contain peers for any workspace package, so its uncertainty is workspace-wide; attributed unresolved endpoints remain restricted to names they could supply. Proven interface conflicts, naming, dependencies and health checks remain enabled. SARIF represents uncertain findings as notes with a confidence property; a baseline for an uncertain finding cannot hide its later confirmed counterpart.

Python node inheritance is resolved across workspace packages, including inherited endpoints and deployment entry points. The cache records workspace source dependencies and invalidates inheritance results when a base package changes. Unknown imported bases used by an entry point remain incomplete definitions rather than disappearing.

Global options

ros2inspector --version
ros2inspector --quiet scan
ros2inspector --include-tests nodes
ros2inspector --preset nav2 scan
ros2inspector --factory-patterns factories.yaml scan

--version prints the package version. --quiet suppresses diagnostic headers and progress output. Test source nodes are excluded by default; --include-tests adds them to architecture results. The nav2 preset recognizes TwistPublisher, TwistSubscriber, and SimpleActionServer helpers. Custom C++ endpoint factories can be declared in YAML:

factories:
  - call: my_pkg::TopicPublisher
    kind: publisher
    name_arg: 1
    msg_type: std_msgs/String

name_arg is the zero-based constructor argument containing a literal ROS name. Supported kinds are publisher, subscription, service, client, action_server, and action_client. Factory-derived endpoints retain source evidence and medium or low confidence. Literal Python and C++ parameter defaults can resolve endpoint names and are labeled parameter_default. When a launch Node passes literal parameters=[{...}] dictionaries, the last value for that parameter replaces the default for that deployment and the edge carries launch_parameter_override evidence. Parameters that cannot be read statically, such as a YAML file path or a substitution, mark the deployment's parameters field unresolved, leave the endpoint name unresolved, and suppress connectivity findings the default would otherwise cause. Computed defaults remain unresolved. Endpoint names are resolved the way rcl resolves them: the name and each remap rule are expanded with the node's namespace and name before matching, and node-qualified (node:from:=to) and rostopic:///rosservice:// rules apply only where ROS applies them. A rule that names an action leaves that action unresolved, because Jazzy releases differ: rcl_action 9.2.7 ignores such a rule and 9.2.11 applies it. A namespace pushed by PushRosNamespace (or push-ros-namespace in XML and YAML) inside a group applies to the nodes in that group; one that cannot be read statically leaves the deployment unresolved instead of guessing. Literal Python QoSProfile settings and C++ rclcpp::QoS(...) settings are reported when available. The qos_compatibility rule reports only explicit reliability or durability mismatches; unknown QoS settings do not create findings. Remaps retain source order and duplicate match keys; the first matching rule wins. JSON remaps values in launch records and node deployments are arrays of [from, to] pairs, including an empty array when there are no rules (previously an object). See the remap JSON schema. A node qualifier precedes the optional kind prefix, for example talker:rostopic://chatter or talker:rosservice://chatter. Node-level remaps are supported; SetRemap launch actions remain unsupported and must not be treated as resolved global remaps.

Output formats

scan, packages, nodes, audit, and validate support table, JSON, and YAML output where applicable. graph supports Mermaid, DOT, and JSON. viz writes interactive HTML.

Machine-readable records go to standard output while diagnostics go to standard error, which keeps pipelines clean:

ros2inspector nodes --format json | jq '.[] | select(.package == "my_pkg")'
ros2inspector audit --format yaml > audit.yaml
ros2inspector graph comms --format mermaid -o architecture.mmd

Policy files

Create a ros2inspector_policy.yaml file:

version: 1
rules:
  - type: health_threshold
    min_score: 70
    severity: warning

  - type: license
    allowed: [Apache-2.0, MIT]
    severity: error

  - type: naming
    packages:
      pattern: '^[a-z][a-z0-9_]*$'
      severity: warning
    nodes:
      pattern: '^[A-Z][a-zA-Z0-9]*Node$'
      severity: info

  - type: dependency
    forbidden:
      - { from: perception, to: planner, severity: error }
    required:
      - { package: navigation, depends_on: nav_core, severity: warning }

  - type: no_circular_deps
    severity: error

  - type: topic_connectivity
    no_publisher: true
    no_subscriber: true
    type_mismatch: true
    exclude: [/rosout, /clock]

  - type: maintainer_required
    require_email: true
    severity: warning

topic_connectivity also reports interface_type_mismatch (severity severity_type_mismatch, default error) when endpoints on one topic, service or action use different interface types; type_mismatch: false turns it off. Connection output lists each endpoint with its own type and only names peers whose type is compatible.

Supported rule types are health_threshold, license, naming, dependency, no_circular_deps, topic_connectivity, node_isolation, service_connectivity, action_connectivity, qos_compatibility, maintainer_required, and version_not_default.

Policy keys and nested options are checked before analysis. Unknown keys include a spelling suggestion when available. Booleans must be YAML booleans, license allowlists must be lists of non-empty strings, and health thresholds must be integers from 0 to 100. Invalid configuration or unsupported file encoding returns exit code 2 with the rule, line and field named.

CI integration

- name: Install ROS 2 Inspector
  run: pip install ros2inspector

- name: Architecture audit
  run: ros2inspector audit --fail-on warning --format json > audit.json

- name: Policy validation
  run: ros2inspector validate --policy .ros2inspector_policy.yaml --fail-on error

Relevant exit codes:

Code Meaning
0 Success or no findings at the configured threshold
1 Findings at or above the configured threshold
2 Invalid invocation or policy configuration
3 Workspace or policy file not found

Architecture

ros2inspector/
├── cli/           # scan, packages, nodes, graph, viz, audit, validate, docs, watch, doctor, design
├── design/        # package designer: models, code generator, local server and UI
├── discovery/     # workspace, package, and interface discovery
├── static/        # Python, C++, interface, launch, package, and health parsers
├── model/         # Pydantic schemas and the NetworkX UAM
├── graph/         # Mermaid, DOT, and JSON renderers
├── viz/           # interactive HTML renderer and bundled Cytoscape.js
├── policy/        # policy loader, engine, and rules
├── cache/         # diskcache-backed incremental analysis
├── output/        # output package plumbing
└── utils/         # shared utilities

The Unified Architecture Model is a networkx.MultiDiGraph. Graph nodes represent packages, ROS nodes, topics, services, actions, and interfaces. Edges carry relationships such as depends_on, publishes, subscribes, provides, calls, defined_in, and uses_interface.

Ignoring generated or vendor trees

Package scans automatically prune common generated directories such as build/, install/, log/, .git/, virtual environments, node_modules/, and __pycache__/. For large package-local vendor or generated trees, add a .ros2inspectorignore file at the package root. It accepts simple shell-style glob patterns; a pattern ending in / prunes that entire subtree before traversal.

vendor/
third_party/generated/
*.generated.py

Development

pip install -e ".[dev]"
pytest
ruff check ros2inspector/
mypy ros2inspector/

The test suite uses fixture workspaces under tests/fixtures/ and does not require a live ROS 2 environment.

Headless-browser tests for the designer and the generated HTML pages need Playwright:

pip install -e ".[dev,browser]"
python -m playwright install chromium
ROS2INSPECTOR_SCREENSHOT_DIR=screenshots pytest tests/browser -m browser --no-cov

Publishing

Maintainers should follow RELEASING.md. Publishing is performed by GitHub Actions through PyPI Trusted Publishing when a GitHub Release is published.

Analysis and output guarantees

Static analysis is deliberately conservative. Common C++ header/source separation, qualified out-of-class methods, indirect node inheritance, and direct rclcpp::Node::make_shared/std::make_shared<rclcpp::Node> construction are recognised when ownership is supported by source evidence. Ambiguous associations are retained as incomplete-analysis diagnostics instead of being guessed.

Launch presence is represented as known, conditional, or unresolved. Conditional and unresolved deployments do not establish definite missing-peer findings. Python launch analysis follows constructor lists and add_action/add_entity calls in order. Literal IfCondition/UnlessCondition namespace pushes, scoped and unscoped groups, and forwarding=False groups follow launch namespace rules. Unknown conditions or mutations mark the dependent namespace unresolved.

Includes of workspace launch files inherit the namespace and conditions at the include. Literal paths and common package-share path expressions resolve to source launch files. An included deployment keeps the child's launch_file and records its direct parent in included_from. A file reached through an include also retains conditional standalone deployments, which cannot establish definite connectivity findings. Unresolved include targets and cycles produce launch_include_unresolved diagnostics and suppress missing-peer claims that an unseen child could explain. Include arguments that cannot be consumed statically leave the included namespace unresolved. Launch files are never executed.

Grouped layout positions all displayed leaf entities, including shared communication entities and disconnected components. uses_interface relationships remain in the HTML payload and are controlled by the relationship filter rather than being discarded.

For nodes, graph, and viz, output parent directories must already exist. File writes use a temporary sibling and atomic replacement; ordinary path/permission failures exit 2 without a traceback.

License

MIT. See LICENSE.

Metadata

Release files for ros2inspector 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ros2inspector 0.2.0
File Size Uploaded Provenance Actions
ros2inspector-0.2.0.tar.gz 282.2 kB With provenance Details for ros2inspector-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ros2inspector 0.2.0
File Interpreter ABI Platform Actions
ros2inspector-0.2.0-py3-none-any.whl Python 3 none any Details for ros2inspector-0.2.0-py3-none-any.whl

Total release size: 582.2 kB

Release files / ros2inspector-0.2.0.tar.gz

Download URL ros2inspector-0.2.0.tar.gz
Size 282.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d2719fa814223d476664a5967f10825e6ab3f74892cbb3d44273531d65649768
BLAKE2b-256 checksum
How to use checksums
34f8afaac28dd0ae13cba36328c8ee2eb5373664d7bd4d6e34709305b5332629
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Release files / ros2inspector-0.2.0-py3-none-any.whl

Download URL ros2inspector-0.2.0-py3-none-any.whl
Size 300.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8d18e4b03c70a9b7f42fdca7a48d342cd9c69297af811a934db22fc9423f73e8
BLAKE2b-256 checksum
How to use checksums
5b17c9a3d90191bbe85798ed69956b4a95f20266270a8bd96de8108ebc5e55cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

Security for ros2inspector 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ros2inspector-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page