ON_OFF Phase 0-3: service-controller (.13:8443, token-gated, containers+systemd-user, audit log) + portal toggle cards (live status, per-user who, failed token, RAM) — full E2E verified

This commit is contained in:
2026-10-08 19:10:45 +11:00
parent bb2c6e5d0d
commit 85827be3be
15 changed files with 549 additions and 7 deletions

View File

@@ -61,3 +61,51 @@ for b in sam jo harry finn shared-media; do $G bucket allow --read --write "$b"
> Symmetry: ensure S3_ACCESS_KEY/S3_SECRET_KEY in `.env` match what `key import`
> registers, else portal uploads will 403.
## ON/OFF service-controller (ON_OFF.md)
Host-side on/off API on `.13`, port **8443** (chosen because it is already in the
NixOS `networking.firewall.allowedTCPPorts` allowlist — 8091/8092 were taken by
langgraph-service / photo-dashboard, and 8099 was blocked by the firewall).
Files live in `deploy/service-controller/` and copy to `/home/sam/service-controller/` on .13.
Install / update:
```bash
# on .27 (this repo), then:
scp -q deploy/service-controller/service_controller.py \
deploy/service-controller/services.toml \
deploy/service-controller/run.sh \
deploy/service-controller/family-service-controller.service \
sam@192.168.20.13:/home/sam/service-controller/
# on .13:
cd /home/sam/service-controller
python3 -m venv .venv # first time only
./.venv/bin/pip install -q -r requirements.txt # first time only
cp family-service-controller.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now family-service-controller.service
```
Secrets:
- `SC_TOKEN` lives in `/home/sam/.config/environment.d/10-secrets.conf` (git-ignored; run.sh sources it).
- The portal gets `SC_TOKEN` from its `.env` on .13 (injected at deploy; never committed).
Firewall note: the controller binds `0.0.0.0:8443` and is protected by the token.
Binding only to the docker bridge gateway was dropped because this NixOS box drops
INPUT from containers to non-allowlisted host listeners; `0.0.0.0` on the already-
allowed port 8443 is the workable, token-gated compromise.
Portal wiring: `docker-compose.yml` sets `SC_URL=http://host.docker.internal:8443`
and `SC_TOKEN=${SC_TOKEN:-}` on the portal service, with
`extra_hosts: ["host.docker.internal:192.168.144.1"]` (host-gateway resolves to the
DOWN docker0 bridge on this box; pinning to the fhl-net gateway works).
Verify:
```bash
curl -s http://127.0.0.1:8443/health # {"ok":true,...}
curl -s -H "X-Controller-Token: $SC_TOKEN" http://127.0.0.1:8443/services
curl -s -X POST -H "X-Controller-Token: $SC_TOKEN" http://127.0.0.1:8443/services/lmms/stop
```