Command: doctor
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.
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:
- Settings file found and parses. The path given by --settings (default
mads.iniin the current directory — unlike every other mads-* executable, whose-s/--settingsdefaults to a broker URI, since checking a local file is the point of this check) exists and is valid TOML. Atcp://...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. - Broker reachable. Probes the broker’s settings endpoint (from --broker, else the
[broker]section’ssettings_address, elsetcp://localhost:9092) the same waymads up’sready = "broker"does. - Declared plugin(s) resolve and load. Every
attachmentkey found in the settings file (or every --plugin path given explicitly, which then takes priority) is dry-run loaded through the samepugg::Kernelpath mads-source/mads-filter/mads-sink use — loaded, itskind()and protocol read, then unloaded.process()/get_output()/load_data()are never called. - Plugin protocol matches the pinned
mads_pluginversion. Compares each loaded plugin’s protocol againstshare/plugin_deps.json’splugin_protocol(the versionmads plugin --updatemigrates plugins to). - CURVE key files, if --crypto** is given, exist and are well-formed.** Checks
<key_client>.key/.puband<key_broker>.pubunder --keys_dir — the same three filesMads::CurveAuth::setup_curve_client()reads — exist and decode as valid Z85 CURVE keys. - Local port-availability sanity check. Probes the settings file’s
[broker]frontend_address/backend_address/settings_addressports on127.0.0.1; a port already answering is reported as a likely broker-already-running collision. - 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 thetransportcommand, 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] transportvalue. See Local transport. - Shared-memory registry (Linux and macOS). Lists
<runtime dir>/shm, where every producer running withshm = trueregisters 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. - 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.
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 = 115200produces:
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 inirenders. --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.iniin the current directory. Atcp://...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, elsetcp://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
attachmentkeys. Resolved like mads-source/-filter/-sink’s ownpluginargument: 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. Defaultbroker. - --key_client name
-
Base name (without
.key/.pub) of the client CURVE key files. Defaultclient. - --plan director.toml
-
Validate a
director.tomldeployment plan (likemads 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 doctorCheck a specific settings file and CURVE keys, scaffolding the file if it is missing:
mads doctor -s /etc/mads/mads.ini --crypto --fixCheck 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 --fixSanity-check a whole deployment plan before running it for real:
mads doctor --plan deploy/director.tomlPrint the declared pub/sub topology as DOT text, and render it to an image with Graphviz:
mads doctor --graph | dot -Tpng -o topology.pngWrite the topology graph straight to a file instead:
mads doctor --graph=topology.dot