Skip to main content

Check the broker without opening MQTT

Orchestrators and monitoring systems need a plain HTTP answer: is the process up, is it accepting clients, and what is it doing. The Coreflux MQTT Broker serves that on a dedicated ops port (default 9100), separate from MQTT and from the WebSocket data plane.
Like a side door for the building inspector. Devices still use the front door (MQTT). Kubernetes and Prometheus use the side door, a small HTTP port that only answers health and status questions.
Use these endpoints when you need to:
  • tell a container orchestrator when to restart the process, and when to send it traffic;
  • scrape broker gauges and counters into Prometheus;
  • read version, ports, and license status from the broker host without an MQTT login.
The listener is unauthenticated. Keep port 9100 on a private network. See Security.
These paths answer only on the ops port. The same path on the WebSocket / HTTP port (default 5000) returns 404.

Endpoints

All JSON responses use camelCase property names.

Liveness

GET /health (alias /healthz) means the process is up and answering HTTP. It does not mean MQTT is accepting clients. Use /ready for that.

Readiness

GET /ready (alias /readyz) returns 200 only when both are true:
  1. The broker application has finished starting.
  2. The MQTT server is accepting connections.
Until then the status is 503. Ready:
Not ready:
Point a Kubernetes liveness probe at /health and a readiness probe at /ready. A liveness probe on /ready restarts the broker during a normal startup.

Runtime info

GET /info is a single JSON document: name, version, uptime, operating system, Python sandbox, cluster and MCP summaries, telemetry status, listener ports, and the CPU, memory, and GC view the process actually has. It does not include passwords, tokens, license files, or the cluster shared secret. A standalone broker (clustering and MCP off, census not yet pinged) has this shape. runtimeResources below is trimmed to the CPU and GC fields operators compare first; the live document also includes process memory, the thread pool, container signals, and cgroup data.
name is the broker configuration name (Coreflux MQTT Broker if that name is unset). ports reflects the listeners in effect, including ops. uptimeSeconds is 0 when the process start time cannot be read.

Python sandbox

Cluster and MCP summaries

cluster is { "enabled": false } when high-availability clustering is off. When it is on, the summary is enabled, clusterId, node, role (lowercase), isLeader, leader, and members (the member count). While a configured cluster fails to start, it is enabled: false with configured: true and a startFailure block. Full status is GET /cluster. mcp is { "enabled": false } when the MCP Server is off. When it is on, the summary is: That object is not the MCP listener. MCP /health and MCP /info stay on the MCP port (default 6278).

Telemetry summary

telemetry is the anonymous install-census status. It does not include an install id, hostname, or license. Set COREFLUX_TELEMETRY to off, false, or 0 to turn the census off. COREFLUX_TELEMETRY affects only the census. DO_NOT_TRACK=1 turns off the census and also turns off fleet performance metrics. Telemetry.Enabled: false in the broker configuration turns the census off too. The environment is checked first.

Runtime resources

runtimeResources is the live view of CPU, memory, garbage collection, the thread pool, and (on Linux) cgroup limits. The fields operators compare most often: ServerGC and isServerGc disagree when the runtime silently drops to workstation GC. That happens when only one processor is visible: a one-CPU container, a cgroup quota of one core, or a Kubernetes limits.cpu of 1 or less (500m is one processor). Give the broker at least two CPUs to keep Server GC, and confirm with processorCount and cgroup. The same object also includes process memory (process), thread-pool counts (threadPool), container signals (container), and dotNetEnv — only DOTNET_GC*, DOTNET_ThreadPool*, COMPlus_*, DOTNET_EnableWriteXorExecute, and DOTNET_SYSTEM_GLOBALIZATION_INVARIANT. Broker passwords and tokens are not in this document.

License status

GET /license returns the license status: tier, limits, usage, fingerprint, activation, and enrollment. A broker with no license still answers, with tier Community. HTTP property names are camelCase. The retained MQTT topic $SYS/Coreflux/License carries the same status with PascalCase names (Tier, EffectiveLimits), except isTrial, which is lowercase on both. Field meanings are on Licensing and Enrollment.
effectiveLimits also includes maxModels, maxRules, maxActions, maxVisus, and maxCertInTls. currentUsage also includes actions (regular actions, route events, Python scripts), models, rules, and panels. Connector counts still use the name routes. If the status cannot be read, the response is 500:
GET /license is unauthenticated. It exposes the hardware fingerprint, plan, and enrollment state. Do not publish port 9100 on the public internet. Bind it to 127.0.0.1 when only local probes need it. MQTT clients that already authenticate can read the same status on $SYS/Coreflux/License.

Metrics

GET /metrics returns Prometheus text exposition format, version 0.0.4 (text/plain; version=0.0.4; charset=utf-8). A scrape still succeeds if the live client list cannot be read. In that case coreflux_clients_connected, coreflux_sessions_total, and coreflux_retained_messages_total are 0.
Counters other than coreflux_subscriptions_total count from process start and return to zero only when the broker restarts, so Prometheus rate() and increase() work across midnight. In earlier releases, coreflux_messages_published_total, coreflux_bytes_sent_total, and coreflux_bytes_received_total followed the daily stats file (FluxCapacitorStats_*.json) and dropped to zero at UTC midnight. That daily file still keeps its per-day totals. coreflux_subscriptions_total still counts $SYS subscriptions since the daily stats reset.The same counters feed an enrolled broker’s fleet performance metrics. They keep updating whether or not fleet metrics are turned on.

When durability or clustering is active

Each family below always includes an *_enabled gauge (0 or 1). The rest of the family is emitted only while that feature is active.

Cluster status

GET /cluster is the high-availability clustering (beta) view of one node. It never includes the cluster shared secret. Ask the leader for the authoritative view of the other nodes: only the leader checks whether members are alive, so a follower reports other nodes as unknown. The answer has one of three shapes.
Clustering is disabled, not granted by the license, its configuration is invalid (the log names the problem), or the node is still starting:
When clustering is running, the document has these blocks: The counters restart at 0 when the node restarts. The short form on /info is enough for a dashboard tile. See Clustering for the values to watch.

Log level

GET /loglevel and POST /loglevel change the broker’s minimum log level without a restart. The new level lasts until the process exits. The next start uses COREFLUX_LOG_LEVEL again (default Information). Read the current level:
Set it with a query string or a JSON body. If both are sent, the query string wins. Names are case-insensitive; the response uses the canonical name.
Success is { "level": "Debug" }. An unknown or missing level is 400:
POST /loglevel is unauthenticated. Anyone who can reach the ops port can raise verbosity for the whole process. Keep the port on a private network.

Configuration

The ops listener is on by default. Environment variables override the broker configuration. Restart the broker after changing them. When COREFLUX_OPS_ENABLED is set, the listener is on only for 1 or true (any letter case). false, 0, and any other value turn it off. When the variable is unset, OpsEnabled is used. An invalid COREFLUX_OPS_PORT (not an integer, or outside 1–65535) is ignored, and OpsPort is used instead. A non-empty COREFLUX_OPS_BIND_IP replaces OpsBindIP. When the listener is disabled, nothing is bound to the ops port and these paths are not served.
The same names are listed under Environment Variables & Secrets.

Security

These endpoints are unauthenticated on purpose, so orchestrator probes and in-cluster scrapers do not need a password.
  • Do not expose port 9100 to the public internet.
  • On a single broker host, bind 127.0.0.1 (or publish the Docker port on 127.0.0.1 only).
  • In Kubernetes, leave the port on the pod network and scrape it with a Service. Do not put it on a public LoadBalancer.
  • Treat /license and POST /loglevel as sensitive even though /health is not.
/info is intentionally free of secrets: no MQTT passwords, no MCP token values, no license file, no cluster shared secret.

Kubernetes

The published broker image has no shell, so use HTTP probes rather than a command inside the container.
A longer startup example is on Startup Options. Prometheus Operator scrape (the Service must expose a port named ops):

Try it on the broker host

With the default port published on the broker host:
Confirm the ops paths are not served on the WebSocket / HTTP port (expect 404):

Next Steps

System topics

Read the same license status, and the rest of the broker, over MQTT.

Licensing and Enrollment

Interpret tier, limits, fingerprint, activation, and enrollment.
Last modified on October 21, 2026