From fffa5560eb5ba3c72228ac5b9762f70413d57106 Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 24 Jun 2026 17:24:31 +0200 Subject: [PATCH] =?UTF-8?q?trmnl=20(128):=20provisioned=20=E2=80=94=20cont?= =?UTF-8?q?ainer=20page,=20inventory,=20ingress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LXC 128 trmnl hosts the TRMNL plugins middleware (dtoro/terminalito), polled by TRMNL cloud. trmnl-plugins.service on :9851; Caddy block + LE cert; VPS traefik router trmnl-public + cert mirror. Public path pending VPS<->home netbird route recovery (was "No networks available" at provision time, artifacto/blog 504 too). LAN Technitium record + SOPS enrollment + Google/MVG creds pending. Plan -> In Progress. Co-Authored-By: Claude Opus 4.8 --- containers/128-trmnl.md | 42 +++++++++++++++++++++++++++++++++++++++ containers/index.md | 1 + infrastructure/ingress.md | 4 ++++ inventory.yaml | 13 ++++++++++++ plans/index.md | 2 +- 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 containers/128-trmnl.md diff --git a/containers/128-trmnl.md b/containers/128-trmnl.md new file mode 100644 index 0000000..cfcfc4f --- /dev/null +++ b/containers/128-trmnl.md @@ -0,0 +1,42 @@ +# 128 — `trmnl` + +Self-hosted middleware for TRMNL e-ink plugins. TRMNL cloud polls it; it fetches/shapes live data into JSON the plugin's Liquid template renders. + +## At a glance +- **Hostname:** `trmnl` +- **IP:** `192.168.8.211` +- **Privilege:** unprivileged +- **Resources:** 1 core / 768 MiB RAM / 8 GiB rootfs (Debian 13) +- **Mounts:** none +- **Public hostname:** `trmnl.hubris.network` (via [VPS ingress](../infrastructure/ingress.md)) + +## Role +Runs one FastAPI aggregator (`server.app:app`, port 9851) that mounts a router per plugin from the `dtoro/terminalito` repo. First consumer: `munich-home` (`/munich-home/dashboard`) — weather (Open-Meteo), MVG transit, Google Calendar, plus server-side Kita/quote logic. Talks out to the public internet for those APIs; TRMNL cloud polls it inbound every 15 min. Bearer-token gated (`TRMNL_POLL_TOKEN`); `/health` is open. + +## Service / port map +| Service | Listen | Notes | +|---------|--------|-------| +| `trmnl-plugins` | `0.0.0.0:9851` | uvicorn aggregator; `EnvironmentFile=/etc/trmnl-plugins/env` | + +## Storage / config paths +- `/opt/terminalito` — git checkout (origin = internal gitea `http://192.168.8.121:3000/dtoro/terminalito.git`) +- `/opt/terminalito/server/.venv` — venv +- `/etc/trmnl-plugins/env` — `TRMNL_POLL_TOKEN` (+ Google/MVG creds once enrolled) +- `/etc/systemd/system/trmnl-plugins.service` + +## Auto-deploy +Not yet wired. Manual deploy: `pct exec 128 -- /opt/terminalito/server/deploy/deploy.sh`. When wired it follows [auto-deploy](../infrastructure/auto-deploy.md) Shape B (`/opt/terminalito-deploy/`, gitea webhook on `dtoro/terminalito`, add `192.168.8.211` to gitea `ALLOWED_HOST_LIST`). + +## Secrets +Not yet SOPS-enrolled. The poll token is set directly in `/etc/trmnl-plugins/env`. Google Calendar + MVG creds are pending: enroll via `homelab client add trmnl` + bootstrap, add `secrets/trmnl-oauth.yaml`, then `server/deploy/render-env.sh` builds the env from `homelab secret trmnl-oauth`. Until then calendar/transit cards degrade to empty; weather works. + +## Related +- [Caddy (121)](121-caddy.md) — LAN reverse proxy (`trmnl.hubris.network → 192.168.8.211:9851`) +- [VPS ingress](../infrastructure/ingress.md) — public edge (cert mirror + traefik router) +- [DNS (107)](107-dns.md) — needs Technitium A record `trmnl → 192.168.8.175` (pending) +- [Gitea (104)](104-gitea.md) — source repo `dtoro/terminalito` +- [Plan: 2026-06-24 TRMNL plugins LXC](../plans/2026-06-24-trmnl-plugins-lxc.md) + +## Changelog +### 2026-06-24 — provisioned +LXC 128 created (Debian 13, unprivileged, `192.168.8.211`). Deployed `trmnl-plugins.service` on :9851 from `dtoro/terminalito`. Caddy block added (`dtoro/caddy-conf`) + LE cert via IONOS DNS-01; verified `/health` 200 and `/munich-home/dashboard` (live weather) through Caddy. Cert mirrored to VPS (`trmnl.fullchain.crt`/`trmnl.privkey.key`) + traefik router `trmnl-public` → `192.168.8.211:9851` added to `/opt/traefik-dynamic.yaml`. **Public path pending**: VPS↔home netbird route was down at provision time (`No networks available`, 3/6 peers — artifacto/blog also 504); resolves when the mesh route recovers. **LAN pending**: Technitium A record not yet added. Not SOPS-enrolled; Google/MVG creds pending. diff --git a/containers/index.md b/containers/index.md index ace0d04..363ccfd 100644 --- a/containers/index.md +++ b/containers/index.md @@ -16,6 +16,7 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per | 122 | [arriman](122-arriman.md) | 192.168.8.132 | priv | 4 | 8 GiB | 24 GiB | `/mnt/library` | `jellyseerr` / `qbit` / `sab` | running | | 124 | [authentik](124-authentik.md) | 192.168.8.180 | priv | 2 | 4 GiB | 20 GiB | — | `auth.hubris.network` | running | | 126 | [plato](126-plato.md) | 192.168.8.190 | priv | 2 | 2 GiB | 8 GiB | `/mnt/library/documents/plato` | `plato.hubris.network` | running | +| 128 | [trmnl](128-trmnl.md) | 192.168.8.211 | unpriv | 1 | 768 MiB | 8 GiB | — | `trmnl.hubris.network` | running | ## Recently destroyed (kept for archaeology) diff --git a/infrastructure/ingress.md b/infrastructure/ingress.md index 163d7a2..59d34e3 100644 --- a/infrastructure/ingress.md +++ b/infrastructure/ingress.md @@ -42,6 +42,7 @@ LAN clients resolve via the [Technitium DNS on dns (107)](dns.md) → `192.168.8 | ------------------------------ | -------------------------------- | -------------------------------- | -------------------------------------------- | ------------------------------------------ | | `artifacto.hubris.network` | `/p/*`, `/static/*`, `/healthz` | `192.168.8.205:3100` | `artifacto-strip-sso` + `artifacto-ratelimit` (50 rps / 100 burst) | `fullchain.crt` / `privkey.key` | | `blog.hubris.network` | whole host | `192.168.8.205:8080` | `blog-ratelimit` (100 rps / 200 burst) | `blog.fullchain.crt` / `blog.privkey.key` | +| `trmnl.hubris.network` | whole host | `192.168.8.211:9851` ([trmnl 128](../containers/128-trmnl.md)) | `trmnl-ratelimit` (20 rps / 40 burst) | `trmnl.fullchain.crt` / `trmnl.privkey.key` | `artifacto-strip-sso` blanks inbound `X-Authentik-*` and `X-Artifacto-Gateway` so external clients can't spoof the SSO auto-login header contract. Path split is enforced at the VPS router rule, not by home Caddy. See [Artifacto on apps (105)](../containers/105-apps.md). @@ -85,6 +86,9 @@ No cert-mirror entry and no `hubris-public-cert-sync.sh` mapping is needed for ` ## Changelog +### 2026-06-24 — `trmnl.hubris.network` exposed +TRMNL plugins middleware on [trmnl (128)](../containers/128-trmnl.md). File-provider router `trmnl-public` → `192.168.8.211:9851`, `trmnl-ratelimit` (20 rps / 40 burst), cert mirrored as `trmnl.fullchain.crt`/`trmnl.privkey.key`. Note: provisioned while the VPS↔home netbird route was down (`No networks available`, 3/6 peers — artifacto/blog 504 too); serves once the mesh route recovers. + ### 2026-05-31 — `auth.hubris.network` now served locally on the VPS Authentik migrated onto the VPS ([investigation](../investigations/2026-05-31-authentik-vps-migration.md)). Unlike the home-backed services above, `auth` is a local container routed via traefik Docker-provider labels with traefik-managed Let's Encrypt — no cert-mirror, no `traefik-dynamic.yaml` router. Admin UI gated by an ipAllowList middleware. Traefik gained a second Docker network (`auth`, `172.30.1.0/24`) to reach it while keeping its DB/Redis isolated from the netbird stack. diff --git a/inventory.yaml b/inventory.yaml index 5950e14..b6d505e 100644 --- a/inventory.yaml +++ b/inventory.yaml @@ -78,6 +78,10 @@ services: artifacto: backend: apps url: https://artifacto.hubris.network + trmnl: + backend: trmnl + url: https://trmnl.hubris.network + note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud) zimaos: url: https://zimaos.hubris.network backend: zimaos @@ -117,6 +121,15 @@ hosts: mounts: - /mnt/library age_pubkey: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6 + trmnl: + kind: lxc + pve_id: 128 + host: hubris + os: linux + role: trmnl-middleware + lan_ip: 192.168.8.211 + public_host: trmnl.hubris.network + # not yet mesh/SOPS-enrolled — see containers/128-trmnl.md jellyfin: kind: lxc pve_id: 101 diff --git a/plans/index.md b/plans/index.md index 70cf1e0..69c949b 100644 --- a/plans/index.md +++ b/plans/index.md @@ -6,7 +6,7 @@ Pre-flight runbooks for planned changes that haven't happened yet. Once executed | Date | Title | Status | | ---- | ----- | ------ | -| 2026-06-24 | [TRMNL plugins LXC (128) + middleware deploy pipeline](2026-06-24-trmnl-plugins-lxc.md) | Planned | +| 2026-06-24 | [TRMNL plugins LXC (128) + middleware deploy pipeline](2026-06-24-trmnl-plugins-lxc.md) | In Progress | | 2026-06-01 | [Slate AX → SODOLA managed switch migration](2026-06-01-slate-ax-to-sodola-migration.md) | Done | ## Conventions