Phase 1-4: full doc reorg

Phase 1 — fix stale state after strong migration (Phase 1+2, 2026-07-05)
  - README: corrected IPs (jellyfin 206→246, arriman 132→245, etc.),
    added missing containers (128 trmnl, 129 house, 133 seanime, 134 romm,
    124 authentik), updated last-refreshed date, added strong host context
  - containers/101-jellyfin.md: IP 206→246, host hubris→strong, mount
    /mnt/library→/mnt/media_local, GPU 760M→680M+RX7600, privilege→priv
  - containers/118-elementsynapse.md: IP 239→242, added Host: strong
  - containers/122-arriman.md: IP 132→245, mount→/mnt/media_local, added Host
  - containers/129-house.md: IP 212→244, added Host: strong
  - containers/130-grimmory.md: IP 213→247, mount→/mnt/media_local, added Host
  - containers/121-caddy.md: fixed site list (books→grimmory, removed auth→VPS,
    added house, roms, teddy, trmnl)
  - hosts/strong.md: updated At-a-glance to reflect 7 LXCs hosted
  - containers/123-claudio-bot.md, 127-mule-photos-new.md: archived to
    containers/archive/ (were destroyed LXCs with living pages)
  - inventory.yaml: verified correct — no changes needed

Phase 2 — structural cleanup
  - infrastructure/index.md: one-page overview of all cross-cutting systems
  - runbooks/: moved runbook-budget-from-csv.md and runbook-dpkg-interrupted.md
    from operations/ with YAML frontmatter added
  - plans/done/: moved 4 completed plans out of active view; updated index
  - vms/index.md: added VM index page

Phase 3 — navigation & discoverability
  - GLOSSARY.md: term definitions (Authentik, Caddy, LXC, VAAPI, etc.)
  - README: added table of contents, links to glossary + infrastructure index
  - investigations/: archived 2 resolved cases (crash-loop, authentik-migration)
    to investigations/archive/; updated index with active vs archived sections

Phase 4 — ongoing discipline
  - CONTRIBUTING.md: documented same-session update rule with explicit checklist
  - README: replaced full LXC table with summary + link to containers/index.md
    (single source of truth; de-duplication)
This commit is contained in:
2026-07-06 00:45:28 +02:00
parent 205d8a1a43
commit fd35b48c8d
26 changed files with 255 additions and 58 deletions

View File

@@ -0,0 +1,443 @@
# Yuvomi deployment — `house.hubris.network`
Deploy [Yuvomi](https://yuvomi.cloud/) (previously Oikos) — a self-hosted
family planner with 14 modules (calendar, tasks, meals, groceries, budget,
documents, notes, etc). Single Docker container (Express.js + SQLCipher
SQLite), 256 MB RAM min.
**Target hostname:** `house.hubris.network` — publicly reachable via VPS
traefik, LAN reachable via Caddy.
**Integrations:**
- Authentik SSO (OIDC)
- Google Calendar (tokens exist on trmnl LXC 128)
- Paperless (Yuvomi's Documents module / clarification needed — see Phase 4)
---
## Phase 0 — Clarifications needed
### 0.1 Paperless connection
Yuvomi's "Documents" module stores documents inside its encrypted SQLite DB or
optionally on WebDAV. There is **no direct Paperless-ngx API connector** in
Yuvomi. Options:
a) **Keep as-is** — Yuvomi's docs are separate from Paperless, no integration
b) **WebDAV bridge** — Mount Paperless's consumption dir as WebDAV, point
Yuvomi doc storage there (Yuvomi stores newly uploaded docs directly in the
Paperless consume folder)
c) **Custom module** — Write a Yuvomi module that fetches from Paperless API
Decision needed before Phase 3 config.
**Decision:** WebDAV bridge (Phase 6.2).
### 0.2 Deployment target
Two options:
| Option | Pros | Cons |
|--------|------|------|
| **apps LXC (105)** — Docker already there, 4GB RAM, 2 cores | Zero provisioning, existing compose pattern | Shared with artifacto, MCP, secrets-issuance; Portainer-managed stacks can be tricky |
| **New LXC (~129)** — dedicated, clean | Isolated, no side-effects | Need to create, install Docker, wire into everything |
**Decision:** New LXC (129).
---
## Phase 1 — Provision new LXC (129) for Yuvomi
### 1.1 Create the LXC on hubris
```
ssh root@192.168.8.77 << 'EOF'
# Check available templates
pveam list local | grep debian
# Create unprivileged Debian 13 LXC (follows trmnl's unpriv pattern)
pct create 129 local:vztmpl/debian-13-standard_13.7-1_amd64.tar.zst \
--hostname house \
--description "Yuvomi family planner — house.hubris.network" \
--cores 1 \
--memory 1024 \
--swap 512 \
--rootfs local:8 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp,type=veth \
--unprivileged 1 \
--features nesting=1 \
--onboot 1 \
--start 1
EOF
```
Resources: 1 core / 1 GiB RAM / 8 GiB rootfs (generous for a single Express.js
container; can downsize later).
### 1.2 Set static IP and install Docker
After the LXC boots, find its DHCP lease, then set a static IP:
```
# Find actual IP
ssh root@192.168.8.77 'lxc-attach 129 -- ip addr show eth0 | grep inet'
# Reserve 192.168.8.212 (or whatever is free) via Technitium DHCP,
# or set static IP in PVE config:
ssh root@192.168.8.77 'pct set 129 --net0 name=eth0,bridge=vmbr0,ip=192.168.8.212/24,gw=192.168.8.1,type=veth'
ssh root@192.168.8.77 'lxc-attach 129 -- reboot'
```
### 1.3 Install Docker inside the LXC
```
ssh root@192.168.8.77 << 'DOCKER'
lxc-attach 129 -- bash -c '
apt-get update
apt-get install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
systemctl enable --now docker
docker --version
docker compose version
'
DOCKER
```
### 1.4 Download Yuvomi and start
```
ssh root@192.168.8.77 'lxc-attach 129 -- bash -c "
mkdir -p /opt/yuvomi /opt/yuvomi/data /opt/yuvomi/backups /opt/yuvomi/modules
cd /opt/yuvomi
curl -O https://raw.githubusercontent.com/ulsklyc/yuvomi/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/ulsklyc/yuvomi/main/.env.example
cp .env.example .env
"'
```
### 1.5 Generate keys and configure .env
```
ssh root@192.168.8.77 'lxc-attach 129 -- bash -c "
SESSION_SECRET=\$(openssl rand -hex 32)
DB_KEY=\$(openssl rand -hex 32)
cd /opt/yuvomi
sed -i \"s/SESSION_SECRET=.*/SESSION_SECRET=\$SESSION_SECRET/\" .env
sed -i \"s/DB_ENCRYPTION_KEY=.*/DB_ENCRYPTION_KEY=\$DB_KEY/\" .env
sed -i \"s/OIKOS_HTTP_PORT=3000/OIKOS_HTTP_PORT=3000/\" .env
sed -i \"s/# TZ=.*/TZ=Europe\\/Berlin/\" .env
echo \"SESSION_SECURE=true\" >> .env
echo \"TRUST_PROXY=1\" >> .env
echo \"BASE_URL=https://house.hubris.network\" >> .env
"'
```
### 1.6 Start Yuvomi
```
ssh root@192.168.8.77 'lxc-attach 129 -- bash -c "cd /opt/yuvomi && docker compose up -d"'
```
### 1.7 Verify
```
ssh root@192.168.8.77 'lxc-attach 129 -- curl -s http://127.0.0.1:3000/health'
# Expected: 200 OK
---
## Phase 2
### 2.1 Caddy — add `house.hubris.network`
Edit `/etc/caddy/Caddyfile` on LXC 121 (via `dtoro/caddy-conf` repo):
```
house.hubris.network {
tls {
dns ionos
}
reverse_proxy 192.168.8.212:3000
}
```
- Commit to `dtoro/caddy-conf` → auto-deploy via webhook
- If not yet deployed, push manually: `cd /etc/caddy && git add Caddyfile && git commit -m 'add house.hubris.network → yuvomi' && git push`
### 2.2 Verify LAN access
```
curl -sI https://house.hubris.network/
# Expected: 200 or 302 (redirect to /login or the setup wizard)
```
### 2.3 DNS — add Technitium record
Add A record `house.hubris.network → 192.168.8.175` (Caddy) on DNS LXC (107).
If using the DNS web UI: http://192.168.8.2/ → Zones → hubris.network → Add A record.
### 2.4 DNS mesh sync
If mesh DNS (Netbird managed zone) is in use, add the same record there or
verify dns-sync picks it up.
---
## Phase 3 — Public exposure (VPS traefik)
### 3.1 Add cert sync entry
On hubris (PVE host), edit `/usr/local/bin/hubris-public-cert-sync.sh`, add:
```bash
[house.hubris.network]="house.fullchain.crt house.privkey.key"
```
Run once:
```
systemctl start hubris-public-cert-sync.service
```
Verify certs landed on VPS:
```
ssh root@100.122.165.149 "ls -la /var/lib/docker/volumes/opt_netbird_traefik_letsencrypt/_data/house.*"
```
### 3.2 Add traefik router
On the VPS, edit `/opt/traefik-dynamic.yaml`:
```yaml
http:
routers:
house-public:
rule: 'Host(`house.hubris.network`)'
entryPoints:
- websecure
priority: 10
tls: {}
middlewares:
- house-ratelimit
service: house-public
middlewares:
house-ratelimit:
rateLimit:
average: 30
period: 1s
burst: 60
services:
house-public:
loadBalancer:
servers:
- url: 'http://192.168.8.212:3000'
tls:
certificates:
- certFile: /letsencrypt/house.fullchain.crt
keyFile: /letsencrypt/house.privkey.key
```
Restart traefik:
```
docker restart netbird-traefik
```
### 3.3 Verify public access
From outside the homelab LAN (or with `--resolve`):
```
curl -sI --resolve house.hubris.network:443:82.165.190.79 https://house.hubris.network/
# Expected: 200 or 302
echo | openssl s_client -connect 82.165.190.79:443 -servername house.hubris.network 2>&1 | openssl x509 -noout -subject
# Expected: CN=house.hubris.network (not TRAEFIK DEFAULT CERT)
```
---
## Phase 4 — Authentik SSO (OIDC)
### 4.1 Create OIDC provider in Authentik
Via VPS admin UI (`https://auth.hubris.network/if/admin/`):
- Applications → Providers → Create → OAuth2/OpenID Provider
- Name: `yuvomi`
- Client ID: auto-generated
- Client Secret: auto-generated (save this)
- Redirect URIs: `https://house.hubris.network/oauth2/callback`
- Signing Key: auto-generated
- Subject Mode: Based on User ID (or Based on Username — pick what Yuvomi expects)
### 4.2 Create application in Authentik
- Applications → Applications → Create
- Name: `Yuvomi`
- Slug: `yuvomi`
- Provider: select the one created above
- Launch URL: `https://house.hubris.network`
### 4.3 Set env vars in Yuvomi `.env`
On apps LXC (105), edit `/opt/yuvomi/.env`:
```
OIDC_ISSUER=https://auth.hubris.network/application/o/yuvomi/
OIDC_CLIENT_ID=<from Authentik>
OIDC_CLIENT_SECRET=<from Authentik>
# OIDC_TRUST_EMAIL_WITHOUT_VERIFIED_CLAIM=true # if Authentik doesn't send email_verified
```
### 4.4 Restart Yuvomi
```
pct exec 105 -- bash -c 'cd /opt/yuvomi && docker compose restart'
```
### 4.5 Verify SSO flow
Open `https://house.hubris.network/` — should redirect to Authentik login,
then back to Yuvomi.
---
## Phase 5 — Google Calendar
### 5.1 Extract tokens from trmnl LXC
On trmnl (LXC 128), the env file at `/etc/trmnl-plugins/env` contains:
```
GOOGLE_CLIENT_ID=119823214387-32f20ed3imesiv7uh5si7p3rou9fros4.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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
GOOGLE_REFRESH_TOKEN=1//03CS0rkuf7XVQCgYIARAAGAMSNwF-L9Irr5_b4kLhkx-dtf9EGQ1eJ1OnvxkaV_P1_4TDPwUN2lFb7nsbrZklN7qbjpkHpQyYlBY
```
### 5.2 Add Google Account redirect URI
In the Google Cloud Console (OAuth 2.0 Client IDs), add:
```
https://house.hubris.network/auth/google/callback
```
to the authorized redirect URIs for the existing client ID.
### 5.3 Set env vars in Yuvomi `.env`
```
GOOGLE_CLIENT_ID=119823214387-32f20ed3imesiv7uh5si7p3rou9fros4.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=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
```
Note: Yuvomi's Google Calendar integration uses the OAuth flow to get its own
refresh token — it doesn't reuse the trmnl refresh token. The first-time setup
in Yuvomi Settings → Calendar → Google Calendar will prompt for authorization.
### 5.4 Restart and verify
```
pct exec 105 -- bash -c 'cd /opt/yuvomi && docker compose restart'
```
Then in Yuvomi UI: Settings → Calendar → Connect Google Calendar → authorize.
---
## Phase 6 — Paperless integration (decide approach first)
### 6.1 If using as standalone documents module (no Paperless bridge)
No action needed. Yuvomi's Documents module works out of the box — docs stored
in encrypted SQLite.
### 6.2 If using WebDAV bridge to Paperless consumption
- Paperless consumes documents from `/mnt/library/documents/consume/`
- Point Yuvomi's WebDAV document storage at a WebDAV server serving that dir
- Options: run a lightweight WebDAV container on paperless LXC (103), or use
Nextcloud's WebDAV if documents are already in `/mnt/library`
Set env vars:
```
DOCUMENT_STORAGE_WEBDAV_ENABLED=true
DOCUMENT_STORAGE_WEBDAV_URL=http://192.168.8.130:8000/... # or WebDAV server
DOCUMENT_STORAGE_WEBDAV_USERNAME=...
DOCUMENT_STORAGE_WEBDAV_PASSWORD=...
DOCUMENT_STORAGE_WEBDAV_ALLOW_PRIVATE_NETWORK=true
```
### 6.3 If building a custom module
Write a Yuvomi module (client-side JS + module.json) that reads from
Paperless API at `https://paperless.hubris.network/api/` using a Paperless
API token. See `modules/MODULES.md` in the Yuvomi repo for the module format.
---
## Phase 7 — Backup & maintenance
### 7.1 Data persistence
Yuvomi stores everything in a single SQLCipher-encrypted SQLite file at
`/opt/yuvomi/data/oikos.db`. This is the only file needed for backup.
### 7.2 Add to homelab context
- Create `/opt/homelab-context/containers/129-yuvomi.md` (or `.../house.md`)
- Update `inventory.yaml` if using a new LXC
- Add changelog entries to caddy (121) and ingress docs
- Update `plans/index.md` → mark this plan `Done`
### 7.3 Schedule backup
Add a cron (or existing backup system) for `/opt/yuvomi/data/` if not already
covered by the host-level backup scheme.
---
## Summary of steps
| Phase | What | Who/Where |
|-------|------|-----------|
| 0 | Clarify Paperless approach + deployment target | dtoro |
| 1 | Docker Compose on apps LXC, start container | Hermes |
| 2 | Caddy block + DNS record for `house.hubris.network` | Hermes |
| 3 | VPS traefik router + cert sync for public exposure | Hermes |
| 4 | Authentik OIDC provider + env vars | Hermes (needs admin UI) |
| 5 | Google Calendar tokens + redirect URI | Hermes + dtoro (Google Cloud Console) |
| 6 | Paperless integration (depends on Phase 0 decision) | Hermes |
| 7 | Documentation, backup, inventory updates | Hermes |
---
## Duration estimate
| Phase | Time | Notes |
|-------|------|-------|
| Phase 1 | ~15 min | Download, config, startup |
| Phase 2 | ~10 min | Caddy + DNS |
| Phase 3 | ~15 min | VPS traefik + cert sync |
| Phase 4 | ~20 min | Authentik provider setup + env |
| Phase 5 | ~10 min + Google UI | Redirect URI takes 1 min in console |
| Phase 6 | TBD | Depends on chosen approach |
| Phase 7 | ~10 min | Doc + inventory updates |
| **Total** | **~1.5h + Phase 6** | |
## Rollback
If anything goes wrong:
```bash
# Stop and remove container
pct exec 105 -- bash -c 'cd /opt/yuvomi && docker compose down'
# Remove Caddy block, commit, push — auto-deploys
# Remove VPS traefik router, restart netbird-traefik
# Remove cert sync entry
# Remove DNS record
```