# 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.