plans: mark TRMNL and wiki-hq adoption as done, move to plans/done/

This commit is contained in:
2026-07-07 21:06:58 +02:00
parent 5e3b946ded
commit 6b51c83b82
3 changed files with 4 additions and 2 deletions

View File

@@ -0,0 +1,107 @@
# 2026-06-24 — TRMNL plugins LXC (128) + middleware deploy pipeline
## Goal
Stand up a dedicated LXC to host self-hosted **middleware for TRMNL e-ink plugins**.
TRMNL cloud polls `https://trmnl.hubris.network/<plugin>/dashboard` every 15 min; the
middleware fetches/shapes live data and returns JSON that TRMNL merges into the plugin's
Liquid template. First consumer: the Munich Home Dashboard (`/munich-home/dashboard`).
One LXC + one FastAPI service hosts all current and future plugins (router per plugin).
Source repo: gitea `dtoro/terminalito` (app code). This repo only documents the fabric
wiring, same split as Artifacto/Plato.
## Current state
- No TRMNL middleware in the lab. Highest LXC id is 127 (see `containers/index.md`).
- Public hostnames terminate at the [VPS netbird traefik](../hosts/netbird-vps.yaml) → netbird
mesh → [caddy (121)](../knowledge/wiki/containers/121-caddy.md) → backend LXC. Cert obtained by Caddy
(IONOS DNS-01) and mirrored to the VPS by the daily cert-sync timer on the host.
- Auto-deploy pipelines are gitea-webhook driven, two shapes (see [auto-deploy](../knowledge/wiki/infrastructure/auto-deploy.md)).
## Target state
```
TRMNL cloud --GET 15m, Bearer token--> https://trmnl.hubris.network/munich-home/dashboard
VPS traefik (public TLS) --netbird--> caddy (121) --> trmnl (128) :9851 trmnl-plugins.service
├ Open-Meteo (weather)
├ MVG departures (transit)
└ Google Calendar (OAuth, SOPS)
```
- **LXC 128 `trmnl`**: Debian, unprivileged, ~1 core / 512 MiB1 GiB / 8 GiB rootfs. No mounts.
- **Service** `trmnl-plugins.service``uvicorn server.app:app --host 0.0.0.0 --port 9851`,
`EnvironmentFile=/etc/trmnl-plugins/env`. Auth: every path except `/health` requires
`Authorization: Bearer $TRMNL_POLL_TOKEN`.
- **Auto-deploy** (Shape B): `/opt/terminalito` working tree, sibling `/opt/terminalito-deploy/`.
- Public hostname `trmnl.hubris.network`.
## Pre-flight checklist
- [ ] Confirm next free LXC id is 128 (`homelab list`, `containers/index.md`).
- [ ] Decide IP on `192.168.8.0/16` LAN (e.g. `192.168.8.211`) — pick a free one.
- [ ] Have Google OAuth client + refresh token, MVG stop globalIds, and a generated
`trmnl_poll_token` ready for the secret (see `dtoro/terminalito` README).
## Step-by-step procedure
1. **Provision + enroll**
```bash
# create LXC 128 trmnl on hubris (Debian), then enroll it:
homelab client add trmnl # joins netbird, provisions /etc/age/key.txt, edits inventory.yaml
homelab client add --finalize-pubkey <age_pubkey> # commits the age pubkey
ssh trmnl 'apt-get install -y python3-venv git'
```
2. **Secret** (`trmnl-oauth`): create `secrets/trmnl-oauth.yaml` with
`google_client_id/secret/refresh_token`, MVG stop ids, and `trmnl_poll_token`; add a
`path_regex` rule in `.sops.yaml` granting **trmnl**'s age pubkey; `sops updatekeys`.
The service reads it at deploy time via `homelab secret trmnl-oauth` → `/etc/trmnl-plugins/env`.
3. **App + service** on LXC 128 (clone uses the **internal** gitea URL — `git.hubris.network`
resets from inside LXCs):
```bash
git clone http://192.168.8.121:3000/dtoro/terminalito.git /opt/terminalito
python3 -m venv /opt/terminalito/server/.venv
/opt/terminalito/server/.venv/bin/pip install -r /opt/terminalito/server/requirements.txt
# install /etc/systemd/system/trmnl-plugins.service, enable --now
```
4. **Deploy pipeline** (Shape B, mirrors homelab-mcp): create `/opt/terminalito-deploy/`
`{webhook.py,deploy.sh}` (HMAC vs `/etc/terminalito-deploy/secret`, filter `refs/heads/main`,
`git pull` → `pip install -r server/requirements.txt` → rebuild env from `homelab secret` →
`systemctl restart trmnl-plugins`). Receiver `:9797`. Git creds at
`/etc/terminalito-deploy/git-credentials` (mode 600). Register a gitea webhook on
`dtoro/terminalito`; add `192.168.8.<128-ip>` to gitea `app.ini` `ALLOWED_HOST_LIST`.
5. **DNS**: add `trmnl.hubris.network` A → `192.168.8.175` (caddy) on [Technitium (107)](../knowledge/wiki/containers/107-dns.md).
6. **Caddy** (`dtoro/caddy-conf`, commit+push auto-deploys):
```
trmnl.hubris.network { reverse_proxy 192.168.8.<128-ip>:9851 }
```
7. **Public exposure** on the [VPS](../hosts/netbird-vps.yaml): add traefik router+service for
`Host(\`trmnl.hubris.network\`)` → `http://192.168.8.<128-ip>:9851`; add the host to the
cert-sync map so the LE cert mirrors over.
8. **TRMNL cloud**: create a Polling private plugin, URL `…/munich-home/dashboard`, header
`Authorization: Bearer <trmnl_poll_token>`, refresh 15 min; paste `full.liquid`; add to a playlist.
## Verification
- `ssh trmnl systemctl is-active trmnl-plugins` → `active`; `curl -s localhost:9851/health` → `ok`.
- LAN: `curl -s -H "Authorization: Bearer <tok>" https://trmnl.hubris.network/munich-home/dashboard`
→ 200 JSON; without the header → 401; `/health` → 200.
- Public: same curl from off-mesh resolves via VPS, 200.
- Push a no-op commit → `/opt/terminalito-deploy` logs show pull+restart; webhook 202.
- `homelab mcp get_host trmnl` and `search_docs trmnl` agree with `containers/128-trmnl.md`.
## Post-migration
When executed, write changelog entries (same date) on:
`containers/128-trmnl.md` (new page), `containers/index.md` (row), `inventory.yaml`
(`services.trmnl`), `infrastructure/auto-deploy.md` (pipeline row + `ALLOWED_HOST_LIST` +
changelog), `containers/104-gitea.md` (webhook + allowed host), `containers/121-caddy.md`
(new proxied host), `containers/107-dns.md` (A record), `hosts/netbird-vps.md` (public route +
cert-sync). Then set this plan's status to `Done` in `plans/index.md`.

View File

@@ -0,0 +1,164 @@
# 2026-07-06 — Adopt the wiki-hq documentation architecture
## Goal
Reorganize the Homelab-Docs **narrative layer** into the `wiki-hq`
(`/Users/dtoro/Downloads/wiki-hq-main`) documentation model — `sources / wiki / index / log`
plus an `.agents/` separation and a single lint-checkable writing-style standard — **without
breaking** the Oikos machine-readable substrate that reads fixed paths. Keep the existing Mermaid
topology generator (no LikeC4).
## Context
Homelab-Docs is already a mature operational-docs system: `inventory.yaml` as source of truth,
generated `hosts/*.yaml`, an MCP server, a `homelab` CLI, and the Oikos OODA kernel (scheduler,
drift, decide, ledger, approvals). Its weakness is on the *narrative* side — the prose layer grew
organically and lacks the discipline wiki-hq shows:
- No enforceable **writing-style** standard (CONTRIBUTING.md has a one-line "voice" note; CAVEMAN.md governs agent *chat*, not docs).
- **Index/README files are inconsistent** — some pure listings, some prose+tables, some with stale counts.
- **No append-only operations log** for doc maintenance — doc changes are visible only in git.
- **Agent instructions and human content are interleaved** at the repo root (AGENTS.md, OIKOS.md, CAVEMAN.md, CONTRIBUTING.md, GLOSSARY.md alongside `containers/`, `infrastructure/`, …).
Decision (confirmed with operator): **full structural adoption**, **keep Mermaid**, adopt all four
borrows — writing-style guide, section-index/README pattern, append-only per-area logs, and
agent-instruction separation.
## Hard constraint: protect the operational substrate
These paths are read programmatically and **must not move** (see `mcp/server.py`, `bin/homelab`, `oikos/`):
- `inventory.yaml` (root) — MCP (`mcp/server.py:36`), CLI (`bin/homelab:36`), scheduler, drift, relations, gen-topology.
- `hosts/*.yaml` (root, generated) — `HOSTS_DIR` (`mcp/server.py:37`); written by `mcp/build_host_files.py`; read/written by `bin/homelab`.
- `oikos/` — kernel code, `oikos/cards/` (`explain` tool, `mcp/server.py:38`), `oikos/state.json`.
- `secrets/`, `secrets-issuance/`, `ssh/`, `scripts/`, `tools/`, `vps/`, `bin/`, `bootstrap.sh`.
Two MCP tools are path-agnostic and survive any narrative reorg: `search_docs` (ripgreps all
`*.md`) and `get_page(path)` (agent supplies a repo-relative path). The **`## Changelog` convention
must stay** on node pages — `get_changelog` (`mcp/server.py:214`) and the ledger parse it. New
`log.md` files are **additive**, not a replacement for per-page changelogs.
## Current → target structure
Restructure the **narrative layer only**; leave the substrate in place.
```
Homelab-Docs/
README.md # human landing page (kept; refreshed to new nav)
AGENTS.md # kept at root (conventional discovery path)
.agents/ # NEW — agent-facing instruction, separated from content
shared/
writing-style.md # NEW — adapted from wiki-hq (homelab voice)
llm-wiki.md # NEW — the sources/wiki/index/log contract for THIS repo
caveman.md # moved from CAVEMAN.md (agent chat mode)
page-templates.md # moved from CONTRIBUTING.md (page templates)
domains/
knowledge/schema.md # contract for the current-state wiki
operations/schema.md # contract for runbooks / investigations / plans
skills/ # runbooks reshaped as SKILL.md (Phase 4)
lifecycle-provision-node/SKILL.md
service-health-check/SKILL.md
...
OIKOS.md # operating-model doc (moved; agent-facing)
knowledge/ # durable current-state wiki (Feedback/authoritative layer)
index.md # pure listing → section indexes only
log.md # append-only doc-maintenance operations log
GLOSSARY.md # moved from root
sources/
index.md # flat catalog table
references/ # external docs (from infrastructure/references/)
wiki/
hosts/ README.md + hubris.md + strong.md
containers/ README.md (= today's index.md) + <id>-<name>.md
vms/ README.md + <id>-<name>.md
infrastructure/ README.md + grouped: network/ identity/ storage/ ingress/ operations/
operations/ # operator runbook narrative index
index.md commands.md agent-enrollment.md hermes-agent.md
investigations/ # incident evidence (Observe sources)
README.md + log.md + YYYY-MM-DD-*.md + archive/
plans/ # projects / design docs (Act)
README.md + YYYY-MM-DD-*.md + done/
# substrate — UNCHANGED (see Hard constraint)
inventory.yaml hosts/*.yaml oikos/ mcp/ secrets/ secrets-issuance/
ssh/ scripts/ tools/ vps/ bin/ bootstrap.sh
```
**hosts/ split:** generated `hosts/*.yaml` stay at root (substrate); only the two *narrative* pages
`hosts/hubris.md` and `hosts/strong.md` move to `knowledge/wiki/hosts/`. This is the one directory
where machine and narrative content currently mix.
## Consumers to update when narrative paths move
Same phase as the move:
- **`inventory.yaml` `doc_page` fields** — per-host pointer; consumed by `bin/homelab` (`die("no doc_page recorded…")` near `bin/homelab:1288`). Repoint to `knowledge/wiki/...`.
- **`oikos/gen-topology.py` / `oikos/gen_topology_lib.py`** — write `infrastructure/topology.md` and link `oikos/cards/` to doc pages. Move `topology.md` under `knowledge/wiki/infrastructure/` and update the output-path constant (`oikos/gen-topology.py:3`, `:49`, `:195`).
- **`oikos/cards/` templates** and `oikos/drift.py` docstring at `oikos/drift.py:218` (`containers/121-caddy.md`) — cosmetic, update for accuracy.
- **Internal cross-links** — repo-wide relative-link rewrite + link-check pass.
- **Same-session update rule** (`CONTRIBUTING.md:125`) — rewrite its path checklist to the new layout.
`search_docs`, `get_page`, `get_changelog` need **no** code change.
## Writing-style standard (adapted, not copied)
Create `.agents/shared/writing-style.md` from
`/Users/dtoro/Downloads/wiki-hq-main/.agents/shared/writing-style/writing-style.md`:
- **Keep:** banned-vocabulary list (puffers, "leverage/utilize/delve", poetic nouns, promotional adjectives), information-density rule, bold-leading-phrase pattern, tables-over-bullets, `>` blockquotes for caveats, one-sided cross-references, "cross-references are content not catalog".
- **Adjust voice:** wiki-hq mandates strict third-person "no you". Homelab docs are operator runbooks that already use imperative recipes. Allow **imperative/second-person in runbooks, recipes, and skills**; third-person reference voice for node/infrastructure descriptions. State this exception explicitly.
- **Clarify vs CAVEMAN.md:** Caveman governs agent *chat responses*; writing-style governs *committed docs*. No conflict.
- **Make it lint-checkable:** the Oikos lint surface (or a new `.agents/skills/docs-lint`) greps banned words + structural violations.
## Reconciling "append-only per-area logs" with the Oikos ledger
- The **Oikos change ledger** (`oikos/ledger.py`) stays authoritative for *infrastructure changes* (who/what/risk/approval/verification). Do not duplicate it.
- **Per-page `## Changelog`** stays (parsed by `get_changelog`).
- New **per-area `log.md`** (`knowledge/log.md`, `investigations/log.md`, `plans/log.md`) records *doc-maintenance operations only* — restructures, source ingests, lint sweeps — in wiki-hq's single-line format `## [YYYY-MM-DD] <op> | <summary>`.
## Convention: plans always live in `plans/`
The restructure must codify — in `.agents/domains/operations/schema.md`, `AGENTS.md`, and
`.agents/shared/page-templates.md` — that **any plan or design doc for the Homelab is always
written into the repo `plans/` folder** (`plans/YYYY-MM-DD-slug.md`), never into a scratch/agent
plan path, an ad-hoc location, or a chat message. Agents drafting a plan create the file under
`plans/`, list it in `plans/index.md`, and move it to `plans/done/` on completion. This rule is
stated once in the operations schema and cross-referenced from `AGENTS.md` so every agent sees it
at orientation.
## Section-index / README pattern
Every folder gets a `README.md`: 13 sentence prose intro, then a single two-column nav table
`| Document | What it covers |`, nothing else — no stale counts, no duplicated prose. Pure-listing
`index.md` files (e.g. `containers/index.md`) become the folder `README.md`. Root `README.md` stays
the human landing page (may exceed the strict pattern).
## Phased rollout
Each phase independently valuable, independently verifiable, committed separately.
1. **Conventions first (no moves).** Add `.agents/shared/writing-style.md`, `llm-wiki.md`; move CAVEMAN.md → `.agents/shared/caveman.md`, CONTRIBUTING.md → `.agents/shared/page-templates.md` (leave thin root stubs pointing to the new locations). Verify: links resolve; MCP untouched.
2. **Agent-instruction separation.** Move OIKOS.md → `.agents/OIKOS.md`; author `.agents/domains/{knowledge,operations}/schema.md`. Keep AGENTS.md at root, pointing at `.agents/`. Verify: AGENTS.md still the discovery entry; `tools/setup-hermes-soul.sh:51` references resolve or are updated.
3. **Narrative move into `knowledge/wiki/`.** Move `containers/`, `vms/`, `infrastructure/`, host narratives; add `knowledge/index.md`, `knowledge/log.md`, `knowledge/sources/`. Same commit: update `inventory.yaml` `doc_page` fields, `oikos/gen-topology.py` output path, all internal links. Verify: link-check clean; CLI `doc_page` lookups work; `gen-topology.py` writes to the new path; MCP `get_page`/`search_docs` return moved pages.
4. **Runbooks → skills.** Reshape `runbooks/*.md` into `.agents/skills/<name>/SKILL.md` (they already carry `risk_class`, `inputs`, `verification` frontmatter — near-compatible). Update runbook-path references. Verify: frontmatter parses; lifecycle transitions still map to `oikos/ontology.yaml`.
5. **Style + README pass.** Apply the writing-style standard and README pattern across narrative pages; add `log.md` entries recording the restructure. Verify: docs-lint clean.
## Verification (end to end)
- **Link integrity:** markdown link checker (or `rg` over `](` targets) after each move phase; zero broken relative links.
- **MCP resolves docs:** `HOMELAB_CONTEXT_DIR=<repo>``get_page("knowledge/wiki/containers/104-gitea.md")` returns content; `search_docs("gitea")` hits new paths; `get_changelog(<moved page>)` parses.
- **CLI doc_page lookups:** exercise `bin/homelab`'s `doc_page` read against a moved page — no "no doc_page recorded" error.
- **Topology generation:** run `oikos/gen-topology.py` — writes the new `topology.md` path; `oikos/cards/` links point at moved pages.
- **Substrate untouched:** `mcp/build_host_files.py` regenerates `hosts/*.yaml` identically; `git diff` shows no substrate churn.
- **Style lint:** banned-vocabulary grep over `knowledge/` returns no hits; every folder `README.md` matches the intro-plus-nav-table shape.
## Post-migration
Update `plans/index.md` to list this plan; on completion move it to `plans/done/`. Record the
restructure in the new `knowledge/log.md`. Update `README.md` and `AGENTS.md` navigation to the new
layout.
```