Command: doctor

command
cli
agents
Author
Affiliation

Paolo Bosetti

University of Trento

Published

July 7, 2026

Modified

July 29, 2026

Abstract

mads doctor (mads-doctor) is a tool designed to help users spot possible issues and solutions they may encounter when setting up a network of MADS agents.

Warning

This command has been introduced in MADS v2.4.1.

Description

mads-doctor is MADS’s answer to ros2 doctor/brew doctor: a single command that checks the things that most commonly go wrong in the first hour of using a pub-sub framework — a wrong broker URI, a stale settings file, a plugin protocol mismatch, a port already in use — and reports each one as [PASS], [WARN], or [FAIL] with a one-line fix hint. It never starts a broker or an agent, never calls a plugin’s process()/get_output()/load_data(), and never writes anything except the one narrow case --fix and --graph cover.

By default it checks, in order:

  1. Settings file found and parses. The path given by --settings (default mads.ini in the current directory — unlike every other mads-* executable, whose -s/--settings defaults to a broker URI, since checking a local file is the point of this check) exists and is valid TOML. A tcp://... value is treated as a remote broker URI instead of a local file, and this check is reported [PASS] with a note pointing at the broker-reachable check below.
  2. Broker reachable. Probes the broker’s settings endpoint (from --broker, else the [broker] section’s settings_address, else tcp://localhost:9092) the same way mads up’s ready = "broker" does.
  3. Declared plugin(s) resolve and load. Every attachment key found in the settings file (or every --plugin path given explicitly, which then takes priority) is dry-run loaded through the same pugg::Kernel path mads-source/mads-filter/mads-sink use — loaded, its kind() and protocol read, then unloaded. process()/get_output()/load_data() are never called.
  4. Plugin protocol matches the pinned mads_plugin version. Compares each loaded plugin’s protocol against share/plugin_deps.json’s plugin_protocol (the version mads plugin --update migrates plugins to).
  5. CURVE key files, if --crypto** is given, exist and are well-formed.** Checks <key_client>.key/.pub and <key_broker>.pub under --keys_dir — the same three files Mads::CurveAuth::setup_curve_client() reads — exist and decode as valid Z85 CURVE keys.
  6. Local port-availability sanity check. Probes the settings file’s [broker] frontend_address/backend_address/settings_address ports on 127.0.0.1; a port already answering is reported as a likely broker-already-running collision.

mads-doctor exits 0 only if every check reported [PASS] or [WARN]; any [FAIL] makes it exit 1.

Note

Carefully read the messages: your setup might be perfectly OK even if the tool reports some failure. For example, you might have installed a non-compatible plugin, which is never loaded by your setup, thus you can safely neglect the corresponding [FAIL] or [WARN] message.

--plan

--plan director.toml is a standalone mode: it parses, validates and expands the given director.toml through the exact same src/director_config.hpp module mads up --dry-run uses (no process spawning), and prints the same kind of expanded-plan report — start order, templated command, scale expansion, after dependencies, ready probes. It ignores every other flag except --settings-independent behaviour: the settings file, broker, plugins, and CURVE keys are not touched. Use it to sanity-check a whole deployment before running mads up for real.

--graph

--graph[=file.dot] is another standalone, read-only mode: it parses the settings file (the same --settings path used by check 1, reusing the same TOML-loading logic) and emits a Graphviz DOT description of the pub/sub topology it declares, to file.dot if given, or to standard output otherwise. It never probes the broker, plugins, ports, or CURVE keys, and never shells out to a dot binary — rendering the DOT text to an image is left to the user (e.g. mads doctor --graph | dot -Tpng -o topology.png).

Every non-[agents]/non-[broker] section becomes one record-shaped node, named after the section, with its sub_topic entries listed underneath (one per line; sub_topic = [""] — subscribe-all —- renders as a single (all) line; no subscriptions renders no compartment). One edge is drawn for every section A’s pub_topic and every other section B’s sub_topic pattern that matches it (per Mads::topic_match(), the same MQTT-style wildcard matcher Agent::connect_sub() uses at runtime), labeled with A’s pub_topic. Nodes are colored by inferred role: a source (pub_topic only) is darkred, a filter (both) is darkgreen, a sink (sub_topic only) is darkblue; an agent with neither gets no color attribute. A node gets a dashed contour if it has a dangling topic — a pub_topic nothing subscribes to, or a sub_topic pattern nothing ever publishes to — which is usually a misconfiguration worth a second look. An agent’s own pub_topic matching its own sub_topic (a self-loop) satisfies both sides and is not flagged as dangling, matching real broker behavior.

For example, the following mads.ini file:

[agents]
frontend_address = "tcp://localhost:9090"
backend_address = "tcp://localhost:9091"
settings_address = "tcp://localhost:9092"
timecode_fps = 25

[broker]
frontend_address = "tcp://*:9090"
backend_address = "tcp://*:9091"
settings_address = "tcp://*:9092"

[logger]
mongo_uri = "mongodb://localhost:27017"
mongo_db = "blescan_roadside2"
sub_topic = ["ble_scan", "ble_filter"]

[ble_scan]
pub_topic = "ble_scan"

[ble_filter]
sub_topic = ["ble_scan"]
pub_topic = "ble_filter"

[feedback]
# Subscribes to ALL topics
sub_topic = ["ble_filter"]

[rerunner]
sub_topic = ["ble_filter", "arduino"]
keypaths = [.. list of keypaths ...]
recording_id = "rec_ble"

[arduino]
pub_topic = "arduino"
port = "/dev/cu.usbmodem34B7DA5F9A5C2"
baudrate = 115200

produces:

mads arduino arduino rerunner rerunner ble_filter arduino arduino->rerunner arduino ble_filter ble_filter ble_scan feedback feedback ble_filter ble_filter->feedback ble_filter logger logger ble_scan ble_filter ble_filter->logger ble_filter ble_filter->rerunner ble_filter ble_scan ble_scan ble_scan->ble_filter ble_scan ble_scan->logger ble_scan

Important

Note that pub_topic and sub_topic are optional in the INI file: pub_topic defaults to the agent name, and sub_topic defaults to an empty list.

To have the graphviz correctly drawn, though, you have to explicitly set the pub_topic field, because this is the only way the tool has to distinguish source and filter agents to pure sink agents.

In other words, a source agent with no explicit pub_topic will be rendered to Graphviz as a dangling agent, even though it will correctly run and publish on its name’s topic.

--fix

--fix attempts the one check with an unambiguous, non-destructive auto-fix: if the settings file is missing, it is scaffolded from the same template mads ini renders. --fix only ever creates a missing file — it never deletes or overwrites an existing one, however broken.

Options

-s, --settings path
Path to the settings file to check. Default mads.ini in the current directory. A tcp://... value is treated as a remote broker URI instead.
--broker uri
Broker settings endpoint to probe for check 2. Default: derived from the settings file’s [broker] section, else tcp://localhost:9092.
--timeout ms
Timeout in milliseconds for the broker-reachable and port-availability probes. Default 1000.
--plugin path
Dry-run load this plugin file (repeatable). When given, replaces the automatic scan of the settings file’s attachment keys. Resolved like mads-source/-filter/-sink’s own plugin argument: relative to the current directory first, falling back to the installed-plugin location.
--crypto
Also run the CURVE key-file check (check 5). Off by default, since CURVE is opt-in.
--keys_dir dir
Directory to look for CURVE key files in. Default: <exec_dir>/../etc, same as every other mads-* executable’s --keys_dir.
--key_broker name
Base name (without .pub) of the broker/server CURVE public key file. Default broker.
--key_client name
Base name (without .key/.pub) of the client CURVE key files. Default client.
--plan director.toml
Validate a director.toml deployment plan (like mads up --dry-run) and exit; see above.
--graph[=file.dot]
Emit a Graphviz DOT topology graph of the settings file’s declared pub/sub topics to file.dot, or standard output if omitted, and exit; see above.
--fix
Attempt safe, non-destructive auto-fixes; see above.
-v, --version
Show version information.
-h, --help
Show summary of options.

Exit status

0 if every check reported [PASS] or [WARN] (or, in --plan mode, if the plan loaded and expanded cleanly). 1 if any check reported [FAIL], or --plan’s file failed to load/validate.

Examples

Check the settings file in the current directory, plus broker reachability and port availability:

mads doctor

Check a specific settings file and CURVE keys, scaffolding the file if it is missing:

mads doctor -s /etc/mads/mads.ini --crypto --fix

Sanity-check a whole deployment plan before running it for real:

mads doctor --plan deploy/director.toml

Print the declared pub/sub topology as DOT text, and render it to an image with Graphviz:

mads doctor --graph | dot -Tpng -o topology.png

Write the topology graph straight to a file instead:

mads doctor --graph=topology.dot
Back to top