# MQTT Management Platform — Release 1 Web-based administration for an Eclipse Mosquitto 2.1 broker: management login/RBAC, MQTT client/group/role management with ACL editing via the Dynamic Security plugin, and generated + validated broker configuration. Architecture and scope follow `specification.md` (Release 1, §68.1). ## Stack ```text mqtt-frontend React (Vite build) served by nginx, proxies /api → backend mqtt-backend FastAPI: management auth, RBAC, dynsec client, config engine mosquitto Eclipse Mosquitto 2.1.2 (pinned), dynsec enabled mosquitto-control control sidecar: validate/activate/reload configs mqtt-db PostgreSQL (management data only; NOT host-exposed) ``` Networks: `frontend-net` (frontend↔backend) and `backend-net` (backend↔db↔mosquitto↔control). Only the frontend HTTP port is published. ## Quick start ```bash cp .env.example .env # then EDIT the change-me values! docker compose up -d --build ``` Open http://localhost:8080 and sign in with `ADMIN_INITIAL_USERNAME` / `ADMIN_INITIAL_PASSWORD` from your `.env`. Bootstrap sequence on first start: 1. PostgreSQL starts; backend creates its schema (or run migrations explicitly, see below) and seeds the management admin. 2. Mosquitto starts with `mosquitto/bootstrap/mosquitto.conf` on the internal network; the dynsec plugin seeds its `admin` client from `MOSQUITTO_DYNSEC_PASSWORD`. 3. Backend connects to dynsec as that admin client — the management channel is alive. 4. Configuration changes made in the UI are generated from the PostgreSQL model, validated with `mosquitto --test-config` (via the control sidecar), deployed atomically, and recorded as a revision. > **Note for Windows users:** the compose healthcheck for mosquitto and > the control sidecar rely on Unix facilities (`pid: service:`, POSIX > shell commands); run the stack with Docker Desktop + WSL2 as usual — > nothing here assumes a Linux host beyond Docker itself. ## Configuration channels (why this design) * **Dynamic Security (runtime)** — clients/groups/roles/ACLs change via the MQTT `$CONTROL/dynamic-security/v1` API. No files involved. * **Generated mosquitto.conf (rare)** — listeners/global settings are generated from the DB model and deployed through `mosquitto-control`, which shares the broker's PID namespace (`pid: "service:mosquitto"`) to signal reloads. No Docker socket is mounted anywhere (spec §29). * Mosquitto 2.1's HTTP API is **not** used as a management channel: it has no authentication and (currently) exposes read-only plugin info. ## Development Backend (in container): ```bash docker compose exec mqtt-backend pytest -q # API + generator tests docker compose exec mqtt-backend ruff check app tests docker compose exec mqtt-backend alembic upgrade head # explicit migrations ``` Frontend (local dev server with proxy to backend): ```bash cd frontend && npm install && npm run dev # http://localhost:5173 ``` ## Repository layout ```text compose.yaml stack definition (5 services, 2 networks) .env.example environment template (secrets: edit before use) mosquitto/bootstrap/ the one file that ships with the deployment broker-control/ validate / atomic-activate / reload sidecar backend/ FastAPI app, models, dynsec client, configgen frontend/ React + Vite SPA served by nginx ``` ## Status & TODO — per release plan (specification.md §68) Progress against the MVP release plan. Checked items are implemented and covered by the test suite / running stack. ### Release 1 — Core broker administration (§68.1) — COMPLETE - [x] Docker Compose stack (5 services, 2 networks, no Docker socket) - [x] Mosquitto 2.1.2 (pinned) + PostgreSQL - [x] FastAPI backend + React (Vite) frontend - [x] Management authentication (HttpOnly session cookies, Argon2id, `ADMIN_INITIAL_*` bootstrap, must-change-password flagging; the backend exposes the change-password API — UI enforcement of the forced change is still pending, see Release 3) - [x] Management RBAC (super_admin / broker_admin / operator / auditor) - [x] Dynamic Security integration (backend as the dynsec admin client) - [x] MQTT clients: create / disable / enable / delete, password reset, random passwords (returned exactly once) - [x] Groups: create / delete, member + role assignment with priorities - [x] Roles: create / delete, ACL editor (all six ACL types, `%u`/`%c` substitution, priorities, draft-then-save) - [x] Broker configuration editor, basic mode (structured forms + read-only preview of the generated `mosquitto.conf`) - [x] Listener management (port, bind address, protocol, anonymous, max-connections; enable/disable, edit, delete) - [x] Configuration validation (`mosquitto --test-config` via sidecar) - [x] Configuration generation from the PostgreSQL model Hardening done beyond the plan: - [x] Management identity protection: the dynsec `admin` client is hidden from Clients and refuses reset/disable/delete (resetting it kicks every connection using it — including the backend's own control channel) - [x] Dynsec channel resilience: disconnects are detected immediately and the MQTT client is rebuilt instead of failing silently - [x] Login flow returns to the originally requested page; mid-session expiry redirects to login with a return target - [x] Navigation follows spec §57; unimplemented pages are greyed out with the planned release as tooltip ### Release 2 — PKI, versioning, and controlled change (§68.2) — PARTIAL - [x] Configuration revisions (immutable history; list + detail API and UI) - [x] Configuration deployment with health check (dynsec roundtrip probe) - [x] Broker reload (`mosquitto_signal config-reload` via sidecar) - [x] mosquitto-control service (validate / atomic activate / signal / broker health) - [ ] Configuration diff view (revision N-1 → N) - [ ] Configuration rollback (creates a new revision, §62) - [ ] Reload-vs-restart classification (§28) — only a restart warning on listener port/bind edits so far, no general classification - [ ] CA creation (internal CA, import CA) - [ ] Server certificate generation (CN, SANs, key algorithm) - [ ] Client certificates (managed-key mode, one-time download) - [ ] CSR signing - [ ] Certificate revocation + CRL generation/publishing - [ ] mTLS listeners (model fields + config generation exist; blocked on PKI — certfile/keyfile cannot be emitted yet) ### Release 3 — Operations, safety, and visibility (§68.3) — PARTIAL - [x] Audit logging (backend records every privileged action: LOGIN, LOGOUT, CLIENT_*, GROUP_*, ROLE_*, ACL_CHANGED, LISTENER_*, BROKER_CONFIG_DEPLOYED, USER_*, …) - [x] Client metadata/tags (owner / purpose / tags in PostgreSQL; API accepts them on create) - [ ] Audit browsing UI (events are recorded; no viewer page yet) - [ ] Forced password-change UI (backend flag + API exist; the frontend does not enforce `must_change_password` yet) - [ ] Client metadata editing in the UI - [ ] ACL simulator (§15) - [ ] Dashboard ($SYS monitoring, broker status, §33) - [ ] Certificate expiry monitoring - [ ] Backup and restore (logical, encrypted, §45) - [ ] Configuration drift detection (§48) - [ ] Last-known-connection tracking + connected-client view (§34) ### Post-MVP (§68.4 / §69 / §70) — NOT STARTED - [ ] Topic explorer / MQTT test client - [ ] Connection tester + client provisioning wizard - [ ] Bridge configuration - [ ] Retained-message administration - [ ] Certificate expiry alerts - [ ] Prometheus metrics + external notifications - [ ] Change approval (four-eyes) workflow - [ ] Topic namespace / JSON-schema registries, and later Phase-3 items