Website: fgjcarlos.github.io/mcm · MCM is an open source control plane for Eclipse Mosquitto.
It aims to make Mosquitto easier to operate by adding a modern web UI, REST API, realtime observability, user management, ACL management, and deployment tooling — without replacing Mosquitto as the MQTT broker.
The goal is simple: the stability and small footprint of Mosquitto, with a modern administration and observability experience.
Mosquitto is lightweight, reliable, widely adopted, and ideal for edge and industrial MQTT deployments. But day-to-day operation often still depends on manually editing configuration files, ACL files, external dashboards, scripts, and ad-hoc tooling.
MCM focuses on the missing operational layer:
- Visual user and ACL management
- MQTT traffic and client observability
- Topic exploration
- Simple Docker and standalone deployments
- API-first administration
- Edge/industrial-friendly operation
MCM is not intended to compete with enterprise MQTT brokers at the broker level. It is designed to make existing Mosquitto deployments easier to manage.
Status: Alpha — the MVP is feature-complete and the project is in a hardening phase.
MCM now ships an end-to-end stack: backend API + CLI, persistent SQLite, MQTT 3.1.1 client (paho), realtime WebSocket stream, dashboard UI with real login, RBAC, structured logging, Prometheus metrics, optional HTTPS / mTLS, audit and security event trails, and webhook alerting. See What works today for the full list.
Reference docs:
- ROADMAP.md — high-level phases and direction.
- docs/openapi.yaml — full REST + WebSocket API contract (OpenAPI 3.1).
- docs/adr — architecture decision records.
- docs/acl.md — ACL model and REST shape.
- docs/json-schemas.md — JSON Schema validators bound to MQTT topic filters.
- docs/sparkplug.md — Sparkplug B topic awareness.
- docs/webhook-alerting.md — outbound operational webhooks.
- docs/integrations/node-red.md — Node-RED integration examples.
- Mosquitto stays the broker: MCM acts as a control plane, not a broker replacement.
- Edge-first: low resource usage, simple deployment, Raspberry Pi / ARM compatibility.
- Simple operations: Docker Compose for fast setup, standalone binary for production-like edge installs.
- Open source first: useful community edition before considering any open-core model.
- Industrial-friendly: clear APIs, diagnostics, auditability, and predictable deployment patterns.
- Secure by default: password hashing, scoped permissions, TLS support, audit logs, and explicit configuration.
- Industrial IoT engineers
- Automation and OT/IT integrators
- Node-RED users
- Homelab and maker users running Mosquitto
- Edge computing deployments
- Smart building, energy, agriculture, and Industry 4.0 projects
Backend
- Admin authentication with bcrypt-hashed passwords, JWT tokens, and an /auth/me session check.
- Optional TOTP MFA per admin user:
/auth/mfa/setupreturns anotpauth://URL and ten single-use recovery codes (hashed at rest); enrolled users complete login via a short-lived MFA challenge token. - Login brute-force protection: per-IP and per-username failure counters with HTTP 429 +
Retry-Afterlockouts and security events. - Role-based access control with four roles (
viewer < auditor < operator < admin); every protected endpoint declares a minimum role. - ACL management persisted in SQLite, MQTT wildcard validation, and Mosquitto ACL line projection.
- JSON Schema validators attached to MQTT topic filters, evaluated live against incoming payloads.
- Sparkplug B namespace classification on observed topics.
- MQTT subscription via the maintained
eclipse/paho.mqtt.golangclient (keepalive, auto-reconnect, TLS). - WebSocket event stream at
/api/v1/broker/eventsauthenticated viaSec-WebSocket-Protocol(no token in URLs). - Optional HTTPS and mTLS on the MCM HTTP API.
- Persistent admin audit trail and operator-facing security event log.
- Outbound webhook alerting with HMAC signatures.
- Prometheus
/metricsendpoint with bounded label cardinality, plus a starter Grafana dashboard. - Structured
log/slogJSON logs with request-ID propagation (X-Request-ID). - OpenAPI 3.1 contract for every endpoint, linted in CI.
Frontend
- Real login screen against
/api/v1/auth/login; the token is the single source of truth for protected API calls and the broker WebSocket subprotocol handshake. - Dashboard with broker status, traffic widgets, topic explorer, realtime log feed, audit + security panels.
- Logout from the sidebar; expired or 401 responses bounce the user back to the login screen.
Operations
- Single-binary CLI (
mcm server | doctor | status | config | backup | version). - Docker Compose dev stack for a local Mosquitto broker.
- GoReleaser packaging targeting Linux amd64/arm64, macOS arm64, Windows amd64.
- CI on every PR:
go test -race+ cross-OS builds + frontend lint/build + OpenAPI lint.
Out of scope for the first stable release
- Multi-broker management
- Clustering / high availability
- Multi-tenancy
- SSO / LDAP / SAML
- Advanced industrial protocol bridges
- Embedded broker mode (Mosquitto stays the broker)
┌────────────────────┐
│ Web UI │
│ React + Vite │
└─────────┬──────────┘
│
REST / WebSocket
│
┌─────────▼──────────┐
│ MCM API │
│ Go │
└─────────┬──────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Mosquitto │ │ SQLite │ │ Realtime │
│ Broker │ │ Config/Auth │ │ Events │
└──────────────┘ └──────────────┘ └──────────────┘
The frontend can be embedded into the Go binary for simple distribution:
//go:embed all:frontend/dist- Go 1.24 with the stdlib
net/httpServeMux (no third-party HTTP framework) andlog/slogfor structured logging. - SQLite via the pure-Go
modernc.org/sqlitedriver so the binary cross-compiles to every supported target without CGO. - Cobra for the CLI.
- MQTT:
eclipse/paho.mqtt.golangfor broker subscription, status, and topic inspection. - Auth:
golang-jwt/jwt/v5+golang.org/x/crypto/argon2/bcrypt for password hashing. - Metrics:
prometheus/client_golangon a private registry. - Tests: stdlib
testing; in-process MQTT broker for integration viamochi-mqtt/server/v2.
- React 19 + Vite 8 + TypeScript 6
- Tailwind CSS 4 via
@tailwindcss/vite - ESLint 10 (with
eslint-plugin-react-hooks7) - Reproducible installs pinned by
frontend/package-lock.jsonandfrontend/.nvmrc.
- Single statically linked Go binary (CGO disabled; pure-Go SQLite driver covers every target).
- Docker Compose stack for local Mosquitto.
- GoReleaser multi-arch builds for
linux/amd64,linux/arm64,darwin/arm64,windows/amd64.
mcm server # Start API, web UI, and Mosquitto integration
mcm doctor # Run diagnostics against Mosquitto, config, DB, TLS, disk
mcm status # Show broker and MCM runtime status
mcm config init # Create initial config file
mcm config validate # Validate config file
mcm backup create # Create a portable SQLite backup artifact
mcm backup restore # Restore local state from a SQLite backup artifact
mcm version # Print version/build informationEvery command takes a global --config flag pointing at the YAML file documented in docs/openapi.yaml and the config init template.
MCM stores its local application state in the SQLite database configured by database.path. Operators can create a consistent backup artifact and restore it later with the CLI:
# Create a backup from the configured SQLite database.
mcm backup create --config ./mcm.yaml --output ./backups/mcm.db
# Restore into the configured database path.
# If the target database already exists, --force is required to overwrite it.
mcm backup restore --config ./mcm.yaml --input ./backups/mcm.db --forceBackup artifacts are SQLite database files created from a consistent snapshot of the configured MCM database. They include MCM state stored in SQLite, such as admin users, persisted broker metrics, security events, and audit events.
Backups do not include files outside the MCM SQLite database, including external Mosquitto configuration, Mosquitto password files, ACL files, TLS CA/client certificates or keys, Docker volumes, logs, or files referenced from configuration. Back up those assets separately according to your deployment process.
Restore validates the input artifact before writing it. To protect running installations from accidental data loss, restore refuses to overwrite an existing configured database unless --force is provided. Stop the MCM server before restore so no process is using the database while it is replaced.
The project should treat security as a product requirement, not a later add-on:
- Password hashing with a modern algorithm
- JWT/session security with explicit expiration
- TLS support documentation
- Principle-of-least-privilege ACL model
- Audit log for administrative actions
- Safe handling of Mosquitto password and ACL files
- Clear warnings for insecure development-only settings
The repository includes a Docker Compose stack for local Mosquitto development.
# Clone repository
git clone https://github.com/fgjcarlos/mcm.git
cd mcm
# Start local Mosquitto
docker compose up -d
# Follow broker logs
docker compose logs -f mosquitto
# Stop the stack
docker compose downThe development broker exposes:
- MQTT TCP:
localhost:1883 - MQTT over WebSocket:
localhost:9001
The local Mosquitto configuration lives in deploy/mosquitto/config/mosquitto.conf.
This configuration allows anonymous access for local development only. Do not use it as-is in production.
The Go CLI lives under cmd/mcm; production wiring is in internal/cli and internal/server.
# Show available commands
go run ./cmd/mcm --help
# Print build/version information
go run ./cmd/mcm version
# Start the initial API server
go run ./cmd/mcm server --config ./mcm.yaml
# Run tests
go test ./...
# or
make test
# Build the CLI binary
make buildThe initial ACL model and REST API are documented in docs/acl.md.
The current API provides:
GET /api/v1/aclsPOST /api/v1/aclsPUT /api/v1/acls/{id}DELETE /api/v1/acls/{id}
It supports MQTT wildcard topic filters, validates invalid wildcard placement, and maps permissions directly to Mosquitto ACL lines such as topic read ..., topic write ..., and topic readwrite ....
The React + Vite frontend lives under frontend/. It ships a login screen, a broker dashboard, audit/security panels, and the realtime topic explorer.
Toolchain requirements:
- Node.js
^22.13.0(LTS Jod) or>=24.0.0(LTS), enforced via theenginesfield infrontend/package.json. - npm
>=10.0.0.
A frontend/.nvmrc pins a tested LTS version — run nvm use from frontend/ to switch.
Reproducible installs rely on the committed frontend/package-lock.json. Use npm ci in clean environments (such as CI) to install the exact resolved tree; use npm install only when you intentionally want to update dependencies.
# One-time, reproducible install (matches package-lock.json exactly)
cd frontend
npm ci
# Start the Vite development server
npm run dev
# Create a production build
npm run buildThe full REST + WebSocket API is described in OpenAPI 3.1 at docs/openapi.yaml. It covers every endpoint MCM currently exposes (health, metrics, auth, admin users, ACLs, JSON schemas, audit/security events, broker WebSocket) with request/response schemas, auth requirements, and per-route role floors.
Render or browse the spec with any OpenAPI viewer, for example:
# Local Swagger UI (no install)
docker run --rm -p 8081:8080 -e SWAGGER_JSON=/spec/openapi.yaml \
-v "$(pwd)/docs:/spec" swaggerapi/swagger-ui
# Or Redocly preview
npx -y @redocly/cli@latest preview-docs docs/openapi.yamlThe spec is linted on every PR via npx @redocly/cli lint in CI (.github/workflows/ci.yml, openapi job), with rule overrides documented in redocly.yaml.
MCM exposes Prometheus metrics at GET /metrics (text exposition format, unauthenticated by design — gate it at your reverse proxy or via network policy in production).
Exposed metric families:
mcm_http_requests_total{method, route, status}— HTTP request counter.routeuses the ServeMux pattern (e.g.GET /api/v1/admin-users/{id}), never the raw URL, so cardinality stays bounded.mcm_http_request_duration_seconds{method, route}— request latency histogram (default Prometheus buckets).mcm_broker_status— 1 when Mosquitto is connected, 0 otherwise.mcm_broker_reconnects_total— broker disconnect transitions (each triggers an auto-reconnect attempt).mcm_broker_messages_total— MQTT topic messages observed. Topic names are intentionally not labels.mcm_broker_payload_bytes_total— total observed payload bytes.mcm_login_attempts_total{result}— admin login attempts (successorfailure).mcm_audit_events_total{result}— administrative audit events.mcm_security_events_total{category}— security events, labeled by category (bounded set).
Sample Prometheus scrape:
scrape_configs:
- job_name: mcm
metrics_path: /metrics
scheme: http # or https when http.tls.enabled is true
static_configs:
- targets: ["mcm-host:8080"]A starter Grafana dashboard is committed at deploy/grafana/mcm-dashboard.json (broker status, reconnect rate, message rate, HTTP request/latency by route, login attempts, security/audit events). Import it in Grafana and pick your Prometheus datasource.
The MCM HTTP API can serve HTTPS by populating http.tls in the configuration:
http:
bind_address: 0.0.0.0
port: 8443
tls:
enabled: true
cert_file: /etc/mcm/tls/server.crt
key_file: /etc/mcm/tls/server.key
min_version: "1.2" # or "1.3"
client_ca_file: "" # set to enable client cert verification
require_client_cert: false # set to true with client_ca_file for strict mTLSWhen tls.enabled is true:
- Cert and key are loaded with
tls.LoadX509KeyPair; validation fails fast if either file is missing. - The minimum negotiated TLS version is enforced (
1.2for broad compatibility,1.3for stricter deployments). - All responses include
Strict-Transport-Security: max-age=31536000; includeSubDomains. - Setting
client_ca_fileenables client certificate verification: optional unlessrequire_client_cert: true, in which case clients without a CA-signed certificate are rejected at the handshake.
Local development with mkcert (trusted by the dev machine, no browser warning):
mkcert -install
mkcert -cert-file dev.crt -key-file dev.key 127.0.0.1 localhostPoint http.tls.cert_file/key_file at dev.crt/dev.key.
Quick self-signed test certificate with openssl (useful for non-browser clients):
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
-keyout server.key -out server.crt -days 365 -nodes \
-subj "/CN=mcm-local" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"Production guidance: use certificates issued by your internal CA (or a public ACME provider for internet-exposed deployments), keep key_file mode 0600, and bind only to the interface that should accept inbound traffic. For mTLS, ship the trusted client CA bundle to client_ca_file and enable require_client_cert: true to make a missing or invalid client certificate fail closed.
The Compose stack starts Mosquitto only; the MCM service is launched separately with the CLI so operators control its lifecycle and TLS posture. The intended deployment shape (and the operator-facing variables) is documented in deploy/mcm/README.md.
End-to-end development loop:
# 1. Start the local broker
docker compose up -d
# 2. Start MCM against the dev config
go run ./cmd/mcm server --config ./mcm.yaml
# 3. In a second terminal, build the frontend (or `npm run dev` for HMR)
npm --prefix frontend run buildThe roadmap is tracked in two places:
- ROADMAP.md: high-level product direction and phases
- GitHub Issues: actionable tasks with acceptance criteria
This keeps the public vision readable while making actual execution trackable.
MCM is public and feedback is welcome, but it is currently maintainer-led. Issues and pull requests should be focused, actionable, and aligned with the roadmap.
Before opening a non-trivial pull request, please open an issue first and wait for maintainer feedback. All changes to main go through pull requests and require maintainer/code owner review.
See CONTRIBUTING.md for the contribution workflow and SECURITY.md for security reporting.
MCM is licensed under the Apache License 2.0.
Please preserve copyright, license, and attribution notices when redistributing or modifying the project. See NOTICE.
If you use MCM in research, documentation, talks, articles, commercial case studies, or public deployments, citation is appreciated. See CITATION.cff.
MCM stands for Mosquitto Control Manager.