flowchart TB
subgraph "Broker process"
FE["Frontend
(XSUB, bind tcp + ipc)"]
BE["Backend
(XPUB, bind tcp + ipc)"]
PX["proxy_steerable
forwards frames verbatim"]
ST["Settings
(ROUTER, bind tcp + ipc)"]
W["File watcher
re-reads mads.ini every 1s"]
D["Discovery thread
UDP broadcast"]
end
Pub["Publishing agents"] -- connect --> FE
FE --> PX --> BE
BE -- connect --> Sub["Subscribing agents"]
Any["Any agent"] -- "settings / timecode request" --> ST
W -. "updates served ini" .-> ST
D -. "advertises host + ports" .-> Net(("local network
UDP"))
Command: broker
mads broker (mads-broker) is the hub of a MADS network: a payload-opaque ZeroMQ XSUB/XPUB proxy that relays messages between agents, plus a settings service that hands out the shared mads.ini configuration (and, optionally, a plugin attachment) to any agent that asks. There is exactly one broker per network. This page covers its sockets, its interactive controls, live settings reload, and CURVE/auto-discovery integration.
Related guides:
- Network structure — the broker’s role in a MADS network
- Service advertising and auto-discovery
- Security in MADS (for
--crypto) - Local transport and shared memory (ipc and zero-copy blobs)
- Services (for running the broker with
-d/--daemonas a systemd service)
Synopsis
mads broker [-s|--settings path] [-r|--room[=name]] [-d|--daemon]
[--crypto[=keyname]] [--keys_dir[=path]] [-v|--version] [-h|--help]Description
The broker does three things, on three separate sockets, all bound (not connected) at startup:
The broker is payload-opaque: it forwards frames verbatim between frontend and backend and never inspects, parses, or rewrites message contents. This is what lets the wire payload format (JSON, MsgPack, compression) evolve without any broker change.
Options
| Flag | Effect |
|---|---|
-s, --settings path |
Settings file to read. See Settings file resolution below. |
-r, --room[=name] |
Service-discovery room name (default mads). See Auto-discovery. |
-d, --daemon |
Run non-interactively: no keyboard controls, real SIGINT/SIGTERM handling for clean shutdown. Intended for services. |
--crypto[=keyname] |
Enable CURVE encryption on all three sockets (default key name broker). See Security. |
--keys_dir[=path] |
Directory to search for CURVE key files (default: etc under the MADS installation prefix). |
-v, --version |
Print version. |
-h, --help |
Print usage. |
Settings file resolution
With no -s, the broker looks for mads.ini in the current directory, falling back to <prefix>/etc/mads.ini if not found there. Whichever file is used, the broker reads its own bind addresses from a section named after its own executable’s name, stripped of everything up to the last -: running the binary mads-broker (however it was invoked) looks up [broker].
Because the section name comes from the executable’s filename, you can run two independent brokers from a single mads.ini by installing (or symlinking) the broker binary under two different names, e.g. mads-broker and mads-broker2, each picking up its own [broker] / [broker2] section — handy for a quick multi-network test on one machine without juggling two ini files. See also federate, for actually bridging two independent networks.
Interactive controls
Unless -d/--daemon is given, the broker runs interactively and reads single keystrokes:
| Key | Action |
|---|---|
I |
Print live statistics (messages/bytes in and out, frontend and backend). |
N |
Cycle through this host’s settings URIs, one per network interface, plus the local ipc:// settings endpoint when one is bound — use this to find the address remote agents should connect to. |
P |
Pause forwarding. |
R |
Resume forwarding. |
Q |
Clean quit: stop advertising, close sockets, exit. |
X (not on Windows) |
Clean quit and relaunch the broker process — the only way to make the broker pick up changes to its own frontend/backend/settings addresses (see below). |
| Ctrl-C | Immediate exit, no cleanup. |
The underlying ZeroMQ steerable-proxy library has a known quirk where the PAUSE/RESUME control commands are inverted (this is called out directly in the broker’s own source comments). If pausing with P does not seem to stop traffic, try R instead.
Prefer Q over Ctrl-C when running interactively: Q stops the discovery advertisement and closes sockets cleanly, Ctrl-C does not (there is no interactive-mode signal handler).
Live settings reload
The broker watches its settings file for changes (polled every second) and, on a valid change, updates the ini text it hands out to agents that request settings from that point on.
This reload only affects what agents receive, not the broker’s own bound addresses: frontend_address, backend_address, and settings_address are read once at startup. Changing them in the ini requires a full broker restart — press X (Unix) or stop and relaunch the process. Agents that are already running keep their old settings until they themselves restart.
Local transport (ipc)
Agents running on the same machine as the broker do not need the TCP stack at all. The broker binds each of its three sockets twice: on its tcp:// address and on an ipc:// endpoint, a Unix-domain socket file. Agents on the broker’s host pick the ipc endpoints automatically (see Choosing the transport); remote agents keep using tcp. Nothing changes on the wire: the same frames travel over a cheaper channel.
These keys live in the broker’s own section, next to the addresses:
[broker]
frontend_address = "tcp://*:9090"
backend_address = "tcp://*:9091"
settings_address = "tcp://*:9092"
# ipc = true
# ipc_dir = "/tmp/mads-1000"
# frontend_ipc = "ipc:///tmp/mads-1000/front.ipc"
# backend_ipc = "ipc:///tmp/mads-1000/back.ipc"
# settings_ipc = "ipc:///tmp/mads-1000/settings.ipc"| Key | Default | Meaning |
|---|---|---|
ipc |
true |
Also bind the ipc endpoints. Ignored, with a warning, if libzmq has no ipc transport. |
ipc_dir |
runtime directory | Where the socket files go. |
frontend_ipc, backend_ipc, settings_ipc |
derived | Explicit ipc:// endpoint for one socket. Needed only when that socket’s tcp address has no numeric port. |
ipc_sndbuf, ipc_rcvbuf |
1048576 (1 MiB) | Kernel buffer size of the ipc connections only, in bytes; 0 keeps the OS default. Read from [broker], then [agents]; agents use the same keys (their own section first, then [agents]). |
The OS gives Unix-domain sockets small buffers by default: 8 KiB on macOS. A 1 MiB message then needs over a hundred refills per hop, which caps ipc at about 1.4 GB/s per hop and makes it slower than loopback tcp for messages of a few hundred KiB and up. With 1 MiB buffers the same hop moves about 30 GB/s, and ipc beats tcp at every message size. The buffer size applies to the ipc endpoints only: the tcp endpoints of the same broker sockets keep the plain sndbuf/rcvbuf settings, or the OS default and its autotuning. MADS builds libzmq with a small patch for this, since stock libzmq 4.3.5 applies socket buffer sizes to tcp only.
On Linux the kernel silently caps every buffer request at net.core.wmem_max (send) and net.core.rmem_max (receive). On a stock install both are about 208 KiB, which is also the default size of a Unix-domain socket, so the 1 MiB ipc_sndbuf/ipc_rcvbuf default has no effect until you raise them:
sudo sysctl -w net.core.wmem_max=4194304 net.core.rmem_max=4194304
# make it permanent:
printf 'net.core.wmem_max = 4194304\nnet.core.rmem_max = 4194304\n' |
sudo tee /etc/sysctl.d/90-mads.confLinux’s AF_UNIX default is already much larger than macOS’s 8 KiB, so the small-buffer bottleneck is milder there; raising the limits matters for large messages. macOS caps at kern.ipc.maxsockbuf, 8 MiB by default, so the 1 MiB default applies as is.
Socket files
By default the socket files live in the per-user runtime directory:
- Linux:
$XDG_RUNTIME_DIR/mads, falling back to/tmp/mads-<uid>; - macOS:
/tmp/mads-<uid>($TMPDIRis too long for a socket path); - Windows:
%LOCALAPPDATA%\mads\run.
The directory is created with owner-only permissions (0700), and the broker refuses one owned by another user. Each file is named after the socket’s tcp port and role, for example broker-9090-front.ipc, broker-9091-back.ipc and broker-9092-settings.ipc, so several brokers on one host never collide. A socket path must fit in 100 bytes, so keep a custom ipc_dir short.
Lifecycle
- Startup. The broker probes each path before binding. A stale file left by a crashed broker is replaced. A socket that another live process still accepts connections on, or a path that is not a socket, is left alone: that endpoint is skipped with a warning. The ipc endpoints appear in the startup banner next to the tcp ones.
- Failure. An ipc problem never affects tcp service. An endpoint that cannot be bound is simply not offered to agents.
- Shutdown. On a clean exit (
Q,X, or a signal in--daemonmode) the broker removes its socket files, but only if they are still the ones it created. Ctrl-C in interactive mode leaves them behind; the next broker replaces them.
The transport command
Agents ask the settings socket what the broker offers with a transport request, sent right after the settings and timecode requests. The broker replies with one JSON frame:
{
"host_token": "lab-pc/4c4c4544-0042-3510-8048-b4c04f4b4d32",
"ipc": {
"frontend": "ipc:///tmp/mads-1000/broker-9090-front.ipc",
"backend": "ipc:///tmp/mads-1000/broker-9091-back.ipc",
"settings": "ipc:///tmp/mads-1000/broker-9092-settings.ipc"
},
"curve": false
}host_token identifies the broker’s machine (hostname plus machine id). An agent compares it with its own to decide whether it shares the broker’s host. An empty ipc string means that role is not offered. A broker that predates the command answers with its bare version string, which agents read as “tcp only”, so mixed-version fleets keep working.
ip_whitelist does not apply to ipc peers, which have no IP address. Their access control is the owner-only runtime directory: only processes of the same user can reach the sockets. CURVE, when enabled with --crypto, applies to the ipc endpoints exactly as to the tcp ones.
ipc on Windows uses AF_UNIX sockets, which need Windows 10 or later and a libzmq built against the Windows 10 SDK (afunix.h). With a libzmq lacking ipc, the broker warns and serves tcp only, and agents silently stay on tcp.
mads doctor shows the advertised endpoints, whether their socket files exist on this host, and which transport a local agent would pick.
Security
With --crypto[=keyname], CURVE authentication is set up on all three sockets (frontend, backend, and settings) — not just the pub/sub pair. Two additional settings, read from the broker’s own ini section, only take effect when --crypto is active:
ip_whitelist: an array of allowed client IPs (default: empty, meaning all IPs are allowed). It only filters tcp peers; see Local transport.auth_verbose: enable verbose authentication logging.
See Security in MADS for generating keypairs (mads --keypair, mads) and the full authentication setup.
Auto-discovery
The broker always attempts to advertise itself via UDP broadcast on port 39092, in room mads by default (-r/--room only changes the room name, it does not toggle advertising on or off).
There is currently no flag to disable discovery advertising entirely. If you need the broker to stay silent on the local network, block UDP port 39092 at the firewall/network level.
If advertising fails at startup (e.g. no network interface is up yet), the broker retries every 5 seconds in --daemon mode, or warns once and gives up when running interactively. See Auto-discovery for how agents consume this with --room.
See also
mads · federate · Network discovery · Timing and scheduling · doctor · Local transport and shared memory