Command: broker

command
core
cli
networking
Author
Affiliation

Paolo Bosetti

University of Trento

Published

July 7, 2026

Modified

October 30, 2026

Abstract

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:

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:

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"))

Important

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].

Tip

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.
Warning

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.

Tip

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.

Important

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]).
NoteWhy ipc needs bigger buffers

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.

WarningLinux caps socket buffers

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.conf

Linux’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> ($TMPDIR is 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 --daemon mode) 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.

Noteipc and security

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.

TipWindows

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).

Note

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

Back to top