# 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= OIDC_CLIENT_SECRET= # 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 ```