# BMS — memory DCIM demo platform: Next.js 16 frontend, FastAPI backend, TimescaleDB, Mosquitto MQTT, and Python simulator bots that publish fake data-centre telemetry. Live at **http://192.168.1.249:5646/dashboard** (Portainer stack `bms` on Unraid). ## Decisions & rationale - **Auth is disabled (demo mode).** The repo shipped half-converted: `proxy.ts` already said "auth is disabled for demo mode", but `app/layout.tsx` still wrapped the tree in `` and `topbar.tsx` used ``. `@clerk/nextjs` v7 fails `next build` without a real publishable key, so the frontend image could not build at all. Finished the conversion instead of buying Clerk keys: removed `ClerkProvider`/`UserButton`, deleted the `sign-in`/`sign-up` route groups, and dropped `@clerk/nextjs` from `package.json`. The service is LAN-only and reachable remotely over Tailscale, so the tailnet is the auth boundary. - **Deploy via the Portainer API, not compose-on-host.** No `docker` CLI in WSL and Unraid's SSH (port 22) is closed, so `ops/deploy.py` tars each service's build context, POSTs it to the remote daemon's `/build` endpoint, then pushes `docker-compose.prod.yml` as a Portainer stack. Same pattern as `toknmtr/ops/deploy.py`. - **Separate `docker-compose.prod.yml`.** The dev compose file bind-mounts source into the containers and runs `uvicorn --reload` — wrong for a long-running deploy, and impossible anyway since the repo is not checked out on the Unraid host. The prod file references prebuilt image tags, sets env inline (no `.env` files on the host), and publishes only the frontend's port. - **Mosquitto config is baked into an image** (`infra/mosquitto/Dockerfile`) rather than bind-mounted, for the same reason: no repo on the host. - **`bms_net` pins subnet 172.31.42.0/24.** Docker's default address pools on that host are fully subnetted — 24 existing stacks have claimed every `172.17-172.31/16` and `192.168.x/20` slot, so auto-allocation fails with "all predefined address pools have been fully subnetted". An explicit subnet bypasses the allocator without disturbing other stacks. Only other 172.31 tenant is `wg0` on 172.31.200.0/24. - **Postgres data bind-mounts to `/mnt/user/appdata/bms/db`**, not a named volume, so a growing timeseries DB lands on the array instead of filling Unraid's fixed-size `docker.img`. - **`pnpm-workspace.yaml` must be copied in the frontend Dockerfile's deps stage.** It carries the build-script allowlist; without it pnpm aborts with `ERR_PNPM_IGNORED_BUILDS`. The file now declares both `allowBuilds` (pnpm 11 spelling) and `ignoredBuiltDependencies` (pnpm 10). `sharp`, `unrs-resolver` and `msw` are all declined — the app has no `next/image`, so sharp is dead weight. `packageManager: pnpm@11.20.0` is pinned so a future pnpm release cannot silently change this again. ## Open questions / TODOs - [ ] Postgres uses the repo's default credentials (`dcim`/`dcim_pass`). Fine while the DB has no published host port and the box is LAN-only, but change both the compose env and `DATABASE_URL` together if the port is ever exposed. - [ ] The simulator floods logs with `mqtt WARNING There are N pending publish calls.` — it publishes faster than aiomqtt drains. Cosmetic, but it makes `docker logs` near-useless and grows the log file. Worth batching or throttling the bots. - [ ] `backend/api/routes/ws.py` exists but nothing in the frontend opens a WebSocket (the UI polls every 15 s). Either wire it up or drop it. Note: Next.js rewrites do not proxy WS upgrades, so a WS client would need to reach the backend directly. - [ ] No host port is published for Postgres or MQTT. Add a `ports:` entry to `docker-compose.prod.yml` if direct DB access or external MQTT publishing is ever needed. - [ ] No reverse-proxy entry yet. Add an Nginx Proxy Manager host pointing at `:5646` if this should be reachable by name rather than IP:port. ## Session log ### 2026-08-04 - Cloned the Forgejo repo into `~/claude/projects/BMS` and deployed it to the Unraid host. - Finished the demo-mode auth conversion (see Decisions) so the frontend image could build; regenerated `pnpm-lock.yaml` without `@clerk/nextjs`. Verified `next build` locally — all 18 routes prerender. - Hit and fixed three deploy blockers in order: (1) the frontend Dockerfile never copied `pnpm-workspace.yaml`, so pnpm's ignored-builds allowlist was invisible inside the build; (2) pnpm 11 renamed that setting to `allowBuilds` and ignores `ignoredBuiltDependencies`; (3) the Unraid daemon's Docker address pools were exhausted, so the stack network could not be created. - Backend's host port 8000 deliberately not published — Portainer's Edge tunnel already owns 8000 on that host. Ports 1883/5433/5646 were free; only 5646 is published. - Added `ops/deploy.py`, `docker-compose.prod.yml`, `infra/mosquitto/Dockerfile`; updated the README (demo-mode note, Unraid deploy section) and both `.env` examples. - Verified live: all 5 containers healthy, 30 min of seeded history present, telemetry flowing, alarm engine firing (55 active), dashboard screenshotted and rendering correctly. ## External references - Repo: https://git.rdx4.com/megaproxy/BMS - Live dashboard: http://192.168.1.249:5646/dashboard - API docs (Swagger): http://192.168.1.249:5646/api/backend/docs - Portainer (stack `bms`, endpoint id 3): http://192.168.1.249:9000 — token at `~/.portainer-token` - Postgres data on the host: `/mnt/user/appdata/bms/db` - Deploy pattern copied from: `~/claude/projects/toknmtr/ops/deploy.py`