Skip to content

Agent Guide

AGENTS.md

Project

CANarchy is a CLI-first CAN security research toolkit with optional REPL and TUI front ends.

The implementation language is Python, and the project uses uv for dependency management, virtual environments, and packaging workflows.

The core design rule is simple:

The CLI is the contract. The REPL and TUI are views over the same engine.

The project is focused on:

  • CAN and CAN FD workflows
  • J1939-first heavy vehicle workflows
  • security research and protocol exploration
  • automation-friendly use by coding agents
  • automation-friendly development by coding agents
  • structured outputs suitable for pipelines and machine parsing

Project planning

GitHub Issues are the source of truth for project planning and task tracking.

Every change must be associated with an issue

This is a hard rule, not a suggestion.

  • Before starting any new feature, bug fix, or non-trivial refactor, check whether an open issue already covers it.
  • If no issue exists, create one with a clear title, description, and acceptance criteria before writing code.
  • Every commit that implements or fixes something must reference the relevant issue number using closes #N, fixes #N, or refs #N in the commit message.
  • Do not merge or push code changes that have no associated issue.
  • Bug fixes discovered incidentally (e.g. while working on a different issue) get their own issue opened first, then fixed.

The only exceptions are: * typo/whitespace-only changes in docs or comments * changes to .gitignore or other non-functional config

Branch and pull-request workflow

This is a hard rule, not a suggestion.

  • Before creating a branch, post a comment on the issue claiming it — for example: "Starting work on this; branch will be <branch-name>." This prevents two agents or contributors from independently implementing the same issue.
  • For any non-trivial feature, bug fix, refactor, or documentation change, agents shall create and work from a dedicated branch rather than committing directly on main.
  • Branch names should be short, descriptive, and tied to the issue when practical, for example issue-110-j1939-compare.
  • When issue-scoped work is complete, agents shall push the branch and open a pull request unless the user explicitly asks for a direct push or direct commit flow.
  • Pull requests should be the default handoff for review, CI, and merge discussion.
  • Agents shall not merge their own pull requests unless the user explicitly asks them to do so.
  • If urgent work must go directly to main, the user shall explicitly request that exception.

Changelog policy

CHANGELOG.md follows the Keep a Changelog format. Every non-trivial change must be recorded in the [Unreleased] section before or alongside the commit that introduces it.

This is a hard rule, not a suggestion.

  • Every new feature, bug fix, behaviour change, or significant documentation update must have a corresponding CHANGELOG.md entry under [Unreleased].
  • Group entries under the standard headings: Added, Changed, Deprecated, Removed, Fixed, Security, or Documentation.
  • Write entries from the operator's perspective — describe what changed and why it matters, not what files were edited.
  • Changelog entries must be committed in the same commit or PR as the change they describe. Do not leave the changelog update for a separate follow-up.
  • When a release is cut, the [Unreleased] section is promoted to a versioned heading. Do not manually create versioned headings outside of the release workflow.
  • The only exceptions are: typo/whitespace-only changes in docs or comments, changes to .gitignore or other non-functional config, and MkDocs nav or theme-only changes.

Versioning after release

This is a hard rule, not a suggestion.

  • After cutting release X.Y.Z, advance src/canarchy/__init__.py on main to X.Y.(Z+1).dev0 unless a different next-version target is explicitly planned and documented.
  • Do not leave main on the released version once unreleased work resumes.
  • Keep release tags on the stable release version only, such as v0.4.0.
  • If the next intended release is a minor or major bump instead of the next patch version, document that decision explicitly and set the .dev0 version accordingly.

PR acceptance criteria

A PR is not ready to merge until all of the following are true.

This is a hard rule, not a suggestion.

# Gate Notes
1 Issue referenced Commit message contains closes #N, fixes #N, or refs #N
2 Tests pass All existing tests pass; new behaviour has new tests
3 Changelog updated [Unreleased] section reflects the change (see Changelog policy)
4 Design spec current Any touched docs/design/ spec reflects the implemented behaviour — requirements updated, no stale wording
5 Test spec current Any touched docs/tests/ spec reflects the actual test coverage — new TEST-* IDs added, traceability table updated
6 Agent guide current If the change affects the command surface, MCP tools, output schema, or any workflow an agent would follow, AGENTS.md and docs/agents.md are updated
7 No stale documentation left Verify that no other doc (architecture docs, tutorials, command_spec.md) references old behaviour that the PR changes

When reviewing your own work before committing or pushing, explicitly check each gate in order. Do not defer documentation updates to a follow-up commit.

Other issue rules

  • Use GitHub Issues to track planned work rather than ad hoc task lists in docs.
  • Claim an issue before starting work. Post a comment on the issue as the very first action — before creating a branch or writing any code — stating that work is beginning and naming the intended branch. This is the coordination signal that prevents duplicate effort by other agents or contributors.
  • When starting or completing work that relates to an issue, update the issue with progress or implementation notes.
  • Commit and push the relevant code changes before closing an issue so the issue state matches the remote repository state.
  • Close an issue when its scope and acceptance criteria have been satisfied.
  • When creating a new issue, always include explicit acceptance criteria that define what must be true to close it.
  • If code changes only partially satisfy an issue, leave the issue open and note the remaining work.
  • If work for an issue is complete locally but has not yet been committed and pushed, explicitly offer to the user to commit and push the changes and then close or update the issue.
  • After completing issue-scoped work, summarize what was implemented, recommend the most sensible next steps, and present those next steps as a concise multiple-choice menu the user can select from.
  1. State that the issue work is complete locally.
  2. Summarize the shipped changes and verification results.
  3. Offer to commit, push the branch, and open a pull request if that has not happened yet.
  4. If the branch and pull request already exist, offer to update or close the issue if still needed.
  5. Recommend the next 2–4 highest-value follow-up tasks.
  6. Present the next steps as a multiple-choice menu, for example:

A. commit, push the branch, and open a pull request B. start the next recommended issue C. refine docs or tests around the completed work D. inspect or review the implementation before moving on


Primary goals

  1. Build a stable, scriptable command surface for CAN security research.
  2. Make common analyst workflows easy from the terminal.
  3. Support agentic use through deterministic commands, structured output, and explicit error handling.
  4. Keep protocol logic in the core engine, not in the UI.
  5. Preserve parity across CLI, REPL, and TUI wherever practical.

Non-goals for the first versions

Do not try to do all of these immediately:

  • Full OEM-specific protocol coverage
  • Deep GUI-first workflows
  • Large-scale cloud architecture
  • Automatic “AI magic” without clear, inspectable outputs
  • Feature parity with every existing CAN tool before the core model is stable

Product shape

CANarchy should be implemented as three layers:

1. Core engine

Responsible for:

  • transport backends
  • frame ingest and transmit
  • decode/encode pipelines
  • protocol state tracking
  • replay and mutation
  • analysis and reverse engineering helpers
  • event generation

2. Command layer

Responsible for:

  • canonical commands and subcommands
  • validation of arguments
  • output formatting modes
  • exit codes
  • scripting compatibility

3. Front ends

Responsible for presentation only:

  • CLI
  • REPL shell
  • TUI

Front ends must not contain business logic that cannot also be reached through the CLI.


Front-end rules

CLI

The CLI is the authoritative interface.

Every important workflow must be reachable as a non-interactive command.

Examples:

  • canarchy capture can0 --jsonl
  • canarchy capture --jsonl when [transport].default_interface or CANARCHY_DEFAULT_INTERFACE is configured
  • canarchy capture-info --file capture.log --json
  • canarchy decode --file capture.log --dbc truck.dbc --json
  • canarchy j1939 monitor --pgn 65262 --json
  • canarchy uds scan can0 --json
  • canarchy replay --file drive.log --rate 0.5

Single-interface transport commands may resolve their CAN interface from user config when the command-line value is omitted. The explicit command-line interface always has the highest precedence; use [transport].default_interface in ~/.canarchy/config.toml or CANARCHY_DEFAULT_INTERFACE for the fallback channel. This is separate from [transport].interface, which selects the python-can backend type such as socketcan, udp_multicast, pcan, vector, or kvaser. Use canarchy doctor for offline configured-backend dependency hints; live hardware access still requires operator-run validation against the adapter and bus.

For dashboard launchers, run canarchy web serve --file <capture> --bind 127.0.0.1:0 --json and read the startup envelope from stdout to obtain data.url. The complete startup result is flushed before the serving loop, including when stdout is a pipe; PYTHONUNBUFFERED is unnecessary. JSONL and human output are also flushed. Keep the subprocess alive while using its URL and terminate it when finished. HTTP request logging does not appear on structured stdout. This remains a CLI-only workflow.

Numeric CLI options must be finite: NaN and positive/negative infinity fail with a structured usage error before file processing or transport access. This includes --seconds, rates, gaps, durations, timeouts, and analytical thresholds. File --seconds 0 keeps its existing initial-timestamp window semantics; other documented zero/range rules are unchanged. Invalid floats are described in error text, never echoed as non-standard JSON numbers. DoIP ?timeout= also requires a finite positive value.

REPL

The REPL is a convenience layer for human operators.

The REPL should:

  • reuse the same command parser where possible
  • preserve context like active bus, loaded decode database, and session artifacts
  • expose the same operations as the CLI with minimal drift

TUI

The TUI is a state and visualization surface over the same engine.

The TUI should:

  • consume the same event stream model used by CLI/REPL
  • trigger the same underlying commands/actions
  • avoid introducing unique features that cannot be expressed as commands

The full-screen TUI shows non-event command answers, errors, help, and version text in a read-only result view. F2 switches between that view and the event panes; Esc returns to the panes. Event rows and live capture continue while results are open. Agents should still use non-interactive CLI or MCP output for automation.

The TUI's responsive workspaces are Traffic, Signals, J1939, UDS, and Findings (Alt+1–Alt+5). F3 expands the activity band; Enter shows the selected row's full fields on narrow terminals and Esc returns to the table. These presentation controls do not alter the CLI command contract or the capture lifecycle.

Traffic defaults to a bounded summary keyed by (bus, arbitration_id, is_extended_id); v switches to the chronological event log, and F4 freezes inspection independently of capture or returns live. The inspector shows complete raw hex/bit data, changed-byte offsets with a textual legend, decoded evidence when available, and recent history. /filter traffic uses the shared CLI frame-filter grammar for typed id==, pgn==, and sa== expressions; plain text remains a substring filter. The CLI filter command also accepts sa==<source-address> for extended J1939 frames. Invalid typed filters and unknown TUI sort fields are reported without changing the previous view state.

DBC decoded_message events with embedded frames populate the Traffic identifier summary and event log, with child signals correlated by source, frame index, and message name. Live follow selects only visible traffic under the active filter; returning live from a historical log row advances to the newest visible event.

DBC decoded_message and signal events share a zero-based payload.frame_index local to each decode invocation. The TUI combines a parent/child signal only when source, frame_index, message_name, and signal_name match; repeated equal-valued frames stay separate. Missing indexes are not inferred from values. Decoded signal events inherit the source-frame timestamp, including zero; unknown timestamps remain null without substituting wall-clock decode time. Signal-only streams preserve the source time series without parent joins.


Initial capability priorities

P0

  • CAN / CAN FD capture
  • transmit/send
  • replay
  • filtering
  • stats
  • structured export (json, jsonl)
  • DBC-backed decode and encode
  • J1939 monitor / decode / PGN-first workflows
  • clear exit codes and error schema

P1

  • UDS scan and trace support (CAN/ISO-TP and DoIP — uds scan|trace doip://host:port?logical_address=0x0E80)
  • session save/load
  • SQLite export
  • remote backends
  • mutation/fuzzing primitives
  • reverse engineering helpers

P2

  • TUI dashboard
  • plugin SDK
  • additional output sinks such as MQTT/Kafka/webhooks
  • advanced anomaly detection and signal inference

Security research focus

Active fuzz guided runs archive each finding before the next transmission under --findings-dir, <corpus>/findings, or ~/.canarchy/findings (in that order). Use the returned archive_path and campaign_id to inspect campaign.json and finding-*.json offline. Findings retain payload/response evidence and lineage after corpus pruning; storage failures stop transmission. Dry-run creates no archive. MCP accepts findings_dir and retains its acknowledgement gate and dry-run default.

CANarchy is intended for defensive research, protocol analysis, lab experimentation, red-team style validation, and tool-assisted reverse engineering.

When implementing features, prefer:

  • reproducibility
  • evidence capture
  • traceability
  • safe defaults
  • explicit operator intent for active transmission and fuzzing

Suggested safeguards:

  • make active transmit/fuzz commands obviously distinct from passive commands
  • support dry-run modes where possible
  • log enough metadata for lab replay and reporting

Architectural principles

1. Structured events over raw text

Internally, the system should model events such as:

  • frame
  • decoded message
  • signal value
  • J1939 PGN/SPN observation
  • UDS request/response transaction
  • anomaly
  • replay action
  • fuzz action
  • alert

Prefer typed event objects over free-form strings.

For protocol transactions that can be partially reconstructed from captured traffic, include explicit completeness fields instead of implying that every decoded payload is whole.

Where optional protocol enrichers are present, such as a Scapy-backed diagnostic adapter for UDS, surface them through stable summary-level fields rather than leaking third-party runtime objects into command output.

2. Human output must never break machine output

Every command should support explicit output modes:

  • --json
  • --jsonl
  • --text

Do not mix human decoration into JSON output.

3. Deterministic behavior matters

Commands should behave predictably.

Avoid:

  • hidden prompts in non-interactive mode
  • unstable field names
  • ambiguous time formats
  • random output ordering unless explicitly requested

4. Stable command grammar

Prefer a command layout like:

  • <domain> <action>
  • or <action> <object> only when very obvious

Examples:

  • j1939 monitor
  • j1939 decode
  • j1939 compare
  • j1939 inventory
  • skills search
  • uds scan
  • re signals
  • session save

5. Protocol semantics are first-class

Do not force users to stay at the raw-frame layer when protocol-aware workflows exist.

Examples:

  • allow PGN/SPN-first commands for J1939
  • allow request/response transaction views for UDS
  • allow decode-aware filtering

Coding guidelines

General

  • Favor readability and explicitness over cleverness.
  • Keep modules small and focused.
  • Add tests for protocol parsing, decode logic, and CLI behavior.
  • Prefer pure functions for transforms and protocol analysis.
  • Keep transport adapters separate from semantic layers.

Error handling

All errors should be actionable.

At minimum, errors should communicate:

  • category
  • message
  • likely cause
  • retry or corrective hint when appropriate

Prefer structured errors internally and in JSON output.

Logging

  • Use structured logging internally.
  • Keep logs useful for replay and debugging.
  • Avoid noisy logs in default CLI usage.

Configuration

  • Prefer explicit CLI flags first.
  • Add config files only where they reduce repetition without hiding behavior.
  • Make the effective configuration inspectable.

Output and exit code conventions

Suggested exit codes:

  • 0 success
  • 1 user/input/usage error
  • 2 backend or transport error
  • 3 decode/schema/plugin error
  • 4 partial result / partial success

Suggested JSON result shape:

{
  "ok": true,
  "command": "j1939 monitor",
  "data": {},
  "warnings": [],
  "errors": []
}

Suggested JSON error shape:

{
  "ok": false,
  "command": "decode",
  "errors": [
    {
      "code": "DBC_LOAD_FAILED",
      "message": "Failed to parse DBC file.",
      "hint": "Validate file format and line endings."
    }
  ]
}

Argument-parsing failures use the same envelope, so one error parser covers every failure. An unrecognised option, an unknown command or subcommand, a missing required argument, or a value that fails type conversion returns command: "cli", code INVALID_ARGUMENTS, and exit code 1 in the requested format — canarchy stats --unknown-option --json emits JSON, not a text block. The output flags are mutually exclusive, so passing more than one is itself an argument error; the envelope reporting it is rendered by the fixed precedence --json > --jsonl > --text > --table, independent of order. --help and --version remain plain text with exit code 0. See docs/design/cli-parse-errors.md.


Proposed initial command tree

canarchy
  capture
  capture-info
  send
  replay
  filter
  stats
  decode
  encode
  skills
    provider list
    search
    fetch
    cache list
    cache refresh
  plugins
    list
    info
    enable
    disable
  datasets
    provider list
    search
    inspect
    fetch
    cache list
    cache refresh
    convert
    stream
    replay
  export
  session
    save
    load
    show
    verify
    annotate
    attach
    bundle
    import
  j1939
    monitor
    decode
    compare
    pgn
    spn
    tp
    dm1
    summary
    inventory
  uds
    scan
    trace
    services
    subservices
    ecu-reset
    tester-present
    security-seed
    dump-dids
    read-memory
    auto
  re
    signals
    counters
    entropy
    correlate
  shell
  tui

This tree is a starting point, not a lock.

Active-transmit MCP tools (send, generate, gateway, replay, sequence_replay, xcp_scan, fuzz_guided, and fuzz payload|replay|arbitration-id|signal|spn) are behind the active-transmit safety model — mandatory ack_active=true with dry_run defaulting to true. The authoritative CLI-to-MCP coverage matrix (exposed / excluded / deferred) is maintained in docs/design/mcp-server.md, and tests/test_mcp.py enforces that every implemented command is either exposed or a documented exclusion (shell, tui, web serve, cannelloni send, mcp serve, mcp install, completion, datasets stream, dbc generate-c, plugins enable, plugins disable, the active uds workflows uds subservices|ecu-reset|tester-present|security-seed|dump-dids|read-memory|auto, the active xcp info / xcp dump, the active doip command group, and fuzz identify). The uds_scan / uds_trace tools are exposed for CAN interfaces but refuse a doip:// target with DOIP_MCP_EXCLUDED, since DoIP is active TCP egress to an arbitrary host and stays a CLI-only operator action. The active UDS workflows (uds subservices, ecu-reset, tester-present, security-seed, dump-dids, read-memory, auto) transmit invasive diagnostic requests and are CLI-only operator actions behind the active-transmit gate; the reference uds services catalog stays exposed (its active-probe mode only activates when a CLI caller supplies an interface on the command line; a configured default interface never promotes it, so MCP uds_services is reference-only regardless of local configuration). xcp info (capability queries) and xcp dump (bounded memory upload) connect to a slave and are likewise CLI-only; the broadcast xcp scan stays exposed. The dedicated doip command group (doip discovery|services|ecu-reset|tester-present|security-seed|dump-dids) is also CLI-only — active network egress (UDP discovery + TCP diagnostic sessions) behind the active-transmit gate. fuzz identify is a stateful, multi-round human-in-the-loop replay/narrowing workflow (one bisected window replayed per invocation) and stays CLI-only.

MCP tool results set isError=true whenever the canonical JSON envelope has ok=false, including command failures, excluded targets, missing active-transmit acknowledgement, and isolated unexpected exceptions. Codes, messages, and hints remain in the JSON text. Successful calls set isError=false. SDK input-schema validation runs before the command handler: missing required fields or invalid schema types return isError=true with SDK validation text rather than a CANarchy JSON envelope. Correct the input and retry; ordinary errors leave the session available for later calls.

For plugin automation, agents can use MCP plugins_list / plugins_info or CLI canarchy plugins list|info --json to inspect discovered Python entry-point plugins. Plugin toggles are user configuration actions; use CLI canarchy plugins enable|disable <name> only when explicitly requested by the operator.

For dataset automation, agents should prefer MCP dataset tools when available, or explicit CLI JSON output otherwise. datasets_search / datasets search --json and datasets_inspect / datasets inspect --json include stable machine fields: ref, is_replayable, is_index, default_replay_file, download_url_available, and source_type. datasets_fetch distinguishes curated indexes from normal dataset entries with is_index, index_instructions, and download_instructions, and reports data_is_local: true when the fetch materialised real bytes into the cache rather than recording provenance for a remote file. When data_is_local is true, next_steps names the local-file commands to run against cache_path — datasets download and datasets replay do not apply and will fail with DATASET_REPLAY_UNAVAILABLE. Use MCP datasets_replay_plan or CLI datasets replay --dry-run --json for safe replay preflight; use datasets replay --list-files --json to choose a replay file and --file <id-or-name> to select it. Use --max-frames or --max-seconds to bound replay. For catalog:comma-car-segments, use --platform <name> to filter dynamic HuggingFace segment manifests and --limit <n> to bound file listings before selecting a segment. Use datasets stream --max-frames <n> to bound local downloaded dataset-file streaming; --chunk-size controls JSONL provenance chunk metadata and is not a frame limit. comma-rlog streaming requires optional openpilot LogReader support (uv pip install git+https://github.com/commaai/openpilot.git on Python 3.12.x) and otherwise returns COMMA_RLOG_SUPPORT_UNAVAILABLE. Actual dataset frame streaming remains CLI-only. Curated index entries that cannot be replayed return DATASET_INDEX_NOT_REPLAYABLE.

Every dataset in the catalog provider downloads from a host (zenodo.org, figshare.com, huggingface.co, ocslab.hksecurity.net) that a typical sandboxed agent environment blocks, so datasets fetch followed by a replay fails there with a transport error. When there is no egress, use the offline provider instead: datasets search --provider offline --json lists synthetic datasets that generate locally and need no network. datasets fetch offline:<name> writes real data into the cache and returns its path in cache_path, after which the local-file commands (datasets convert, datasets stream, capture-info, stats, j1939 summary, replay --file) work normally. Offline datasets carry synthetic: true in the machine fields, a licence string identifying them as synthetic, and a SYNTHETIC: description prefix. The data has real protocol structure but is not vehicle behaviour: use it to exercise tooling, never report analysis of it as a research finding. Current offline datasets are can-basic (candump, with a discoverable counter), j1939-basic (candump, with BAM/TP and a DM1 active fault), can-intrusion (hcrl-csv, attack-labelled), and signal-decoded (decoded-signal-csv).

A bare dataset ref (no provider: prefix) resolves against providers in order, and datasets_provider_list / datasets provider list --json reports that effective order as search_order plus a per-provider order index. The default is catalog before offline, so a bare ref prefers a real dataset over synthetic data of the same name; an operator can reorder it with [datasets].search_order in ~/.canarchy/config.toml. Prefer a prefixed ref (catalog:road, offline:can-basic) when the provider matters. A provider name in that config that does not exist fails every datasets command with DATASET_PROVIDER_NOT_FOUND (exit 1); a malformed value fails with DATASET_SEARCH_ORDER_INVALID.

For DBC reconnaissance, agents can use canarchy dbc inspect <dbc> --layout --json or MCP dbc_inspect with layout=true to retrieve cantools-rendered bit-layout diagrams, signal trees, and choice tables as structured message fields.

For compare, identify each message by (arbitration_id, is_extended_id). Standard and extended frames with the same numeric ID have separate rows. id_count and summary.unique_ids count these pairs. Use summary *_identifiers arrays (objects with arbitration_id and is_extended_id) for unambiguous category membership; legacy *_ids arrays remain sorted, deduplicated numeric views. Summaries include all findings even when --top limits rows.

MCP output caps measure final canonical CANarchy JSON tool envelopes in UTF-8, including truncation metadata and warnings, excluding transport wrapping and SDK-generated schema-validation or protocol errors. CANARCHY_MCP_MAX_RESPONSE_BYTES defaults to 512000 and requires at least 1024 bytes; smaller positive values fail before startup. Truncation metadata lists only paths present in the returned data. Oversized scalar data or diagnostics may become an actionable stub; compact diagnostics preserve a short original error code, or use RESPONSE_TRUNCATED when no bounded code is available. Re-run the equivalent CLI command for the full result.

For stdin pipelines, capture-info --file -, stats --file -, and filter --file - read candump text from stdin. filter --stdin, decode --stdin, and j1939 decode --stdin read JSONL FrameEvents from stdin regardless of output format. These pipelines are CLI-only: the MCP server owns stdin as its JSON-RPC transport, so passing - to any MCP file or source parameter is refused before the command runs with error code STDIN_MCP_EXCLUDED and the MCP tool-failure flag set. MCP callers must pass a real file path; write a piped stream to a file first.

For security workflow examples that combine commands into complete agent tasks, see docs/security-use-cases.md. The primary documented workflows are CAN/J1939 capture triage, dataset-driven IDS experimentation, DBC-assisted signal reconnaissance, protocol-aware incident reporting, and safe replay/regression testing.


J1939 expectations

J1939 should be treated as a first-class workflow, not an afterthought.

Priorities:

  • PGN-first filtering
  • SPN presentation where decode data is available
  • source address tracking
  • TP/BAM reassembly support
  • DM message visibility
  • ECU/node activity summaries

The user should not be forced to manually reason from raw 29-bit IDs for common J1939 tasks.


Reverse engineering expectations

Reference-DBC matching in re suggest uses exact physical-bit overlap for Intel and Motorola signals; sharing a byte alone is insufficient. Confidence remains heuristic, and corrected overlap can change suggestion inclusion and ranking without changing output fields.

Reverse engineering features should be evidence-driven and explainable.

Good early features:

  • field entropy ranking
  • likely counter detection
  • likely checksum detection
  • correlation against known external series
  • changing-bit analysis
  • signal boundary suggestions

Do not present guesses as facts. Always expose confidence and rationale where possible.


TUI expectations

The TUI should be useful for live analysis, demos, and triage.

Good initial panes:

  • bus/interface status
  • live traffic table
  • decoded signals
  • J1939 PGN/SPN activity
  • node list
  • alerts/events
  • command entry area

The TUI should subscribe to the same event model used elsewhere.


Testing expectations

At minimum, cover:

  • frame parsing and formatting
  • J1939 ID decomposition
  • DBC-backed decode behavior
  • replay timing behavior
  • CLI argument validation
  • JSON output stability
  • error schema behavior

Where possible, use fixtures for:

  • representative CAN logs
  • representative J1939 traces
  • malformed inputs
  • edge cases like extended IDs, CAN FD, transport protocol fragmentation

Documentation expectations

Documentation should be written for three audiences:

1. Operators

Show practical commands and workflows.

2. Developers

Explain architecture, module responsibilities, and extension points.

3. Agents

Keep command help, output schemas, and command behavior explicit and stable.

Prefer example-heavy docs.


Design and architecture documents

CANarchy maintains living specification documents alongside the code. These documents are the authoritative record of how the system is designed to work.

Document types

Architecture documents (docs/architecture/)

Describe the overall structure and module responsibilities. Each document should cover:

  • the problem the component solves
  • its boundaries and responsibilities
  • how it interacts with adjacent components
  • key design decisions and the reasons for them
  • what is intentionally out of scope

Software design specs (docs/design/)

Describe the intended behavior of a specific feature or subsystem before or during implementation. Each spec should include:

  • document control metadata including current status and affected command surface
  • the goal and user-facing motivation
  • explicit requirement IDs in a stable format such as REQ-<AREA>-NN
  • the proposed command surface or API shape
  • data models and event types involved
  • output format definitions (JSON schema or representative examples)
  • error cases and expected error codes
  • open questions or deferred decisions

Test specs (docs/tests/)

Describe the test strategy and coverage expectations for a component or feature. Each test spec should include:

  • document control metadata including the related design spec
  • explicit test case IDs in a stable format such as TEST-<AREA>-NN
  • what behaviors must be covered
  • requirement-to-test traceability mapping back to the design spec requirement IDs
  • representative test cases including happy path, edge cases, and failure modes
  • fixture requirements
  • what is explicitly not tested and why

Spec language standard

All design specs and test specs must use the formats defined in docs/spec-template.md.

Design specs shall write requirements using EARS (Easy Approach to Requirements Syntax):

EARS type Template
Ubiquitous The system shall …
Event-driven When \<trigger>, the system shall …
State-driven While \<state>, the system shall …
Optional feature Where \<feature> is specified, the system shall …
Unwanted behaviour If \<condition>, the system shall …

Each requirement row in the table shall include a Type column identifying which EARS pattern applies.

Test specs shall write test cases using Gherkin Given/When/Then syntax inside a fenced gherkin block, followed by a **Fixture:** line naming the required files or environment.

Rules for agents

  • When implementing a new feature or command, check docs/design/ for an existing spec before writing code. If a spec exists, implement against it. If it conflicts with the code, flag the conflict rather than silently diverging.
  • When no spec exists for a significant new feature, create one in docs/design/ before or alongside the implementation. Follow docs/spec-template.md for the required sections, requirement IDs (REQ-<AREA>-NN), and EARS language.
  • When adding new protocol logic, transport backends, or output formats, update or create the relevant architecture document in docs/architecture/.
  • After implementing a feature, verify the test spec in docs/tests/ matches what was actually tested, includes test IDs (TEST-<AREA>-NN), uses Gherkin Given/When/Then, and traces back to the design-spec requirement IDs. Update it if coverage changed.
  • Specs are living documents. Update them when the implementation changes rather than letting them drift.
  • Do not create specs for trivial changes (single-function fixes, minor output tweaks). Reserve them for features that affect the command surface, data model, or module boundaries.
  • Before marking work complete or opening a PR, run through the PR acceptance criteria checklist above. Documentation currency (gates 4–7) is a required part of acceptance, not optional follow-up work.

Preferred development style for agents

When proposing code changes or new modules, agents should:

  • preserve CLI stability
  • avoid leaking UI logic into core modules
  • prefer structured outputs over formatted prose
  • add tests with new behavior
  • document new commands and output fields
  • keep protocol-specific logic in well-named modules
  • avoid unnecessary dependencies in the core runtime

When uncertain, agents should optimize for:

  1. CLI reliability
  2. output stability
  3. protocol correctness
  4. architecture clarity
  5. UI polish

Initial implementation suggestion

A reasonable first milestone is:

  • core frame/event model
  • SocketCAN capture/send/replay
  • JSON/JSONL output modes
  • DBC decode pipeline
  • basic J1939 decomposition and monitor command
  • simple session model
  • shell scaffolding

Only after the command and event model feel stable should the project invest heavily in TUI and plugin work.


Working summary

CANarchy should become:

A CLI-first CAN security research environment with structured outputs, protocol-aware workflows, and shared core logic across CLI, REPL, and TUI.

When making design choices, preserve the following order of importance:

  • command clarity
  • structured outputs
  • protocol correctness
  • session reproducibility
  • front-end parity
  • visual polish


For compare, identify each message by (arbitration_id, is_extended_id). Standard and extended frames with the same numeric ID have separate rows. id_count and summary.unique_ids count these pairs. Use summary *_identifiers arrays (objects with arbitration_id and is_extended_id) for unambiguous category membership; legacy *_ids arrays remain sorted, deduplicated numeric views. Summaries include all findings even when --top limits rows.

For dashboard launchers, run canarchy web serve --file <capture> --bind 127.0.0.1:0 --json and read the startup envelope from stdout to obtain data.url. The complete startup result is flushed before the serving loop, including when stdout is a pipe; PYTHONUNBUFFERED is unnecessary. JSONL and human output are also flushed. Keep the subprocess alive while using its URL and terminate it when finished. HTTP request logging does not appear on structured stdout. This remains a CLI-only workflow.

MCP Server Integration

Active fuzz guided runs archive each finding before the next transmission under --findings-dir, <corpus>/findings, or ~/.canarchy/findings (in that order). Use the returned archive_path and campaign_id to inspect campaign.json and finding-*.json offline. Findings retain payload/response evidence and lineage after corpus pruning; storage failures stop transmission. Dry-run creates no archive. MCP accepts findings_dir and retains its acknowledgement gate and dry-run default.

CANarchy ships a native Model Context Protocol (MCP) server. Agents that support MCP tool calls can connect directly instead of spawning subprocesses and parsing stdout.

For security workflow examples that combine CLI/MCP calls into complete analyst tasks, see Security Use Cases With Coding Agents.

Starting the Server

canarchy mcp serve

The server communicates over stdio using JSON-RPC 2.0 and runs until the client disconnects.

Claude Desktop Configuration

The fastest way to wire CANarchy into a client is the canarchy mcp install helper, which merges the mcpServers.canarchy block for you (see Install the CANarchy MCP server):

canarchy mcp install --client claude-desktop --dry-run   # preview
canarchy mcp install --client claude-desktop             # write (prompts; --ack to skip)
canarchy mcp install --client claude-code --ack

It is CLI-only (writing a client config is a user action, so it is not an MCP tool) and refuses to overwrite a different existing canarchy entry. To wire it up by hand instead, add the block directly:

{
  "mcpServers": {
    "canarchy": {
      "command": "canarchy",
      "args": ["mcp", "serve"]
    }
  }
}

With uv in a project environment:

{
  "mcpServers": {
    "canarchy": {
      "command": "uv",
      "args": ["run", "canarchy", "mcp", "serve"],
      "cwd": "/path/to/your/project"
    }
  }
}

Available Tools

The current MCP surface exposes a curated non-interactive subset of the CLI. Spaces in command names become underscores:

For MCP tools that accept a single CAN interface, omit the interface argument only when [transport].default_interface or CANARCHY_DEFAULT_INTERFACE is configured. Explicit MCP interface arguments take precedence over the configured default. [transport].interface is the python-can backend type (socketcan, udp_multicast, pcan, vector, kvaser, etc.) and is not the CAN channel fallback. doctor can report offline configured-backend dependency hints, but it does not open hardware.

Active-transmit MCP tools (send, generate, simulate, gateway, replay, sequence_replay, and fuzz_*) require ack_active=true. Their dry_run argument defaults to true, so agent calls plan without transmitting unless an operator explicitly authorizes live transmission with dry_run=false.

For DBC reconnaissance, dbc_inspect accepts layout=true to include cantools-rendered message bit diagrams, signal trees, and choice tables as structured strings on each message payload.

DBC decode emits a decoded_message event and individual signal events for the same source frame. Their payloads share a zero-based frame_index local to that decode invocation; use (source, frame_index, message_name, signal_name) to correlate them. Equal values at different indexes are distinct observations. Signal timestamps inherit the source-frame timestamp, including zero, and remain null when unknown; no wall-clock decode time is substituted. Select signal events directly for time-series analysis without joining parent events, and do not infer missing identities from matching values.

MCP tool CLI equivalent
capture canarchy capture
send canarchy send
generate canarchy generate
gateway canarchy gateway
replay canarchy replay
sequence_replay canarchy sequence replay
simulate canarchy simulate
filter canarchy filter
stats canarchy stats
capture_info canarchy capture-info
decode canarchy decode
encode canarchy encode
dbc_inspect canarchy dbc inspect
dbc_signals canarchy dbc signals
dbc_convert canarchy dbc convert
dbc_provider_list canarchy dbc provider list
dbc_search canarchy dbc search
dbc_fetch canarchy dbc fetch
dbc_cache_list canarchy dbc cache list
dbc_cache_prune canarchy dbc cache prune
dbc_cache_refresh canarchy dbc cache refresh
datasets_provider_list canarchy datasets provider list
datasets_search canarchy datasets search
datasets_inspect canarchy datasets inspect
datasets_fetch canarchy datasets fetch
datasets_cache_list canarchy datasets cache list
datasets_cache_refresh canarchy datasets cache refresh
datasets_replay_plan canarchy datasets replay --dry-run
export canarchy export
session_save canarchy session save
session_load canarchy session load
session_show canarchy session show
session_verify canarchy session verify
session_annotate canarchy session annotate
session_attach canarchy session attach
session_bundle canarchy session bundle
session_import canarchy session import
j1939_monitor canarchy j1939 monitor
j1939_decode canarchy j1939 decode
j1939_pgn canarchy j1939 pgn
j1939_spn canarchy j1939 spn
j1939_tp canarchy j1939 tp sessions
j1939_dm1 canarchy j1939 dm1
j1939_summary canarchy j1939 summary
j1939_inventory canarchy j1939 inventory
uds_scan canarchy uds scan
uds_trace canarchy uds trace
uds_services canarchy uds services
xcp_scan canarchy xcp scan
xcp_trace canarchy xcp trace
xcp_read canarchy xcp read
xcp_commands canarchy xcp commands
j1587_decode canarchy j1587 decode
j1587_pids canarchy j1587 pids
j2497_decode canarchy j2497 decode
j2497_mids canarchy j2497 mids
config_show canarchy config show
doctor canarchy doctor
re_anomalies canarchy re anomalies
re_correlate canarchy re correlate
re_counters canarchy re counters
re_entropy canarchy re entropy
re_match_dbc canarchy re match-dbc
re_shortlist_dbc canarchy re shortlist-dbc
j1939_tp_compare canarchy j1939 tp compare
j1939_faults canarchy j1939 faults
j1939_compare canarchy j1939 compare
j1939_map canarchy j1939 map
re_signals canarchy re signals
re_corpus canarchy re corpus
re_suggest canarchy re suggest (heuristic path only)
plot canarchy plot
cannelloni_decode canarchy cannelloni decode
datasets_convert canarchy datasets convert
datasets_replay_files canarchy datasets replay --list-files
skills_provider_list canarchy skills provider list
skills_search canarchy skills search
skills_fetch canarchy skills fetch
skills_cache_list canarchy skills cache list
skills_cache_refresh canarchy skills cache refresh
plugins_list canarchy plugins list
plugins_info canarchy plugins info

| fuzz_payload | canarchy fuzz payload | | fuzz_replay | canarchy fuzz replay | | fuzz_arbitration_id | canarchy fuzz arbitration-id | | fuzz_signal | canarchy fuzz signal | | fuzz_spn | canarchy fuzz spn | | fuzz_guided | canarchy fuzz guided | | compare | canarchy compare |

Current exclusions:

  • dataset streaming commands that emit frame records, such as datasets stream and non-dry-run datasets replay
  • interactive or service commands such as shell, tui, web serve, mcp serve, and mcp install

  • cannelloni send — active UDP egress to an arbitrary host:port; CLI-only operator action (cannelloni decode is exposed)

  • doip:// targets on uds_scan / uds_trace — DoIP routes UDS over active TCP egress to an arbitrary host; the tools stay CAN-interface-only and refuse a doip:// interface with DOIP_MCP_EXCLUDED. Run DoIP scans/traces from the CLI as an operator action
  • the dedicated doip command group (doip discovery, doip services, doip ecu-reset, doip tester-present, doip security-seed, doip dump-dids) — active network egress (UDP vehicle-identification discovery + TCP diagnostic sessions), CLI-only operator actions behind the active-transmit gate
  • completion, which emits a raw shell script rather than a JSON envelope
  • dbc generate-c, which generates C source/header files to disk and is a developer action
  • plugins enable and plugins disable, which write user plugin configuration
  • the active UDS workflows uds subservices, uds ecu-reset, uds tester-present, uds security-seed, uds dump-dids, uds read-memory, and uds auto — they transmit invasive diagnostic requests (ECU reset, seed collection, DID/memory extraction, ranged enumeration, multi-id recon) and stay CLI-only operator actions behind the active-transmit gate. The reference uds services catalog stays exposed; its active-probe mode only activates when a CLI caller supplies an interface on the command line. A configured [transport].default_interface or CANARCHY_DEFAULT_INTERFACE never promotes it to probing, so MCP uds_services is reference-only on any machine regardless of local configuration (#530)
  • the active xcp info and xcp dump workflows — they connect to an XCP slave and read its capabilities / a bounded memory range, so they stay CLI-only operator actions behind the active-transmit gate (the broadcast xcp scan stays exposed)
  • fuzz identify, a stateful multi-round human-in-the-loop replay/narrowing workflow (one bisected window replayed per invocation); CLI-only operator action

The TUI is for human inspection, not agent automation. Alt+1–Alt+5 switch the responsive Traffic, Signals, J1939, UDS, and Findings workspaces. Traffic defaults to a typed per-bus identifier summary; v switches to the chronological event log, F4 freezes inspection or returns live, and Enter drills into a selected row on narrow terminals. /filter traffic accepts the CLI filter atoms id==, pgn==, and sa== (J1939 source address, extended frames only), while plain text still performs substring filtering; invalid typed filters and unknown sort fields are reported. The sa== atom also works in non-interactive canarchy filter with unchanged output schemas. The TUI command entry uses the shared CLI executor; non-event results and errors appear in a read-only result view (F2 switches back to the workspaces, Esc closes the result). Agents should use CLI JSON or MCP tools for machine-readable results.

DBC decode results populate Traffic from decoded_message embedded frames and correlated signals. Active Traffic filters also constrain live inspector follow; F4 returns from historical inspection to the newest visible log event.

The authoritative CLI-to-MCP coverage matrix (exposed / excluded / deferred, with rationale) lives in docs/design/mcp-server.md; a test guard (test_every_cli_command_is_exposed_or_documented) fails the build if a new command drifts out of coverage.

For dataset workflows, agents should prefer MCP dataset tools when available. datasets_search and datasets_inspect include stable machine fields: ref, is_replayable, is_index, default_replay_file, download_url_available, and source_type. datasets_fetch distinguishes curated indexes from normal dataset entries with is_index, index_instructions, and download_instructions, and reports data_is_local: true when the fetch materialised real bytes into the cache rather than recording provenance for a remote file. When data_is_local is true, next_steps names the local-file commands to run against cache_path — datasets download and datasets replay do not apply and will fail with DATASET_REPLAY_UNAVAILABLE. Use datasets_replay_plan for safe replay preflight; use CLI datasets replay --list-files --json to choose a replay file and --file <id-or-name> to select it. Use max_frames or max_seconds to bound replay. For catalog:comma-car-segments, pass --platform <name> and --limit <n> when listing files so dynamic HuggingFace manifests remain bounded. Use CLI datasets stream --max-frames <n> to bound local downloaded dataset-file streaming. --chunk-size controls JSONL provenance chunk metadata only; it is not a frame limit. comma-rlog streaming requires optional openpilot LogReader support (uv pip install git+https://github.com/commaai/openpilot.git on Python 3.12.x) and returns COMMA_RLOG_SUPPORT_UNAVAILABLE when unavailable. Actual frame streaming remains CLI-only. Curated index entries that cannot be replayed return DATASET_INDEX_NOT_REPLAYABLE.

Every dataset in the catalog provider downloads from a host (zenodo.org, figshare.com, huggingface.co, ocslab.hksecurity.net) that a typical sandboxed agent environment blocks, so datasets fetch followed by a replay fails there with a transport error. When there is no egress, use the offline provider instead: datasets search --provider offline --json lists synthetic datasets that generate locally and need no network. datasets fetch offline:<name> writes real data into the cache and returns its path in cache_path, after which the local-file commands (datasets convert, datasets stream, capture-info, stats, j1939 summary, replay --file) work normally. Offline datasets carry synthetic: true in the machine fields, a licence string identifying them as synthetic, and a SYNTHETIC: description prefix. The data has real protocol structure but is not vehicle behaviour: use it to exercise tooling, never report analysis of it as a research finding. Current offline datasets are can-basic (candump, with a discoverable counter), j1939-basic (candump, with BAM/TP and a DM1 active fault), can-intrusion (hcrl-csv, attack-labelled), and signal-decoded (decoded-signal-csv).

A bare dataset ref (no provider: prefix) resolves against providers in order, and datasets_provider_list / datasets provider list --json reports that effective order as search_order plus a per-provider order index. The default is catalog before offline, so a bare ref prefers a real dataset over synthetic data of the same name; an operator can reorder it with [datasets].search_order in ~/.canarchy/config.toml. Prefer a prefixed ref (catalog:road, offline:can-basic) when the provider matters. A provider name in that config that does not exist fails every datasets command with DATASET_PROVIDER_NOT_FOUND (exit 1); a malformed value fails with DATASET_SEARCH_ORDER_INVALID.

Skills Workflow

CANarchy skills are phase-1 workflow descriptors, not MCP tools. Agents should discover and fetch skills through the CLI provider workflow, inspect the cached manifest and entry file, then run the referenced CANarchy commands explicitly through either the CLI or the MCP tools that already exist.

Recommended flow:

  1. Run canarchy skills search <domain-or-task> --json.
  2. Select a provider-qualified reference such as github:j1939_compare_triage.
  3. Run canarchy skills fetch <provider>:<skill> --json.
  4. Read local_manifest_path and local_entry_path from the fetch result.
  5. Check compatibility, required_tools, inputs, outputs, skill.tags, and skill.domains before applying the workflow.
  6. Run required CANarchy commands explicitly and record the selected skill reference plus provenance in the final analysis.

Example:

canarchy skills search j1939 --provider github --json
canarchy skills fetch github:j1939_compare_triage --json
canarchy j1939 summary --file baseline.candump --json
canarchy j1939 compare --file baseline.candump --file after-start.candump --json

If MCP is available, agents may use MCP for commands that are already exposed as tools, such as j1939_summary. The skill itself is still selected and fetched through the CLI in phase 1, and compatibility.mcp=false means the agent should not assume MCP invocation is supported for the skill workflow.

Research Record Workflow

A session is the durable record of an analysis: what it consumed, what it produced, and whether that still holds. Agents that report a finding should record it so a human can check the finding later against the exact bytes it came from.

Recommended flow:

  1. session_save with the capture and DBC under analysis. The record stores a SHA-256 hash, size, and modification time per input, the effective configuration, and the CANarchy version.
  2. Run the analysis tools as usual, writing the result to a file.
  3. session_attach that file with command set to the exact command that produced it and derived_from set to the input ids from step 1, so the artifact points at the inputs it came from. Pass embed: true to keep the result inside the record.
  4. session_annotate with the interpretation, targeting the input or artifact it refers to.
  5. session_verify before relying on an earlier record. It re-hashes everything offline and returns verification.status, per-entry unchanged / changed / missing / unverifiable, a reproduce_command per recorded invocation, and required_actions. A degraded verification returns ok: false with code SESSION_VERIFICATION_FAILED — the recorded conclusion no longer follows from the files on disk, so re-run the analysis rather than repeating the old finding.
  6. session_bundle to hand the whole record — manifest plus copies of its files — to another machine, and session_import to open it there.

session_verify and session_load only read files: they never re-run a recorded command or touch the bus. A record saved before provenance existed reports provenance_available: false with a SESSION_PROVENANCE_UNAVAILABLE warning; re-run session_save with its inputs to upgrade it. Credentials are never recorded — omitted configuration keys are listed in effective_config.redacted_keys.

Response Format

Every tool returns a single JSON text block with the canonical result envelope:

{
  "ok": true,
  "command": "<command>",
  "data": { "events": [...] },
  "warnings": [],
  "errors": []
}

Failures set "ok": false and populate errors with structured objects (code, message, hint), using the same error codes as the CLI JSON output.

For UDS workflows, uds_transaction events may include payload.complete=false when a multi-frame ISO-TP response was only partially captured or arrived out of order. In that case payload.response_data still contains the partial bytes that were reassembled.

When the optional Scapy extra is installed, UDS results may also report data.protocol_decoder="scapy" and include summary-level request_summary / response_summary enrichment on uds_transaction payloads while preserving the same result envelope and event type.

Example Interactions

tool: uds_services {}
→ {"ok": true, "command": "uds services", "data": {"service_count": 26, ...}}

tool: j1939_spn {"spn": 190, "file": "trace.candump"}
→ {"ok": true, "command": "j1939 spn", "data": {"mode": "passive", "observations": [...]}}

tool: j1939_spn {"spn": 190}   # no file → built-in reference lookup
→ {"ok": true, "command": "j1939 spn", "data": {"mode": "reference", "name": "Engine Speed", "pgn": 61444, "units": "rpm", ...}}

tool: send {"interface": "vcan0", "frame_id": "0x7DF", "data": "0201F1", "ack_active": true}
→ {"ok": true, "command": "send", "data": {"frame": {...}, "mode": "dry_run"}}

Notes

  • Numeric CLI options must be finite: NaN and positive/negative infinity fail with a structured usage error before file processing or transport access. This includes --seconds, rates, gaps, durations, timeouts, and analytical thresholds. File --seconds 0 keeps its existing initial-timestamp window semantics; other documented zero/range rules are unchanged. Invalid floats are described in error text, never echoed as non-standard JSON numbers. DoIP ?timeout= also requires a finite positive value.
  • MCP tool results set isError=true whenever the canonical JSON envelope has ok=false, including command failures, excluded targets, missing active-transmit acknowledgement, and isolated unexpected exceptions. Codes, messages, and hints remain in the JSON text. Successful calls set isError=false. SDK input-schema validation runs before the command handler: missing required fields or invalid schema types return isError=true with SDK validation text rather than a CANarchy JSON envelope. Correct the input and retry; ordinary errors leave the session available for later calls.
  • MCP streaming is not supported in v1 — live-capture tools (capture, live gateway with dry_run=false) return a buffered batch from the active backend. Use CLI datasets stream --max-frames <n> for bounded local dataset-file JSONL or candump pipelines, and datasets replay --max-frames <n> or --max-seconds <s> for bounded remote dataset-ref or URL playback to stdout.
  • shell, tui, mcp serve, and mcp install are not exposed as MCP tools.
  • Error codes are identical to the CLI, so existing JSON-parsing logic transfers without changes.
  • Every canonical CANarchy JSON tool envelope is bounded (default 512 kB of final JSON text including metadata, configurable via CANARCHY_MCP_MAX_RESPONSE_BYTES, minimum 1024 bytes; smaller positive settings fail before server startup). SDK-generated schema-validation and protocol errors are outside this cap. Oversized list data is trimmed and marked with data.truncated: true plus a data.truncation block recording each surviving trimmed list's total_items vs returned_items — check that marker before treating a short list as a complete result, and re-run via the CLI (or bound the input with max_frames/seconds) when you need the full output. An unexpected in-tool failure returns a TOOL_EXECUTION_ERROR envelope; the session and the other tools stay usable.
  • encode (and send --dbc) resolve message names by exact DBC name, case/spacing-insensitive match, or SAE PGN label (EEC1), and signal names by exact name, case/spacing-insensitive match, or SAE SPN name (Engine Speed) — so names displayed by decode tools re-encode directly. Unsupplied signals are defaulted and reported under data.resolution.filled_signals with a warning; review them before transmitting.
  • CLI capture paths: the re * family and j1939 compare accept both positional paths and --file <path> flags, so agents shelling out do not need to remember which convention a command uses. Filter expressions accept decimal, 0x-prefixed hex, or bare hex IDs/PGNs with whitespace tolerated around operators.
  • RE tool results (re_signals, re_counters, re_entropy, re_anomalies, re_corpus) annotate J1939 frames with pgn, pgn_label, source_address, and source_address_name, and label or exclude J1939 transport-protocol framing (j1939_transport, excluded_transport_ids). re_anomalies without a baseline reports sparse ids under low_rate_ids instead of ranking them and caps z-scores at ±100σ — prefer supplying a known-good baseline capture.
  • Reference-DBC matching in re suggest uses exact physical-bit overlap for Intel and Motorola signals; sharing a byte alone is insufficient. Confidence remains heuristic, and corrected overlap can change suggestion inclusion and ranking without changing output fields.
  • re_suggest proposes signal names for ranked candidates using offline heuristics only (reference-DBC overlap, the J1939 SPN/PGN catalog, and behaviour templates); each suggestion carries a source and confidence. The optional external-LLM enrichment (re suggest --llm <provider>) is CLI-only — it is not reachable through the MCP tool — because it sends candidate metadata to an external service and requires explicit operator confirmation. Even on the CLI it sends only candidate metadata (ids, bit ranges, observed ranges, heuristic names), never raw payload bytes, and records an external_enrichment note plus an EXTERNAL_SERVICE_CALLED warning.
  • Stdin pipelines: capture-info, stats, and filter --file - read candump text from stdin. filter --stdin, decode --stdin, and j1939 decode --stdin read JSONL FrameEvents from stdin regardless of output format. This enables piping datasets replay candump output directly into analysis commands without temporary files.
  • Stdin pipelines are CLI-only. Over MCP, stdin carries the JSON-RPC transport, so passing - to any file or source parameter (file, files, dbc, capture, baseline, reference, reference_dbc, source, artifact, artifacts, bundle, corpus) is refused before the command runs, with error code STDIN_MCP_EXCLUDED and the MCP failure flag set. Pass a real file path instead: if the data is coming from a pipeline, write it to a file first (for example canarchy datasets replay offline:can-basic > /tmp/run.candump) and call the tool with that path. The parameter descriptions returned by list_tools state the restriction.