Command: doctor

command
cli
agents
Author
Affiliation

Paolo Bosetti

University of Trento

Published

July 7, 2026

Modified

October 30, 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.
  7. Local transport (ipc). Reports whether this libzmq supports ipc://, and whether the per-user runtime directory that holds the socket files exists, belongs to you and is owner-only (its permissions are the only access control on ipc endpoints). When the broker is reachable, doctor also sends it the transport command, lists the ipc endpoints it advertises and whether their socket files exist on this host, and predicts the transport an agent started here would pick, using the agent’s own rules and the settings file’s [agents] transport value. See Local transport.
  8. Shared-memory registry (Linux and macOS). Lists <runtime dir>/shm, where every producer running with shm = true registers its segments with its pid, and counts the orphans: segments whose owner is no longer running, typically after a crash. They hold memory until reboot; --fix removes them. Windows frees a segment with its last handle and keeps no registry.
  9. Open-file limit. Reports this process’s descriptor limit and how many agents a broker started the same way could serve.

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 checks 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;
  • if the shared-memory registry lists orphaned segments, they are unlinked and their registry entries removed, exactly as a new producer does at startup. Segments whose owner is still running are never touched.

Here is a sample of the new local-transport and shared-memory checks, against a broker on ports 19290–19292 and with one orphaned segment left by a crashed producer:

  [PASS] This libzmq supports the ipc:// transport.
  [PASS] Runtime directory /tmp/mads-501 is owner-only.
  [PASS] The broker advertises ipc endpoints:
         frontend: ipc:///tmp/mads-501/broker-19290-front.ipc
         backend: ipc:///tmp/mads-501/broker-19291-back.ipc
         settings: ipc:///tmp/mads-501/broker-19292-settings.ipc
  [PASS] Agents on this host would use ipc (transport = "auto").
  [WARN] Shared-memory registry /tmp/mads-501/shm: 0 live segment(s), 1 orphan(s) (owner dead).
         -> Run `mads doctor --fix` to unlink orphaned segments (a producer crashed without
            cleaning up; they hold memory until reboot).

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 (scaffold a missing settings file, unlink orphaned shared-memory segments); 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

Check which transport local agents would use against a broker on non-default ports, and clean up the shared-memory segments of crashed producers:

mads doctor --broker tcp://localhost:19292 --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