Compare commits
23 Commits
claude/wiz
...
claude/jol
| Author | SHA1 | Date | |
|---|---|---|---|
| a45e4f6f29 | |||
| 77b3a6f677 | |||
| 41df77fb20 | |||
| b67389b137 | |||
| 45b7813eb7 | |||
| 8f6f43eeb1 | |||
| c2c257aa12 | |||
| 41fe34a3c3 | |||
| b6c9184a7b | |||
|
|
636c90f99c | ||
|
|
cef1b836c2 | ||
|
|
335498d069 | ||
|
|
867998e9c0 | ||
| cb0c1a5c0d | |||
| ddf541ebd3 | |||
| 4472e73ae1 | |||
| 59261c7ef1 | |||
| 41f22c2d85 | |||
| b7cfc350b3 | |||
| fffa5560eb | |||
| d6bfd7b8c9 | |||
| b71402aaed | |||
| 2a7876c711 |
32
.sops.yaml
32
.sops.yaml
@@ -24,7 +24,8 @@ creation_rules:
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/gitea-pat\.yaml$
|
||||
# Write-scoped Gitea PAT (dtoro user). Same recipient list as hello.yaml
|
||||
@@ -34,7 +35,8 @@ creation_rules:
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/gitea-tokens\.yaml$
|
||||
# Workstations only.
|
||||
@@ -55,7 +57,8 @@ creation_rules:
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/netbird-authentik-oidc\.yaml$
|
||||
# Authentik OIDC client secret for the netbird-dashboard provider.
|
||||
@@ -65,7 +68,8 @@ creation_rules:
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/netbird-pat\.yaml$
|
||||
# NetBird API Personal Access Token. Consumed by the dns-sync job on the
|
||||
@@ -86,5 +90,23 @@ creation_rules:
|
||||
age: >-
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/yuvomi-api-token\.yaml$
|
||||
# Named Bearer token for the Yuvomi REST API, consumed by yuvomi-mcp on
|
||||
# LXC 129 (house).
|
||||
age: >-
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
|
||||
- path_regex: ^secrets/hermes-house-users\.yaml$
|
||||
# Signal number → Yuvomi user_id mapping (PII). Consumed by hermesd on LXC 129.
|
||||
age: >-
|
||||
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
|
||||
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,
|
||||
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs,
|
||||
age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
# webhook noop 2026-05-20T18:16:57+02:00
|
||||
|
||||
File diff suppressed because one or more lines are too long
13
AGENTS.md
13
AGENTS.md
@@ -19,8 +19,8 @@ the operator to run `homelab client add <hostname>` from an existing client.
|
||||
- `/opt/homelab-context/inventory.yaml` — every host, LXC, VM, and workstation
|
||||
with their mesh addresses, roles, and service mappings. Treat this file as
|
||||
authoritative; anything you read in narrative pages should agree with it.
|
||||
- `/opt/homelab-context/infrastructure/mesh.md` — Netbird mesh state.
|
||||
Tailscale was decommissioned 2026-06-21; the fleet is Netbird-only.
|
||||
- `/opt/homelab-context/infrastructure/mesh.md` — Tailscale → Netbird state.
|
||||
Both meshes are accepted today; Netbird is preferred for new traffic.
|
||||
- `/opt/homelab-context/infrastructure/dns.md` — split-horizon DNS via
|
||||
dnsmasq on LXC 124. `*.hubris.network` resolves to 192.168.x.x on the LAN
|
||||
and to mesh addresses off-LAN.
|
||||
@@ -83,7 +83,12 @@ Grep is fine for browsing or when MCP is unreachable.
|
||||
- **Wiki updates**: same-session rule applies to any meaningful state change
|
||||
this client makes.
|
||||
|
||||
## 6. Auto-setup mechanism
|
||||
## 6. Communication mode
|
||||
|
||||
Read and apply `/opt/homelab-context/CAVEMAN.md` (if present). It defines the lab's
|
||||
terse-communication standard — drop filler, keep substance, use fragments.
|
||||
|
||||
## 7. Auto-setup mechanism
|
||||
|
||||
The homelab-context repo ships tooling that gets automatically installed
|
||||
on every client after `git pull`. This is handled by `tools/post-pull.sh`
|
||||
@@ -104,7 +109,7 @@ commit and push. All enrolled clients pick it up within 5 minutes.
|
||||
|
||||
To trigger sync manually: `sudo homelab sync` or wait for the 5-min timer.
|
||||
|
||||
## 7. When in doubt
|
||||
## 8. When in doubt
|
||||
|
||||
Run `homelab mcp search_docs <query>` or `homelab mcp get_host <name>`.
|
||||
The clone is the fallback; MCP is the index.
|
||||
|
||||
33
CAVEMAN.md
Normal file
33
CAVEMAN.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# CAVEMAN.md — communication mode for homelab agents
|
||||
|
||||
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
||||
|
||||
## Rules
|
||||
|
||||
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
||||
|
||||
Pattern: `[thing] [action] [reason]. [next step].`
|
||||
|
||||
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
||||
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
|
||||
|
||||
## Levels
|
||||
|
||||
- **lite** — no filler/hedging. Keep articles + full sentences. Professional but tight.
|
||||
- **full** (default) — drop articles, fragments OK, short synonyms. Classic caveman.
|
||||
- **ultra** — abbreviate prose words (DB/auth/config/req/res), strip conjunctions, arrows (X → Y). Code symbols/API names/errors: never abbreviate.
|
||||
|
||||
Switch: `/caveman lite|full|ultra`. Stop: "normal mode".
|
||||
|
||||
## Auto-Clarity
|
||||
|
||||
Drop caveman for: security warnings, irreversible actions, multi-step sequences where fragments risk misread, user confused/repeating. Resume after clear part done.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
|
||||
|
||||
---
|
||||
|
||||
Source: https://github.com/JuliusBrussee/caveman
|
||||
Copy to `~/.hermes/skills/` for Hermes Agent, or `~/.claude/projects/<name>/SKILL.md` for Claude Code.
|
||||
@@ -22,7 +22,7 @@ See the full table in [`containers/index.md`](containers/index.md). Quick links:
|
||||
| 102 | [nfs-export](containers/102-nfs-export.md) | 192.168.8.200 | NFSv4 re-export of /mnt/library for ZimaOS |
|
||||
| 103 | [paperless](containers/103-paperless.md) | 192.168.8.130 | Document mgmt |
|
||||
| 104 | [gitea](containers/104-gitea.md) | 192.168.8.121 | Git server |
|
||||
| 105 | [apps](containers/105-apps.md) | 192.168.8.205 | Docker host (Artifacto / Booklore / PlantUML / Portainer / WriteFreely) |
|
||||
| 105 | [apps](containers/105-apps.md) | 192.168.8.205 | Docker host (Artifacto / PlantUML / Portainer / WriteFreely) |
|
||||
| 114 | [nextcloud](containers/114-nextcloud.md) | 192.168.8.224 | Personal cloud |
|
||||
| 118 | [elementsynapse](containers/118-elementsynapse.md) | 192.168.8.239 | Matrix Synapse |
|
||||
| 119 | [sophia](containers/119-sophia.md) | 192.168.8.157 | Sophia |
|
||||
@@ -30,12 +30,12 @@ See the full table in [`containers/index.md`](containers/index.md). Quick links:
|
||||
| 121 | [caddy](containers/121-caddy.md) | 192.168.8.175 | Reverse proxy |
|
||||
| 122 | [arriman](containers/122-arriman.md) | 192.168.8.132 | Docker host (\*arr stack) |
|
||||
| 124 | [authentik](containers/124-authentik.md) | 192.168.8.180 | SSO + split-horizon DNS |
|
||||
| 126 | [plato](containers/126-plato.md) | 192.168.8.190 | Plato (notes/discovery workspace) |
|
||||
| 130 | [grimmory](containers/130-grimmory.md) | 192.168.8.213 | Digital library (Grimmory — fork of Booklore) |
|
||||
|
||||
### Cross-cutting infrastructure
|
||||
- [DNS — split-horizon](infrastructure/dns.md)
|
||||
- [Ingress — Caddy + VPS traefik](infrastructure/ingress.md)
|
||||
- [Mesh — Netbird overlay](infrastructure/mesh.md)
|
||||
- [Mesh — Tailscale → Netbird migration](infrastructure/mesh.md)
|
||||
- [Monitoring — Hermes health watchdog](infrastructure/monitoring.md)
|
||||
- [Media permissions — `media` GID 10000](infrastructure/media-permissions.md)
|
||||
- [SSH access](infrastructure/ssh-access.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ Dedicated, single-purpose LXC that re-exports `/mnt/library` over NFSv4 to clien
|
||||
- **LAN DNS:** `nfs-export.hubris.network` → `192.168.8.200` (direct, no Caddy)
|
||||
- **Privilege:** privileged (`unprivileged: 0`) + `lxc.apparmor.profile: unconfined` — required for `nfs-kernel-server`
|
||||
- **Resources:** 1 core / 512 MiB RAM / 2 GiB rootfs / 256 MiB swap
|
||||
- **Mounts:** host `/mnt/library` ↔ container `/mnt/library` (same path on both sides — matches the bind-mount convention used by jellyfin, paperless, arriman, nextcloud, mule-images, plato, apps)
|
||||
- **Mounts:** host `/mnt/library` ↔ container `/mnt/library` (same path on both sides — matches the bind-mount convention used by jellyfin, paperless, arriman, nextcloud, mule-images, apps)
|
||||
|
||||
## What it does
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ LXC has `/etc/hosts` override mapping `auth.hubris.network → 192.168.8.175` (r
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-24 — terminalito deploy webhook (id 12)
|
||||
Push webhook on `dtoro/terminalito` → `http://192.168.8.211:9797/deploy` ([trmnl (128)](128-trmnl.md)); `app.ini` `ALLOWED_HOST_LIST` extended with `192.168.8.211`. See [auto-deploy](../infrastructure/auto-deploy.md).
|
||||
|
||||
### 2026-04-28 — wiki entry created
|
||||
Initial documentation.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 105 — `apps`
|
||||
|
||||
Docker host for everything that doesn't justify its own LXC. Currently runs Artifacto, Booklore, PlantUML server, Portainer (and historically WriteFreely / blog), plus the [homelab-context distribution services](../infrastructure/homelab-context.md) (MCP + secrets-issuance) since 2026-05-20.
|
||||
Docker host for everything that doesn't justify its own LXC. Currently runs Artifacto, PlantUML server, Portainer (and historically WriteFreely / blog), plus the [homelab-context distribution services](../infrastructure/homelab-context.md) (MCP + secrets-issuance) since 2026-05-20. Booklore migrated to [grimmory (130)](130-grimmory.md) on 2026-06-29.
|
||||
|
||||
## At a glance
|
||||
- **Hostname:** `apps`
|
||||
@@ -15,7 +15,6 @@ Docker host for everything that doesn't justify its own LXC. Currently runs Arti
|
||||
| Hostname | Container | Backend port | Notes |
|
||||
| --------------------------------- | ---------------- | ------------ | ----- |
|
||||
| `docker.hubris.network` | Portainer | `:9443` | Native OAuth2 via Authentik. Trusted-origins requires hostname only (no scheme/port). |
|
||||
| `books.hubris.network` | Booklore | `:6060` | Native OIDC. Redirect URI `/oauth2-callback`. |
|
||||
| `artifacto.hubris.network` | Artifacto | `:3100` | Public `/p/*`, `/static/*`, `/healthz` exposed via [VPS traefik](../infrastructure/ingress.md). |
|
||||
| `blog.hubris.network` | WriteFreely | `:8080` | Native OIDC via `[oauth.generic]`. |
|
||||
| `git.hubris.network/_plantuml/*` | PlantUML server | `:8079` | Same-origin route from [gitea (104)](104-gitea.md). |
|
||||
@@ -46,11 +45,6 @@ Receiver at `/opt/artifacto-deploy/` (outside the app repo): `deploy.sh` + `webh
|
||||
### Portainer
|
||||
Native OAuth2 (Settings → Authentication → OAuth → Custom). Manual endpoints (no OIDC discovery). Uses `portainer-uid` custom-claim scope from Authentik. Container is **not** compose-managed — safe to `docker run` recreate; data lives in named volume `portainer_data`. CLI flag: `--trusted-origins docker.hubris.network` (hostname only — `IsTrustedOrigin` rejects strings containing `://`).
|
||||
|
||||
### Booklore
|
||||
Native OIDC via Authentik (Settings → OIDC). Redirect URI `/oauth2-callback` (NOT `/api/oidc`). Container needs `extra_hosts: auth.hubris.network:192.168.8.175`. **Edit via Portainer UI** if it's a Portainer-managed stack.
|
||||
|
||||
> ⚠️ **Never `docker compose up` Portainer-managed stacks from the host shell.** Portainer's compose state lives at `/var/lib/docker/volumes/portainer_data/_data/compose/<N>/`. Running `docker compose up -d <svc>` from the host triggers recreates of OTHER services in the stack and silently destroys bind-mounted data. **This wiped Booklore's mariadb data on 2026-04-22.** Use the Portainer UI editor for compose changes. See [mesh migration](../infrastructure/mesh.md#critical-never-docker-compose-up-portainer-managed-stacks) for the full warning.
|
||||
|
||||
### homelab-mcp (`/opt/homelab-mcp/`)
|
||||
FastMCP server (Python venv at `/opt/homelab-mcp/.venv`). Reads from
|
||||
`/opt/homelab-context/` (this LXC is itself an enrolled
|
||||
@@ -87,10 +81,9 @@ same key. Mesh+LAN source-IP gated via the `MESH_SUBNETS` env in
|
||||
- `/revoke` is admin-token-gated by `/etc/secrets-issuance/admin-token`;
|
||||
shreds the local key file and adds the hostname to the denylist.
|
||||
Called by `homelab client remove`.
|
||||
- Trust subnets today: `100.122.0.0/16` (Netbird), `192.168.8.0/24` (LAN).
|
||||
(The legacy Tailscale CGNAT range `100.64.0.0/10` was removed from
|
||||
`secrets-issuance`'s `MESH_SUBNETS` on 2026-06-21 when Tailscale was
|
||||
decommissioned; service restarted.) Tighten if the LAN gets untrusted devices.
|
||||
- Trust subnets today: `100.122.0.0/16` (Netbird), `100.64.0.0/10`
|
||||
(Tailscale), `192.168.8.0/24` (LAN). Tighten if the LAN gets
|
||||
untrusted devices.
|
||||
|
||||
#### Auto-deploy pipeline (secrets-issuance)
|
||||
Receiver at `/opt/secrets-issuance/secrets-issuance/deploy/webhook/`,
|
||||
@@ -114,6 +107,9 @@ Native OIDC via `[oauth.generic]` in `config/config.ini`. `host = https://auth.h
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-29 — Booklore migrated to Grimmory on LXC 130
|
||||
Booklore stack removed from Portainer. MariaDB dump taken first, then restored into [grimmory (130)](130-grimmory.md)'s fresh MariaDB. `books.hubris.network` Caddy backend updated to `192.168.8.213:6060`. Authentik OIDC provider updated to Public client type (PKCE) for Grimmory compatibility.
|
||||
|
||||
### 2026-05-20 — homelab-mcp + secrets-issuance live
|
||||
Two new services from the [homelab-context distribution plan](../infrastructure/homelab-context.md):
|
||||
`homelab-mcp.service` on `:9810` (MCP read+management surface) and
|
||||
|
||||
@@ -24,14 +24,12 @@ Authoritative split-horizon DNS for `hubris.network` on the LAN/mesh, plus recur
|
||||
- API: `http://192.168.8.2:5380/api/...` (token via `/api/user/login`). Zone was built via the API.
|
||||
|
||||
## Who points here
|
||||
- **NetBird mesh peers:** resolve `hubris.network` by **forwarding to Technitium** via the `home-lab-dns` nameserver group (`→ 192.168.8.2`, domain `hubris.network`, applied to all peers). The **NetBird managed DNS zone was removed 2026-06-21 (Phase 4)** — Technitium is now the single DNS source for the mesh too. This works because: (a) roaming peers (`Core`) have the `192.168.8.0/24` route to reach `192.168.8.2` (added 2026-06-21), and (b) clients run NetBird **0.71.x** — on the old 0.68.3 client, forwarding reported `Available` but didn't serve queries, and the resolver cache (`100.122.255.254`) wouldn't clear on `down/up`; a `netbird service restart` (or app toggle) clears it. See [dns.md changelog 2026-06-21](../infrastructure/dns.md).
|
||||
- **NetBird mesh peers:** resolve via the **NetBird managed DNS zone**, kept in sync *from* this Technitium (see dns-sync below). The `home-lab-dns` nameserver group (`→ 192.168.8.2`) is a thin fallback forwarder.
|
||||
- **Homelab DHCP clients:** Technitium's own DHCP scope hands out `192.168.8.2` as the DNS server for `192.168.8.x` leases (see DHCP section below).
|
||||
- **Plain LAN clients (`192.168.178.x`):** Fritz!Box DHCP still hands out Fritz!Box itself (`192.168.178.1`) as DNS, **but** the Fritz!Box now *forwards* upstream to Technitium — DNSv4 server set to `192.168.8.2` (Internet → Filter → DNS Server, 2026-06-17). So household clients get split-horizon `*.hubris.network` answers via Fritz!Box→Technitium, with **no NetBird dependency**. (This is the change that decoupled the on-prem tier from the mesh — see [dns.md](../infrastructure/dns.md) 2026-06-17.)
|
||||
- **Plain LAN clients (`192.168.178.x`):** Fritz!Box DHCP still hands out Fritz!Box itself (`192.168.178.1`) as DNS — no split-horizon for non-mesh clients. Changing this requires a secondary DNS fallback, which Fritz!OS 8.x doesn't expose in a single DHCP field.
|
||||
|
||||
## dns-sync (RETIRED 2026-06-21 — Phase 4 complete)
|
||||
**The managed-zone sync is no longer scheduled.** `/opt/dns-sync/sync.py` reconciled this zone's named A-records → the NetBird managed DNS zone; the `*/10` cron (`/etc/cron.d/dns-sync`) was **removed 2026-06-21** when the managed zone was retired. Technitium is now the **single** DNS source — mesh peers forward to it (see "Who points here" above), LAN/household clients query it directly.
|
||||
|
||||
The script + token + a pre-deletion record backup remain at `/opt/dns-sync/` **as an emergency-restore tool only**: running `python3 /opt/dns-sync/sync.py` once re-creates the managed zone from Technitium (used during the Phase 4 rollback). Do not re-add the cron unless reverting Phase 4. Tracked: [scripts/dns-sync.py](../scripts/dns-sync.py).
|
||||
## dns-sync (Technitium = authoring source)
|
||||
`/opt/dns-sync/sync.py` (cron `*/10`, logs `/var/log/dns-sync.log`) reconciles this zone's named A-records → the NetBird managed DNS zone via the NetBird API (`/api/dns/zones/{id}/records`). Token at `/opt/dns-sync/netbird-token` (mode 600; source of truth in sops `secrets/netbird-pat.yaml`). **Edit DNS only here**; the sync propagates to the mesh. It deletes NetBird records absent from Technitium. Tracked: [scripts/dns-sync.py](../scripts/dns-sync.py). *Why this exists:* NetBird won't forward to Technitium for mesh peers (self-IP / nameserver-group quirks), so we sync into the managed zone instead — see [dns.md](../infrastructure/dns.md).
|
||||
|
||||
## DHCP
|
||||
|
||||
@@ -50,6 +48,9 @@ Replaces the DHCP that was previously served by the Slate AX router. Static-IP L
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-24 — A record `trmnl.hubris.network → 192.168.8.175`
|
||||
Added for [trmnl (128)](128-trmnl.md) (LAN path via [Caddy (121)](121-caddy.md)); propagated to the NetBird managed zone by `dns-sync`.
|
||||
|
||||
### 2026-06-06 — dns-sync cron installed (had been missing since deployment)
|
||||
Although the 2026-06-03 changelog claimed "cron */10", **no crontab was actually configured** on the LXC. The sync was running only via ad-hoc manual invocations during incident debugging. Fixed by adding `/etc/cron.d/dns-sync`.
|
||||
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
# 126 — `plato`
|
||||
|
||||
Docker host for [Plato](https://git.hubris.network/dtoro/Plato) — a cross-linked notes workspace (SvelteKit SPA embedded into a Go HTTP server, SQLite-backed). LAN+mesh only, no public ingress.
|
||||
|
||||
## At a glance
|
||||
- **Hostname:** `plato`
|
||||
- **IP:** `192.168.8.190`
|
||||
- **Privilege:** privileged
|
||||
- **Resources:** 2 cores / 2 GiB RAM / 8 GiB rootfs / 1 GiB swap
|
||||
- **Mounts:** host `/mnt/library/documents/plato` ↔ container `/opt/plato/data`
|
||||
- **Public hostname:** [`plato.hubris.network`](../infrastructure/dns.md) → [caddy (121)](121-caddy.md) → `192.168.8.190:8080`
|
||||
|
||||
## Stack
|
||||
|
||||
Single-container deploy. The repo's `Dockerfile` is a three-stage build (Node → Go → distroless/static-debian12:nonroot, ~23 MiB final image). The container exposes `:8080` and writes its SQLite db to `/data`.
|
||||
|
||||
- **Checkout:** `/opt/plato/app` (clone of `http://192.168.8.121:3000/dtoro/Plato.git`, using the cached gitea PAT in `/root/.git-credentials` — same pattern as [caddy (121)](121-caddy.md)).
|
||||
- **Data:** host `/mnt/library/documents/plato` (owned `65532:65532` to match the distroless nonroot UID) bind-mounted into the LXC at `/opt/plato/data`, then bound into the container at `/data` via a `docker-compose.override.yml`:
|
||||
```yaml
|
||||
services:
|
||||
plato:
|
||||
volumes: !override
|
||||
- /opt/plato/data:/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
- **`.env`** at `/opt/plato/app/.env` (optional, untracked) — LLM provider keys (`OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, etc.) and `PLANTUML_BASE_URL` override. Absent by default; LLM features stay greyed out, PlantUML defaults to the public service.
|
||||
- **Run / update:** push to `dtoro/Plato` (auto-deploys, see below) or `cd /opt/plato/app && git pull && docker compose up -d --build` for a manual rebuild.
|
||||
|
||||
## Auto-deploy
|
||||
|
||||
Push to `dtoro/Plato` `main` triggers a rebuild — same Shape B pattern as [Artifacto / mule-image](../infrastructure/auto-deploy.md). Webhook receiver at `/opt/plato-deploy/`, systemd unit `plato-deploy-webhook.service`, port `9799`, gitea hook id 8.
|
||||
|
||||
- Receiver: `http://192.168.8.190:9799/deploy`, signed payload (HMAC-SHA256, secret in `/etc/plato-deploy/secret`).
|
||||
- Logs: `journalctl -u plato-deploy-webhook -f`.
|
||||
- Health: `curl http://127.0.0.1:9799/health`.
|
||||
- Manual deploy: `/opt/plato-deploy/deploy.sh`.
|
||||
- Gitea's `app.ini` `ALLOWED_HOST_LIST` was extended with `192.168.8.190` to allow this delivery.
|
||||
|
||||
## Fresh-DB bootstrap workaround
|
||||
|
||||
The `schema` constant in `backend/internal/views/store.go` (as of commit `e0542c0`) creates the `views` table without `project_id`, then immediately runs `CREATE UNIQUE INDEX … ON views(project_id, lower(title))`. On a fresh DB this fails (no such column) and Plato crash-loops with `open views store: SQL logic error: no such column: project_id`. `ensureProjectIDColumn()` adds the column on subsequent migrations, but the schema apply happens first.
|
||||
|
||||
Until the upstream fix lands, pre-seed the DB before first start:
|
||||
|
||||
```
|
||||
docker compose stop
|
||||
rm -f /opt/plato/data/plato.db
|
||||
python3 - <<'PY'
|
||||
import sqlite3
|
||||
c = sqlite3.connect('/opt/plato/data/plato.db')
|
||||
c.executescript("""
|
||||
CREATE TABLE views (
|
||||
id TEXT PRIMARY KEY,
|
||||
type TEXT NOT NULL DEFAULT 'document',
|
||||
title TEXT NOT NULL,
|
||||
aliases TEXT NOT NULL DEFAULT '[]',
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE UNIQUE INDEX views_project_title_lower ON views(project_id, lower(title));
|
||||
CREATE INDEX views_project_id ON views(project_id);
|
||||
""")
|
||||
c.commit()
|
||||
PY
|
||||
chown 65532:65532 /opt/plato/data/plato.db
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Once the column exists, every subsequent boot's `IF NOT EXISTS` clauses no-op. Once Plato is fixed upstream (remove the `CREATE UNIQUE INDEX` line from the boot `schema` constant — `ensureTitleIndexPerProject()` already re-creates it after the migration), this preseed becomes unnecessary.
|
||||
|
||||
## Why privileged
|
||||
|
||||
Matches the docker-host convention used by [120 mule-images](120-mule-images.md) and [122 arriman](122-arriman.md). Distroless nonroot's UID `65532` on the host bind mount maps directly through; unprivileged would shift the UID by the idmap offset and the container couldn't write `/data` without extra plumbing.
|
||||
|
||||
## Caddy
|
||||
|
||||
```
|
||||
plato.hubris.network {
|
||||
tls {
|
||||
dns ionos {env.IONOS_AUTH_API_TOKEN}
|
||||
}
|
||||
reverse_proxy 192.168.8.190:8080
|
||||
}
|
||||
```
|
||||
|
||||
No Authentik forward-auth — Plato has no auth model yet; access control is "be on the LAN or the mesh".
|
||||
|
||||
## DNS
|
||||
|
||||
dnsmasq entry on [124-authentik](124-authentik.md):
|
||||
```
|
||||
address=/plato.hubris.network/192.168.8.175
|
||||
```
|
||||
|
||||
## Related
|
||||
- [Caddy (121)](121-caddy.md)
|
||||
- [DNS (split-horizon)](../infrastructure/dns.md)
|
||||
- [Media permissions](../infrastructure/media-permissions.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-05-13 — auto-deploy wired
|
||||
Shape B pipeline added (`/opt/plato-deploy/`, port `9799`, gitea hook id 8). `ALLOWED_HOST_LIST` in gitea `app.ini` extended with `192.168.8.190`. See [auto-deploy](../infrastructure/auto-deploy.md#plato).
|
||||
|
||||
### 2026-05-13 — container created, Plato deployed
|
||||
LXC 126 stood up on Debian 12 standard, privileged, docker-ce installed. Plato cloned from `dtoro/Plato`, built and started. Caddy site and dnsmasq split-horizon entry added. Recycled the IP/ID slot freed earlier the same day by the [decommissioned Seafile experiment (LXC 125)](index.md#recently-destroyed-kept-for-archaeology). Hit the [fresh-DB bootstrap bug](#fresh-db-bootstrap-workaround) on first boot; worked around by pre-seeding the SQLite schema.
|
||||
48
containers/128-trmnl.md
Normal file
48
containers/128-trmnl.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 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
|
||||
Wired — [auto-deploy](../infrastructure/auto-deploy.md) Shape B, webhook id 12 on `dtoro/terminalito` → `http://192.168.8.211:9797/deploy` (`terminalito-deploy.service`). Push to `main` → `server/deploy/deploy.sh` (`git pull` + pip + reinstall units + restart `trmnl-plugins`). Secret `/etc/terminalito-deploy/secret`; git creds `/etc/terminalito-deploy/git-credentials` wired as a repo-local `credential.helper`. Manual: `pct exec 128 -- /opt/terminalito/server/deploy/deploy.sh`.
|
||||
|
||||
## 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) — Technitium A record `trmnl → 192.168.8.175` (LAN path via Caddy)
|
||||
- [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 — auto-deploy + LAN DNS wired
|
||||
Gitea Shape-B deploy pipeline (webhook id 12, `:9797`) — push to `dtoro/terminalito` redeploys; verified end-to-end. Technitium A record `trmnl.hubris.network → 192.168.8.175` added on [dns (107)](107-dns.md) (propagated to the NetBird managed zone via dns-sync), so LAN clients take the short path through [Caddy (121)](121-caddy.md). See [auto-deploy](../infrastructure/auto-deploy.md).
|
||||
|
||||
### 2026-06-24 — public path live
|
||||
Verified end-to-end from the internet: `https://trmnl.hubris.network/munich-home/dashboard` → 200 with token, 401 without; `/health` 200. The provision-time outage was the netbird `home-lab-network` (192.168.8.0/24) route having no active routing peer — the **mac-mini routing peer's netbird daemon was down** (artifacto/blog were 504 too). Bringing netbird up on mac-mini restored the route; the edge recovered with no config change. See [ingress](../infrastructure/ingress.md) / [mesh](../infrastructure/mesh.md).
|
||||
|
||||
### 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.
|
||||
51
containers/129-house.md
Normal file
51
containers/129-house.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 129 — `house`
|
||||
|
||||
Yuvomi family planner (formerly Oikos). Self-hosted family planner with 14 modules: calendar, tasks, meals, groceries, budget, documents, notes, contacts, birthdays, housekeeping, recipes, reminders.
|
||||
|
||||
## At a glance
|
||||
|
||||
- **Hostname:** `house`
|
||||
- **IP:** `192.168.8.212` (static)
|
||||
- **Privilege:** unprivileged
|
||||
- **Resources:** 1 core / 1344 MiB RAM / 8 GiB rootfs (Debian 13)
|
||||
- **Mounts:** none
|
||||
- **Public hostname:** [`house.hubris.network`](../infrastructure/ingress.md) → VPS traefik → Caddy
|
||||
|
||||
## Service / port map
|
||||
|
||||
| Service | Listen | Notes |
|
||||
|---------|--------|-------|
|
||||
| `oikos` (Yuvomi) | `0.0.0.0:3000` | Docker Compose at `/opt/yuvomi/`, image `ghcr.io/ulsklyc/yuvomi` |
|
||||
|
||||
## Integrations
|
||||
|
||||
- **Authentik SSO (OIDC):** Provider `Provider for Yuvomi` (PK 31) in Authentik on VPS. Env vars in `/opt/yuvomi/.env`: `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`. Redirect URI: `https://house.hubris.network/auth/oidc/callback`.
|
||||
- **Paperless DMS connector (native):** Yuvomi connects directly to Paperless-ngx API at `http://192.168.8.130:8000/`. API token stored in SQLite `dms_accounts` table. Search, link, and upload documents from Yuvomi to Paperless via Settings → Documents → DMS.
|
||||
- **Weather widget:** Open-Meteo (free, no API key). Munich coordinates set.
|
||||
- **Google Calendar:** OAuth configured via env vars (`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI`). Redirect URI: `https://house.hubris.network/api/v1/calendar/google/callback`. Authorize in Settings → Calendar → Connect Google Calendar.
|
||||
|
||||
## Config paths
|
||||
|
||||
- `/opt/yuvomi/docker-compose.yml` — downloaded from upstream
|
||||
- `/opt/yuvomi/.env` — config including secrets (untracked)
|
||||
- `/opt/yuvomi/data/` — SQLCipher SQLite DB (`oikos.db`)
|
||||
- `/opt/yuvomi/backups/` — auto backups
|
||||
- `/opt/yuvomi/modules/` — Yuvomi modules (empty for now)
|
||||
|
||||
## Related
|
||||
|
||||
- [Caddy (121)](121-caddy.md) — LAN reverse proxy (`house.hubris.network → 192.168.8.212:3000`)
|
||||
- [VPS ingress](../infrastructure/ingress.md) — public edge (cert mirror + traefik router)
|
||||
- [DNS (107)](107-dns.md) — Technitium A record `house → 192.168.8.175` (LAN path via Caddy)
|
||||
- [Paperless (103)](103-paperless.md) — native DMS connector (API at `:8000`)
|
||||
- [TRMNL (128)](128-trmnl.md) — Google Calendar tokens source
|
||||
- [Deployment plan](../plans/2026-06-25-yuvomi-deployment.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-27 — Google Calendar OAuth env vars configured
|
||||
`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI` set in `.env`. New OAuth client ID (`-bho4iq..`).
|
||||
|
||||
### 2026-06-26 — provisioned
|
||||
|
||||
LXC 129 created (Debian 13, unprivileged, `192.168.8.212`). Docker installed. Yuvomi container running on `:3000` from `ghcr.io/ulsklyc/yuvomi:latest`. Caddy block + DNS A record + VPS traefik router `house-public` for public access. Authentik OIDC provider created (PK 31). WebDAV document bridge on paperless LXC (103) at `:8088` for Paperless auto-import.
|
||||
65
containers/130-grimmory.md
Normal file
65
containers/130-grimmory.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# 130 — `grimmory`
|
||||
|
||||
Self-hosted digital library (eBooks, comics, audiobooks). Community fork/successor of Booklore, with smart shelves, metadata enrichment, Kobo/KOReader sync, OPDS, and a built-in EPUB/PDF reader. Migrated from [apps (105)](105-apps.md) on 2026-06-29.
|
||||
|
||||
## At a glance
|
||||
|
||||
- **Hostname:** `grimmory`
|
||||
- **IP:** `192.168.8.213` (static, set in PVE `net0` config — same pattern as all other LXCs)
|
||||
- **Privilege:** privileged (UID = host UID for `/mnt/library` media GID)
|
||||
- **Resources:** 1 core / 2 GiB RAM / 16 GiB rootfs (Debian 13)
|
||||
- **Mounts:** `/mnt/library`
|
||||
- **Public hostname:** `books.hubris.network`
|
||||
|
||||
## Service / port map
|
||||
|
||||
| Service | Listen | Notes |
|
||||
|---------|--------|-------|
|
||||
| Grimmory | `192.168.8.213:6060` | Docker Compose at `/opt/grimmory/` |
|
||||
| MariaDB | internal only | Sidecar in the same compose stack |
|
||||
|
||||
## Compose
|
||||
|
||||
Located at `/opt/grimmory/docker-compose.yml`. Key points:
|
||||
|
||||
- Image: `ghcr.io/grimmory-tools/grimmory:latest`
|
||||
- Books library: `/mnt/library/books` → `/books` (read-write; media GID 10000 via `GROUP_ID=10000`)
|
||||
- Bookdrop (watched folder for auto-import): `/opt/grimmory/bookdrop` → `/bookdrop`
|
||||
- App data (covers, DB config): `/opt/grimmory/data` → `/app/data`
|
||||
- MariaDB config: `/opt/grimmory/mariadb/config` → `/config` (linuxserver/mariadb image)
|
||||
- `extra_hosts: auth.hubris.network:192.168.8.175` — routes Authentik OIDC discovery to Caddy from inside the container
|
||||
- `FORCE_DISABLE_OIDC=false` — OIDC stays enabled; provider configured via Grimmory admin UI
|
||||
|
||||
Credentials live in `/opt/grimmory/.env` (untracked):
|
||||
- `DATABASE_PASSWORD` / `MYSQL_PASSWORD` — MariaDB Grimmory user password
|
||||
- `MYSQL_ROOT_PASSWORD` — MariaDB root password
|
||||
|
||||
## Authentik OIDC
|
||||
|
||||
Uses Confidential client (client secret stored in Grimmory's DB — migrated from Booklore). The OIDC config carried over in the database dump; no manual re-entry needed.
|
||||
|
||||
- **Authentik provider:** `Provider for Grimmory` (renamed from `Provider for Booklore` on migration)
|
||||
- **Client ID:** `L1u0eFsNhbKgiIvvFeIr2mvZdbtFyzidCq2h6thL`
|
||||
- **Client type:** Confidential (client secret in `oidc_provider_details` in MariaDB `app_settings`)
|
||||
- **Redirect URI:** `https://books.hubris.network/oauth2-callback`
|
||||
- **Scopes:** openid, profile, email, offline_access
|
||||
- **Back-channel logout:** `http://192.168.8.213:6060/api/v1/auth/oidc/backchannel-logout`
|
||||
- **Application slug:** `booklore` → Issuer URI: `https://auth.hubris.network/application/o/booklore/`
|
||||
|
||||
## Media permissions
|
||||
|
||||
LXC is privileged → in-container UID = host UID. Docker container gets media GID via `GROUP_ID=10000` env var (Grimmory/linuxserver pattern). The `/mnt/library/books` subtree is owned `:media` mode `2775` (setgid). See [media-permissions](../infrastructure/media-permissions.md).
|
||||
|
||||
## Related
|
||||
|
||||
- [apps (105)](105-apps.md) — previous host (Booklore)
|
||||
- [Caddy (121)](121-caddy.md) — `books.hubris.network → 192.168.8.213:6060`
|
||||
- [Authentik (124)](124-authentik.md) — OIDC provider `Grimmory`
|
||||
- [DNS (107)](107-dns.md) — `books.hubris.network → 192.168.8.175` (unchanged from Booklore)
|
||||
- [Media permissions](../infrastructure/media-permissions.md)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-29 — provisioned; Booklore migrated
|
||||
|
||||
LXC 130 created (Debian 13, privileged, `192.168.8.213`). Docker installed. Grimmory compose deployed at `/opt/grimmory/`. MariaDB dump from Booklore (LXC 105) restored — schema-compatible since Grimmory is a direct fork. Caddy `books.hubris.network` backend updated from `192.168.8.205:6060` to `192.168.8.213:6060`. Authentik provider updated: Booklore → Grimmory, Confidential → Public (PKCE). Booklore stack removed from Portainer on LXC 105.
|
||||
112
containers/131-teddycloud.md
Normal file
112
containers/131-teddycloud.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# 131 — `teddycloud`
|
||||
|
||||
Open-source replacement server for Toniebox smart audio devices (Tonieboxes). Serves device content and API on port 443 and exposes a management web UI at `teddy.hubris.network`.
|
||||
|
||||
## At a glance
|
||||
|
||||
- **Hostname:** `teddycloud`
|
||||
- **IP:** `192.168.8.243` (DHCP reservation; MAC `bc:24:11:11:7a:df`)
|
||||
- **Privilege:** privileged
|
||||
- **Resources:** 1 core / 1 GiB RAM / 16 GiB rootfs (Debian 12)
|
||||
- **Mounts:** `/mnt/library` (`mp0`) — TeddyCloud content at `/mnt/library/cloud/leon`
|
||||
- **Public hostname:** none (LAN-only)
|
||||
|
||||
## Role
|
||||
|
||||
Replaces the Boxine cloud (`prod.de.bb-online.com`) as the backend for Leon's Toniebox. Tonieboxes connect on port 443 using a custom CA cert issued by TeddyCloud. Content (Tonies) is stored on the NAS at `/mnt/library/cloud/leon` and is accessible from the management UI.
|
||||
|
||||
## Service / port map
|
||||
|
||||
| Service | Listen | Notes |
|
||||
|---------|--------|-------|
|
||||
| TeddyCloud device API | `0.0.0.0:443` | HTTPS, TeddyCloud self-signed CA, Toniebox connects here |
|
||||
| TeddyCloud HTTP | `0.0.0.0:80` | Redirects to 443 |
|
||||
| TeddyCloud web UI | `0.0.0.0:8443` | HTTPS management UI — fronted by Caddy at `teddy.hubris.network` (backend uses `tls_insecure_skip_verify` for self-signed cert on LAN hop) |
|
||||
|
||||
## Docker Compose
|
||||
|
||||
`/opt/teddycloud/docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
teddycloud:
|
||||
image: ghcr.io/toniebox-reverse-engineering/teddycloud:latest
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8443:8443"
|
||||
volumes:
|
||||
- certs:/teddycloud/certs
|
||||
- config:/teddycloud/config
|
||||
- /mnt/library/cloud/leon:/teddycloud/content
|
||||
- /mnt/library/cloud/leon:/teddycloud/library
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
certs:
|
||||
config:
|
||||
```
|
||||
|
||||
`certs` and `config` are Docker named volumes (runtime state). `content` and `library` are bind-mounted from `/mnt/library/cloud/leon` so audio content persists across container rebuilds and is browsable from the host.
|
||||
|
||||
## Storage / config paths
|
||||
|
||||
- `/opt/teddycloud/docker-compose.yml` — compose file
|
||||
- Docker volume `teddycloud_certs` — TeddyCloud CA + server certs (generated on first boot)
|
||||
- Docker volume `teddycloud_config` — TeddyCloud config
|
||||
- `/mnt/library/cloud/leon/` — Tonie content + library (NAS bind mount)
|
||||
|
||||
## Networking
|
||||
|
||||
Two separate traffic paths — different IPs, no port 443 conflict:
|
||||
|
||||
**Management UI (browser):**
|
||||
```
|
||||
teddy.hubris.network → Technitium → 192.168.8.175 (Caddy) → 192.168.8.243:8443
|
||||
```
|
||||
|
||||
**Toniebox device traffic:**
|
||||
```
|
||||
prod.de.bb-online.com → Technitium override → 192.168.8.243:443 (TeddyCloud direct)
|
||||
```
|
||||
|
||||
Caddy terminates TLS for the management UI (IONOS DNS-01 wildcard cert). TeddyCloud terminates TLS for device traffic with its own self-signed CA — the Toniebox must have this CA installed.
|
||||
|
||||
### DNS overrides in Technitium
|
||||
|
||||
| Record | Type | Value | Purpose |
|
||||
|--------|------|-------|---------|
|
||||
| `teddy.hubris.network` | A | `192.168.8.175` | Management UI → Caddy (standard pattern) |
|
||||
| `prod.de.bb-online.com` | A | `192.168.8.243` | Toniebox device traffic → TeddyCloud direct |
|
||||
|
||||
The `prod.de.bb-online.com` override is Technitium-only — it intercepts Toniebox DNS locally without touching public DNS. The `dns-sync.py` cron on LXC 107 skips non-`hubris.network` records, so it stays local.
|
||||
|
||||
## Config notes
|
||||
|
||||
- `core.boxCertAuth=false` — client cert validation disabled. The box connects without presenting its unique client cert. Set in `/var/lib/docker/volumes/teddycloud_config/_data/config.ini` (TeddyCloud hot-reloads on change).
|
||||
- If you ever want per-box auth, flip to `true` and supply `certs/client/ca.der`, `client.der`, `private.der` extracted from the box flash.
|
||||
|
||||
## Toniebox onboarding — ESP32 SD card method
|
||||
|
||||
Leon's box is ESP32 generation. No hardware mod required.
|
||||
|
||||
1. Download the TeddyCloud CA cert from the web UI: **Security → CA Certificate → Download CA** (`ca.der`).
|
||||
2. Power off the Toniebox, remove the SD card.
|
||||
3. On the SD card, create folder `cert/` at the root.
|
||||
4. Copy the downloaded `ca.der` into `cert/ca.der` on the SD card.
|
||||
5. Reinsert SD card, power on the box.
|
||||
6. The box patches itself to trust TeddyCloud's CA, then resolves `prod.de.bb-online.com` via Technitium's override (`192.168.8.243`) and connects on port 443.
|
||||
|
||||
Reference: [upstream wiki — ESP32 SD card method](https://github.com/toniebox-reverse-engineering/teddycloud/wiki).
|
||||
|
||||
## Related
|
||||
|
||||
- [Caddy (121)](121-caddy.md) — LAN reverse proxy (`teddy.hubris.network → 192.168.8.243:8443`)
|
||||
- [DNS (107)](../infrastructure/dns.md) — Technitium A records for `teddy.hubris.network` and `prod.de.bb-online.com`
|
||||
- [Media permissions](../infrastructure/media-permissions.md) — NAS `/mnt/library` mount pattern
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-29 — provisioned
|
||||
|
||||
LXC 131 created (Debian 12, privileged, nesting=1). Docker installed. TeddyCloud running via Docker Compose at `/opt/teddycloud/`. Content bind-mounted from `/mnt/library/cloud/leon`. Caddy block added at `teddy.hubris.network → :8443`. Technitium A records: `teddy.hubris.network → 192.168.8.175` (Caddy), `prod.de.bb-online.com → 192.168.8.243` (device traffic direct).
|
||||
@@ -7,7 +7,7 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
||||
| 101 | [jellyfin](101-jellyfin.md) | 192.168.8.206 | unpriv (idmap) | 2 | 4 GiB | 16 GiB | `/mnt/library` | `media.hubris.network` | running |
|
||||
| 103 | [paperless](103-paperless.md) | 192.168.8.130 | priv | 2 | 3 GiB | 8 GiB | `/mnt/library` | `paperless.hubris.network` | running |
|
||||
| 104 | [gitea](104-gitea.md) | 192.168.8.121 | priv | 1 | 1 GiB | 8 GiB | `/mnt/library` | `git.hubris.network` | running |
|
||||
| 105 | [apps](105-apps.md) | 192.168.8.205 | priv | 2 | 4 GiB | 30 GiB | `/mnt/library` | `docker` / `books` / `artifacto` / `blog` | running |
|
||||
| 105 | [apps](105-apps.md) | 192.168.8.205 | priv | 2 | 4 GiB | 30 GiB | `/mnt/library` | `docker` / `artifacto` / `blog` | running |
|
||||
| 114 | [nextcloud](114-nextcloud.md) | 192.168.8.224 | priv | 4 | 6 GiB | 25 GiB | `/mnt/library` | `cloud.hubris.network` | running |
|
||||
| 118 | [elementsynapse](118-elementsynapse.md) | 192.168.8.239 | unpriv | 1 | 2 GiB | 8 GiB | — | `matrix.hubris.network` | running |
|
||||
| 119 | [sophia](119-sophia.md) | 192.168.8.157 | priv | 2 | 1 GiB | 10 GiB | `/mnt/library` | — | running |
|
||||
@@ -15,7 +15,10 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
||||
| 121 | [caddy](121-caddy.md) | 192.168.8.175 | unpriv | 1 | 512 MiB | 6 GiB | — | (terminates all `*.hubris.network`) | running |
|
||||
| 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 |
|
||||
| 129 | [house](129-house.md) | 192.168.8.212 | unpriv | 1 | 1344 MiB | 8 GiB | — | `house.hubris.network` | running |
|
||||
| 130 | [grimmory](130-grimmory.md) | 192.168.8.213 | priv | 1 | 2 GiB | 16 GiB | `/mnt/library` | `books.hubris.network` | running |
|
||||
| 131 | [teddycloud](131-teddycloud.md) | 192.168.8.243 | priv | 1 | 1 GiB | 16 GiB | `/mnt/library` | `teddy.hubris.network` (LAN only) | running |
|
||||
|
||||
## Recently destroyed (kept for archaeology)
|
||||
|
||||
@@ -25,6 +28,7 @@ All containers live on [`hubris`](../hosts/hubris.md). Each row links to the per
|
||||
| 100 | arr (yunohost) | ~2026-04-28 | Migrated to docker stack on [arriman](122-arriman.md); planned retention window expired |
|
||||
| 106 | flaresolverr | ~2026-04-28 | Folded into the arriman docker compose |
|
||||
| 116 | heaper | 2026-05-14 | Decommissioned by user; data subtree at `/mnt/library/heaper` (224 MiB) retained |
|
||||
| 126 | plato | 2026-06-28 | Notes/discovery workspace decommissioned; data at `/mnt/library/documents/plato` retained for archaeology |
|
||||
| 123 | claudio-bot | 2026-06-04 | Replaced by Hermes Agent on mac-mini; monitoring migrated to `homelab-health-watchdog` cron. See [deprecation plan](../plans/2026-06-04_130000-deprecate-claudio-bot.md) |
|
||||
| 109 | syncthing | 2026-05-14 | Decommissioned by user; `/mnt/library/syncthing` was already empty |
|
||||
| 125 | seafile | 2026-05-13 | Seafile Pro evaluation, user disliked the product; teardown also removed `files.hubris.network` from caddy + dnsmasq |
|
||||
|
||||
@@ -8,10 +8,15 @@ role: docker-apps
|
||||
host: hubris
|
||||
pve_id: 105
|
||||
lan_ip: 192.168.8.205
|
||||
mesh:
|
||||
tailscale:
|
||||
ip: 100.121.171.122
|
||||
fqdn: apps
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_hosts:
|
||||
|
||||
@@ -8,10 +8,14 @@ role: arr-stack
|
||||
host: hubris
|
||||
pve_id: 122
|
||||
lan_ip: 192.168.8.132
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: arr
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_hosts:
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
# Generated by mcp/build_host_files.py from inventory.yaml.
|
||||
# Do NOT edit by hand — your changes will be overwritten.
|
||||
# Source of truth: ../inventory.yaml
|
||||
name: plato
|
||||
name: auth-outpost
|
||||
kind: lxc
|
||||
os: linux
|
||||
role: app
|
||||
role: authentik-gateway
|
||||
host: hubris
|
||||
pve_id: 126
|
||||
lan_ip: 192.168.8.190
|
||||
pve_id: 106
|
||||
lan_ip: 192.168.8.6
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
mounts:
|
||||
- /mnt/library/documents/plato
|
||||
public_host: plato.hubris.network
|
||||
runs:
|
||||
- plato
|
||||
services_hosted:
|
||||
- name: plato
|
||||
url: https://plato.hubris.network
|
||||
backend: plato
|
||||
- tailscale
|
||||
notes:
|
||||
- Runs Authentik outpost (reverse-proxy/SSO enforcement) for protected services
|
||||
see_also:
|
||||
- containers/126-plato.md
|
||||
- containers/106-auth-outpost.md
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
secrets_issuance_endpoint: https://secrets.hubris.network/issue
|
||||
@@ -12,6 +12,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
peers:
|
||||
- authentik
|
||||
- gitea
|
||||
|
||||
30
hosts/dns.yaml
Normal file
30
hosts/dns.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# Generated by mcp/build_host_files.py from inventory.yaml.
|
||||
# Do NOT edit by hand — your changes will be overwritten.
|
||||
# Source of truth: ../inventory.yaml
|
||||
name: dns
|
||||
kind: lxc
|
||||
os: linux
|
||||
role: dns-server
|
||||
host: hubris
|
||||
pve_id: 107
|
||||
lan_ip: 192.168.8.2
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
runs:
|
||||
- authentik
|
||||
services_hosted:
|
||||
- name: authentik
|
||||
url: https://auth.hubris.network
|
||||
backend: dns
|
||||
dns: null
|
||||
note: Technitium DNS, split-horizon zone
|
||||
notes:
|
||||
- Technitium DNS, split-horizon zone for *.hubris.network
|
||||
- Primary DNS for 192.168.8.0/24 LAN (inventory.services.dns references this)
|
||||
see_also:
|
||||
- containers/107-dns.md
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
secrets_issuance_endpoint: https://secrets.hubris.network/issue
|
||||
@@ -8,10 +8,14 @@ role: matrix-server
|
||||
host: hubris
|
||||
pve_id: 118
|
||||
lan_ip: 192.168.8.239
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: elementsynapse
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
public_host: matrix.hubris.network
|
||||
runs:
|
||||
- matrix
|
||||
|
||||
@@ -8,10 +8,14 @@ role: git-server
|
||||
host: hubris
|
||||
pve_id: 104
|
||||
lan_ip: 192.168.8.121
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: gitea
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_host: git.hubris.network
|
||||
|
||||
@@ -8,10 +8,14 @@ role: home-automation
|
||||
host: hubris
|
||||
pve_id: 108
|
||||
lan_ip: 192.168.8.101
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: homeassistant
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
runs:
|
||||
- haos
|
||||
services_hosted:
|
||||
|
||||
24
hosts/house.yaml
Normal file
24
hosts/house.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by mcp/build_host_files.py from inventory.yaml.
|
||||
# Do NOT edit by hand — your changes will be overwritten.
|
||||
# Source of truth: ../inventory.yaml
|
||||
name: house
|
||||
kind: lxc
|
||||
os: linux
|
||||
role: family-planner
|
||||
host: hubris
|
||||
pve_id: 129
|
||||
lan_ip: 192.168.8.212
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
public_host: house.hubris.network
|
||||
notes:
|
||||
- Docker host for Yuvomi (family planner). Created 2026-06-26.
|
||||
- Runs Yuvomi container + WebDAV doc bridge to paperless
|
||||
age_pubkey: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
see_also:
|
||||
- containers/129-house.md
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
secrets_issuance_endpoint: https://secrets.hubris.network/issue
|
||||
@@ -14,6 +14,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
ssh:
|
||||
|
||||
@@ -8,10 +8,14 @@ role: media-server
|
||||
host: hubris
|
||||
pve_id: 101
|
||||
lan_ip: 192.168.8.206
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: jellyfin
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_host: media.hubris.network
|
||||
|
||||
@@ -13,6 +13,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
ssh:
|
||||
user: dtoro
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
|
||||
@@ -13,6 +13,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
ssh:
|
||||
user: dtoro
|
||||
notes:
|
||||
|
||||
@@ -8,10 +8,14 @@ role: photo-management
|
||||
host: hubris
|
||||
pve_id: 120
|
||||
lan_ip: 192.168.8.136
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: muleimage
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_host: photos.hubris.network
|
||||
|
||||
@@ -13,6 +13,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
ssh:
|
||||
user: root
|
||||
notes:
|
||||
|
||||
@@ -8,10 +8,14 @@ role: file-sync
|
||||
host: hubris
|
||||
pve_id: 114
|
||||
lan_ip: 192.168.8.224
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: nextcloud
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_host: cloud.hubris.network
|
||||
|
||||
@@ -12,6 +12,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
see_also:
|
||||
- containers/102-nfs-export.md
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
|
||||
@@ -8,10 +8,14 @@ role: document-archive
|
||||
host: hubris
|
||||
pve_id: 103
|
||||
lan_ip: 192.168.8.130
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: paperless
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
public_host: paperless.hubris.network
|
||||
|
||||
@@ -12,6 +12,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
ssh:
|
||||
user: dtoro
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
|
||||
@@ -8,10 +8,14 @@ role: workshop
|
||||
host: hubris
|
||||
pve_id: 119
|
||||
lan_ip: 192.168.8.109
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: sophia
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
mounts:
|
||||
- /mnt/library
|
||||
see_also:
|
||||
|
||||
27
hosts/trmnl.yaml
Normal file
27
hosts/trmnl.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Generated by mcp/build_host_files.py from inventory.yaml.
|
||||
# Do NOT edit by hand — your changes will be overwritten.
|
||||
# Source of truth: ../inventory.yaml
|
||||
name: trmnl
|
||||
kind: lxc
|
||||
os: linux
|
||||
role: trmnl-middleware
|
||||
host: hubris
|
||||
pve_id: 128
|
||||
lan_ip: 192.168.8.211
|
||||
mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
public_host: trmnl.hubris.network
|
||||
runs:
|
||||
- trmnl
|
||||
services_hosted:
|
||||
- name: trmnl
|
||||
backend: trmnl
|
||||
url: https://trmnl.hubris.network
|
||||
note: self-hosted middleware for TRMNL e-ink plugins (polled by TRMNL cloud)
|
||||
see_also:
|
||||
- containers/128-trmnl.md
|
||||
mcp_endpoint: https://mcp.hubris.network/mcp
|
||||
secrets_issuance_endpoint: https://secrets.hubris.network/issue
|
||||
@@ -12,6 +12,7 @@ mesh_globals:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
public_host: zimaos.hubris.network
|
||||
runs:
|
||||
- zimaos
|
||||
|
||||
@@ -23,7 +23,8 @@ The app repo at `/opt/<thing>` is the working tree, but the deploy tooling (`web
|
||||
- ~~`192.168.8.230` (claudio-bot — destroyed 2026-06-04)~~
|
||||
- `192.168.8.136` ([mule-images (120)](../containers/120-mule-images.md))
|
||||
- `192.168.8.77` ([hubris host](../hosts/hubris.md) — backup-library)
|
||||
- `192.168.8.190` ([plato (126)](../containers/126-plato.md))
|
||||
- ~~`192.168.8.190` ([plato (126)](../containers/126-plato.md))~~ (destroyed 2026-06-28)
|
||||
- `192.168.8.211` ([trmnl (128)](../containers/128-trmnl.md) — terminalito)
|
||||
|
||||
**Don't strip these when editing app.ini.**
|
||||
|
||||
@@ -37,11 +38,12 @@ The app repo at `/opt/<thing>` is the working tree, but the deploy tooling (`web
|
||||
| `dtoro/gitea-customizations` | [gitea (104)](../containers/104-gitea.md) `/var/lib/gitea/custom/` | A | `http://127.0.0.1:9797/deploy` (loopback) | (orig) | `systemctl restart gitea` if templates changed |
|
||||
| `dtoro/mule-image` | [mule-images (120)](../containers/120-mule-images.md) `/opt/mule-image/` | B | `http://192.168.8.136:9797/deploy` | 6 | `docker compose up -d --build` |
|
||||
| `dtoro/Artifacto` | [apps (105)](../containers/105-apps.md) `/opt/artifacto/` | B | `http://192.168.8.205:9798/deploy` | 7 | `docker compose up -d --build` |
|
||||
| `dtoro/Plato` | [plato (126)](../containers/126-plato.md) `/opt/plato/app/` | B | `http://192.168.8.190:9799/deploy` | 8 | `docker compose up -d --build` |
|
||||
| ~~`dtoro/Plato`~~ | ~~[plato (126)](../containers/126-plato.md) `/opt/plato/app/`~~ (destroyed 2026-06-28) | ⊘ | `http://192.168.8.190:9799/deploy` (dead) | 8 (removed) | Repo archived — LXC destroyed |
|
||||
| `dtoro/claudio-bot` | ~~[claudio-bot (123)](../containers/123-claudio-bot.md)~~ (destroyed 2026-06-04) | ⊘ | `http://192.168.8.230:9797/deploy` (dead) | (archived) | Repo archived — LXC destroyed |
|
||||
| `dtoro/backup-library` | [hubris host](../hosts/hubris.md) `/opt/backup-library/` | A | `http://192.168.8.77:9798/deploy` | (orig) | runs `deploy.sh` (preserves admin-edited `/etc/restic/include-*.list`) |
|
||||
| `dtoro/Homelab-Docs` → homelab-mcp | [apps (105)](../containers/105-apps.md) `/opt/homelab-mcp/` | B | `http://192.168.8.205:9811/deploy` | 10 | reinstalls `homelab-mcp.service` + restart |
|
||||
| `dtoro/Homelab-Docs` → secrets-issuance | [apps (105)](../containers/105-apps.md) `/opt/secrets-issuance/` | B | `http://192.168.8.205:9821/deploy` | 11 | reinstalls `secrets-issuance.service` + restart |
|
||||
| `dtoro/terminalito` | [trmnl (128)](../containers/128-trmnl.md) `/opt/terminalito/` | B | `http://192.168.8.211:9797/deploy` | 12 | reinstalls units + `systemctl restart trmnl-plugins` |
|
||||
|
||||
> Note: `dtoro/Homelab-Docs` has **two webhooks** firing on the same push.
|
||||
> Each owns its own clone on LXC 105. They don't conflict because each
|
||||
@@ -57,7 +59,7 @@ Always commit + push. Local-only edits drift. Common ones:
|
||||
- `/var/lib/gitea/custom/` ↔ `dtoro/gitea-customizations` (auto-deploys)
|
||||
- `/opt/artifacto/` ↔ `dtoro/Artifacto` (auto-deploys)
|
||||
- `/opt/mule-image/` ↔ `dtoro/mule-image` (auto-deploys)
|
||||
- `/opt/plato/app/` ↔ `dtoro/Plato` (auto-deploys)
|
||||
- ~~`/opt/plato/app/` ↔ `dtoro/Plato`~~ (destroyed 2026-06-28)
|
||||
- ~~`/opt/claudio-bot/` ↔ `dtoro/claudio-bot`~~ (destroyed 2026-06-04)
|
||||
- `/opt/backup-library/` ↔ `dtoro/backup-library` (auto-deploys)
|
||||
- `/opt/homelab-mcp/` + `/opt/secrets-issuance/` ↔ `dtoro/Homelab-Docs` (auto-deploys both, see [homelab-context](homelab-context.md))
|
||||
@@ -72,11 +74,6 @@ Always commit + push. Local-only edits drift. Common ones:
|
||||
- Receiver is on **loopback** (`127.0.0.1:9797`), not the LXC IP.
|
||||
- Online3DViewer binary assets are NOT tracked; `deploy.sh` fetches them on first run.
|
||||
|
||||
### Plato
|
||||
- Shape B (`/opt/plato-deploy/{webhook.py,deploy.sh}`, port `9799`).
|
||||
- The in-LXC checkout's `origin` is `http://192.168.8.121:3000/dtoro/Plato.git` (internal gitea), and git creds are at `/root/.git-credentials` rather than the `/etc/plato-deploy/git-credentials` pattern — the unit doesn't set `ProtectHome` so root's home is reachable.
|
||||
- `/data` is a host bind (`/mnt/library/documents/plato`), so `docker compose up -d --build` rebuilds the image + restarts the container without touching the SQLite db. The [fresh-DB bootstrap workaround](../containers/126-plato.md#fresh-db-bootstrap-workaround) only matters if you blow `plato.db` away.
|
||||
|
||||
### mule-image / Artifacto
|
||||
- Async deploy (returns 202) — gitea would otherwise time out the request. Logs: `pct exec <id> -- journalctl -u <thing>-deploy-webhook -f`.
|
||||
- **Cloning from inside the LXC must use the internal gitea IP** (`http://192.168.8.121:3000/...`). `https://git.hubris.network` hits a connection reset from inside [apps (105)](../containers/105-apps.md) (Caddy routing / TLS hairpin not configured for this LXC). Configured `origin` on the in-LXC checkout is the internal URL.
|
||||
@@ -123,6 +120,12 @@ If you're not sure what's already lurking, run `homelab apt-audit --fleet` and l
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-28 — Plato pipeline decommissioned
|
||||
LXC 126 destroyed, webhook id 8 on `dtoro/Plato` removed. `192.168.8.190` removed from gitea `app.ini` `ALLOWED_HOST_LIST`.
|
||||
|
||||
### 2026-06-24 — terminalito pipeline added
|
||||
Webhook id 12 on `dtoro/terminalito` → `http://192.168.8.211:9797/deploy` on [trmnl (128)](../containers/128-trmnl.md). Shape B (`server/deploy/webhook.py` receiver, in-repo `server/deploy/deploy.sh`; secret `/etc/terminalito-deploy/secret`). `app.ini` `ALLOWED_HOST_LIST` extended with `192.168.8.211`. Verified end-to-end with a push. Repo-local `credential.helper` in `/opt/terminalito/.git/config` (the unit can't read root's global git config).
|
||||
|
||||
### 2026-05-20 — homelab-mcp + secrets-issuance pipelines added
|
||||
Webhook ids 10 + 11 on `dtoro/Homelab-Docs` (ports `9811` + `9821` on [apps (105)](../containers/105-apps.md)). Two webhooks on one repo — each owns its own clone (`/opt/homelab-mcp`, `/opt/secrets-issuance`) and only restarts its own service. See [homelab-context](homelab-context.md) for why both services live in one repo.
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ There is **no wildcard on the LAN side**. Every subdomain needs an explicit entr
|
||||
## Components
|
||||
|
||||
- **Authoritative public DNS:** IONOS. `*.hubris.network → 82.165.190.79` (was `74.118.126.4` until 2026-04-22).
|
||||
- **LAN authoritative for `hubris.network` records:** [Technitium DNS](https://technitium.com) on [dns (107)](../containers/107-dns.md) at `192.168.8.2:53` — the **single DNS source** for LAN, household, and mesh. (Through 2026-06-21 it also synced A-records into a NetBird managed zone; that replica was retired in Phase 4, see changelog.) Formerly dnsmasq on [authentik (124)](../containers/124-authentik.md) (decommissioned 2026-06-04).
|
||||
- **LAN authoritative for `hubris.network` records:** [Technitium DNS](https://technitium.com) on [dns (107)](../containers/107-dns.md) at `192.168.8.2:53`. Syncs A records to the NetBird managed DNS zone via cron (see [dns-sync.py](../scripts/dns-sync.py)). Formerly dnsmasq on [authentik (124)](../containers/124-authentik.md) (decommissioned 2026-06-04).
|
||||
- **PVE host** (`192.168.8.77`): resolver is the local Netbird daemon at `100.122.38.109:53`, which forwards to the LAN/upstream and learns hubris.network answers via that path. `netbird status` says "Nameservers: 0/0 Available" — confirming netbird does NOT manage a hubris.network zone; it just caches whatever the system resolver returns.
|
||||
- **All LXCs** now point at Technitium (`192.168.8.2`) directly (since 2026-06-21 — see changelog). The earlier mix of router DNS (`192.168.8.1`) / Tailscale MagicDNS (`100.100.100.100`) — which returned the public IONOS A record and forced `/etc/hosts` overrides — has been removed.
|
||||
- **Some LXCs** keep router DNS (`192.168.8.1`) or Tailscale MagicDNS (`100.100.100.100`), both of which return the public IONOS A record. Those LXCs need either a `/etc/hosts` override or local dnsmasq — see [mesh migration](mesh.md) for which technique applies where.
|
||||
|
||||
## Live entries (as of 2026-06-04)
|
||||
|
||||
@@ -32,11 +32,19 @@ address=/blog.hubris.network/192.168.8.175
|
||||
address=/photos.hubris.network/192.168.8.175
|
||||
address=/photos-new.hubris.network/192.168.8.175
|
||||
address=/artifacto.hubris.network/192.168.8.175
|
||||
address=/plato.hubris.network/192.168.8.175
|
||||
address=/zimaos.hubris.network/192.168.8.175
|
||||
address=/teddy.hubris.network/192.168.8.175
|
||||
address=/nfs-export.hubris.network/192.168.8.200
|
||||
```
|
||||
|
||||
**Non-`hubris.network` override (Toniebox device traffic):**
|
||||
|
||||
```
|
||||
address=/prod.de.bb-online.com/192.168.8.243 # → TeddyCloud (131) direct on :443
|
||||
```
|
||||
|
||||
This intercepts Toniebox DNS locally without touching public DNS. The `dns-sync.py` cron on LXC 107 skips non-`hubris.network` records — this entry is Technitium-only.
|
||||
|
||||
Note: `nfs-export.hubris.network` is the only `.hubris.network` entry that points to a non-HTTP service (NFSv4 on port 2049). It bypasses [caddy (121)](../containers/121-caddy.md) because NFS is L4, not HTTP — Caddy has nothing to do.
|
||||
|
||||
## Why split-horizon
|
||||
@@ -50,11 +58,11 @@ Creating a new Caddyfile site block is necessary but **not sufficient**. Without
|
||||
## Recipe — adding a new subdomain
|
||||
|
||||
1. Edit `/etc/caddy/Caddyfile` on [caddy (121)](../containers/121-caddy.md), commit + push to `dtoro/caddy-conf`. Webhook reloads caddy. See [auto-deploy](auto-deploy.md).
|
||||
2. Add the A record in the [Technitium UI](http://192.168.8.2) at `dns (107)`. That's it — Technitium is the single DNS source; mesh peers forward to it live and LAN/household query it directly. (No managed-zone sync to wait for — retired 2026-06-21, Phase 4.)
|
||||
2. Add the A record in the [Technitium UI](http://192.168.8.2) at `dns (107)` — the NetBird managed DNS zone sync picks it up within ~10 minutes via cron. Or add directly to the NetBird managed zone via API if you need it faster.
|
||||
3. Verify: `dig @192.168.8.2 +short <new>.hubris.network` → `192.168.8.175`.
|
||||
4. On macOS clients, flush: `sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder`. On a NetBird peer that caches a stale answer, `netbird service restart` clears its resolver cache.
|
||||
4. On macOS clients, flush: `sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder`.
|
||||
|
||||
> Technitium on LXC 107 is the single source of truth. (Through 2026-06-21 a `dns-sync.py` cron mirrored it into a NetBird managed zone; that replica + cron were removed in Phase 4 once mesh peers were forwarding directly — see [changelog](#2026-06-21--dns-single-source-phase-4-complete--managed-zone-removed-technitium-is-the-single-source).)
|
||||
> The Technitium config on LXC 107 is the single source of truth. Never hand-edit the NetBird managed zone directly — the [`scripts/dns-sync.py`](../scripts/dns-sync.py) cron on 107 reconciles them and reaps stale records. See [dns.md changelog 2026-06-03](#2026-06-03--single-authoring-source-technitium--netbird-managed-zone-sync).
|
||||
|
||||
## Public path — what does and doesn't follow the LAN map
|
||||
|
||||
@@ -76,53 +84,11 @@ Either:
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-21 — DNS single-source, Phase 4: COMPLETE — managed zone removed, Technitium is the single source
|
||||
After upgrading the Mac client `0.68.3 → 0.71.3` (matching mgmt) and clearing its NetBird resolver cache (`netbird service restart`), the managed-zone deletion was retried and **works**: with 0 managed-zone records, the Mac resolves `git`/`cloud`/`nfs-export`/`auth`.hubris.network entirely by forwarding to Technitium (`192.168.8.2`). The iPhone confirmed the same on **cellular** (no LAN/Fritz path — proof it's the mesh-forward path).
|
||||
### 2026-06-29 — `teddy.hubris.network` added; `prod.de.bb-online.com` override added
|
||||
TeddyCloud (LXC 131) provisioned. `teddy.hubris.network → 192.168.8.175` (Caddy → TeddyCloud web UI at :8443). Non-hubris override `prod.de.bb-online.com → 192.168.8.243` routes Toniebox device HTTPS traffic directly to TeddyCloud port 443 — this bypasses Caddy and is Technitium-only (dns-sync cron does not replicate non-hubris.network records to the NetBird managed zone).
|
||||
|
||||
So the first deletion attempt (the REVERTED entry below) was a **misdiagnosis**: forwarding wasn't broken — the old 0.68.3 client's resolver cache (`100.122.255.254`) held stale/empty answers and wouldn't clear on `down/up` (only a full daemon restart clears it), and the Mac's dual LAN+mesh resolver paths muddied the test. A direct query (`dig @100.122.255.254 <unsynced-name>`) had in fact shown forwarding working all along.
|
||||
|
||||
**Done:**
|
||||
- Deleted all 23 NetBird managed-zone A-records via API.
|
||||
- Removed the `*/10` sync cron (`/etc/cron.d/dns-sync`). Kept `/opt/dns-sync/sync.py` + token + a pre-deletion backup as an emergency-restore tool only (run it once to rebuild the managed zone if ever reverting).
|
||||
|
||||
**End state — one zone:** Technitium (`192.168.8.2`) is the single authoring + serving source. Mesh peers forward to it (route via `Core` → `192.168.8.0/24`); LAN/household query it directly (Fritz!Box DNSv4 → Technitium). No managed-zone replica, no sync.
|
||||
|
||||
**Prereqs for it to keep working:** mesh clients on NetBird **0.71.x+**, and roaming peers in the `Core` group (route) — both satisfied. Other peers (laptops, proxmox-server) pick up forwarding as their resolver caches expire, or after a `netbird service restart`.
|
||||
|
||||
**Optional, not done:** flip the Technitium wildcard `*.hubris.network` from `→ 82.165.190.79` (VPS, mirrors public) to `→ 192.168.8.175` (Caddy) so new Caddy services need zero DNS entries. Deferred — decide separately.
|
||||
|
||||
### 2026-06-21 — DNS single-source, Phase 4: ATTEMPTED + REVERTED — managed zone is load-bearing
|
||||
Tried to collapse to a single zone by deleting the NetBird managed-zone replica (and pausing `dns-sync`). Result: **mesh-peer DNS broke.** With the managed zone gone, the Mac (NetBird 0.68.3) failed to resolve `git`/`cloud`/`nfs-export`.hubris.network via the NetBird resolver (`100.122.255.254`) — the `home-lab-dns` nameserver group (`→ 192.168.8.2`) reports `Available` but does **not** actually serve forwarded queries. Restored the managed zone via `dns-sync.py` (23 records) and re-enabled the cron; resolution recovered immediately.
|
||||
|
||||
**Correction to the Phase 2 entry below:** the missing route was *a* real problem (it blocked roaming-peer *connectivity* to services — the actual user-facing win), but it was **not** the whole story. DNS forwarding to the routed-LAN IP `192.168.8.2` still does not work for mesh peers, so the original "NetBird won't forward to Technitium" finding stands and the managed zone stays.
|
||||
|
||||
**To actually finish single-source later**, forwarding must first be made to work. Two candidates, untried:
|
||||
- Upgrade NetBird clients to 0.71.x (the Mac is 0.68.3 — version skew with mgmt 0.71.3 is a known source of resolver bugs), then retest.
|
||||
- Point `home-lab-dns` at a **mesh-native** DNS IP — join CT 107 to the mesh and use its `100.122.x` address instead of the routed-LAN `192.168.8.2` (the original Phase 2 hypothesis; needs a brief 107 restart for `/dev/net/tun`).
|
||||
|
||||
Net state after today: Phase 1 (on-prem single resolver) and Phase 2 (roaming route → iPhone reaches the homelab) stand and deliver the practical goals. The managed-zone replica + sync are **retained** as load-bearing.
|
||||
|
||||
### 2026-06-21 — DNS single-source, Phase 2: roaming-peer route fixed (see Phase 4 correction above — forwarding still doesn't serve queries; this fixed *connectivity*, not DNS)
|
||||
The long-standing belief that "NetBird won't forward to Technitium for mesh peers" (which is *why* the managed-zone sync was built) turned out to be **wrong**. The NetBird API showed the real cause:
|
||||
|
||||
- The `home-lab-dns` nameserver group (→ `192.168.8.2`, match-domain `hubris.network`) was already applied to **all** peers (via the `All` group). So every peer *had* the forwarding rule.
|
||||
- But the `192.168.8.0/24` route (network resource `home-lab-network`) was distributed to the **`Services`** group only = `{netbird-ionos, proxmox-server}`. Roaming peers (`dtoro-iphone`, `mac-mini`, `republic-laptop`, `muli-laptop`, `ludo-mini`) are in **`Core`**, which had **no route to the homelab subnet** → they couldn't reach `192.168.8.2` → forwarding silently failed (`Networks: -`).
|
||||
|
||||
**Fix:** added the `Core` group to the `home-lab-network` resource distribution (now `[Services, Core]`) via `PUT /api/networks/.../resources/...`. Roaming peers immediately picked up `Networks: 192.168.8.0/24` and `[192.168.8.2:53] for [hubris.network] is Available`. No CT 107 mesh-join / TUN / restart was needed (the original Phase 2 hypothesis is obsolete). This also gives roaming devices full homelab **service** access, not just DNS.
|
||||
|
||||
The managed zone is still in place as a fallback pending the Phase 3 roaming test (iPhone on cellular); Phase 4 then deletes the managed zone + `dns-sync` cron. Rollback: PUT the resource back to `[Services]` only.
|
||||
|
||||
### 2026-06-21 — DNS single-source, Phase 1: on-prem LXCs decoupled from NetBird
|
||||
Goal: collapse the three overlapping DNS sources (Technitium + NetBird managed zone + per-LXC band-aids) toward **one zone**, keeping NetBird. Phase 1 (the safe, mesh-independent half) is done:
|
||||
|
||||
- **Every homelab LXC now resolves via Technitium (`192.168.8.2`).** Fixed 8 boxes that were on a dead resolver, the router, or Tailscale MagicDNS:
|
||||
- `192.168.8.180` (dead ex-Authentik): 102, 106, 126
|
||||
- `192.168.8.1` (router → public answer): 101
|
||||
- `100.100.100.100` (Tailscale MagicDNS): 104, 105, 114, 119 — also ran `tailscale set --accept-dns=false` so `tailscaled` stops rewriting `/etc/resolv.conf`.
|
||||
- Already correct (`.2`): 103, 118, 120, 121, 122. CT 107 stays on `1.1.1.1` by design (no self-dependency).
|
||||
- **Removed the redundant `/etc/hosts` band-aids** (`auth`/`mcp`/`secrets` → `192.168.8.175`) on 101, 103, 104, 105, 114, 118, 120, 121, 122, 126; disabled `hubris-hosts-override.service` where enabled. Backups at `/etc/hosts.bak-dnsplan`. PVE-managed lines and self-hostname maps preserved. Technitium already returns identical-or-better answers (verified: `auth → 82.165.190.79`, `mcp`/`secrets`/`cloud`/`git` → `192.168.8.175`).
|
||||
- **Nextcloud (114):** its documented Guzzle-workaround dnsmasq is not running; resolves correctly straight from Technitium, so no special-casing remains.
|
||||
- Net effect: **NetBird's DNS now only matters for off-LAN roaming peers** (Tier 2). On-prem (LXCs + household via Fritz!Box→Technitium) is fully NetBird-independent — so dropping the managed zone later can no longer break on-LAN resolution. Phases 2–4 (mesh-IP forwarding, roaming test, managed-zone + sync deletion) still pending.
|
||||
### 2026-06-28 — `plato.hubris.network` removed
|
||||
Plato (LXC 126) decommissioned. Technitium entry deleted; dns-sync cron reaped the NetBird managed zone record.
|
||||
|
||||
### 2026-06-17 — Fritz!Box DNSv4 server set to Technitium; old limitation resolved
|
||||
Household LAN clients (192.168.178.x) now resolve `*.hubris.network` to LAN IPs — the limitation noted below is resolved. Configured at Fritz!Box Internet → Filter → DNS Server → DNSv4 Server = `192.168.8.2` (User-defined).
|
||||
|
||||
@@ -64,11 +64,9 @@ The MCP server and secrets-issuance each have their own clone
|
||||
|
||||
- Both services bind `0.0.0.0:<port>`. The trust boundary is
|
||||
`MESH_SUBNETS` in the service's environment + nftables (planned). Today
|
||||
`MESH_SUBNETS=100.122.0.0/16,192.168.8.0/24` — Netbird + the homelab LAN.
|
||||
(The legacy Tailscale CGNAT range `100.64.0.0/10` was dropped 2026-06-21
|
||||
when Tailscale was decommissioned; secrets-issuance restarted. Only
|
||||
secrets-issuance reads `MESH_SUBNETS` — homelab-mcp does not.) Adjust if
|
||||
the LAN ever has untrusted devices.
|
||||
`MESH_SUBNETS=100.122.0.0/16,100.64.0.0/10,192.168.8.0/24` — Netbird +
|
||||
Tailscale + the homelab LAN. Adjust if the LAN ever has untrusted
|
||||
devices.
|
||||
- Caddy fronts both with Let's Encrypt certs via the IONOS DNS challenge:
|
||||
`mcp.hubris.network` → `192.168.8.205:9810`,
|
||||
`secrets.hubris.network` → `192.168.8.205:9820`. Off-LAN clients on
|
||||
@@ -127,7 +125,7 @@ The MCP server and secrets-issuance each have their own clone
|
||||
step-by-step for adding a new client
|
||||
- [Auto-deploy](auto-deploy.md) — the `homelab-mcp` + `secrets-issuance`
|
||||
pipelines (and the rest of the lab's webhook pipelines)
|
||||
- [Mesh](mesh.md) — Netbird paths and the `192.168.8.0/24`
|
||||
- [Mesh](mesh.md) — Netbird / Tailscale paths and the `192.168.8.0/24`
|
||||
network resource
|
||||
- [Apps (105)](../containers/105-apps.md) — where both services run
|
||||
- [Gitea (104)](../containers/104-gitea.md) — the source of truth
|
||||
|
||||
@@ -42,6 +42,8 @@ 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` |
|
||||
| `house.hubris.network` | whole host | `192.168.8.212:3000` ([house 129](../containers/129-house.md)) | `house-ratelimit` (30 rps / 60 burst) | `house.fullchain.crt` / `house.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 +87,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`. Verified live from the internet (200 with token / 401 without). It was provisioned during a mesh outage — the `home-lab-network` (192.168.8.0/24) route had no active routing peer because the **mac-mini routing peer's netbird was down** (all home-backed public services 504'd). Bringing netbird up on mac-mini restored the route; no traefik change was needed.
|
||||
|
||||
### 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.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Every LXC that mounts `/mnt/library` participates in a shared `media` group with
|
||||
- `/etc/subgid` has `root:100000:65536` AND `root:10000:1` (second line required for unprivileged LXCs to receive GID 10000).
|
||||
- Shared subtrees owned `:media` mode `2775` (drwxrwsr-x, setgid):
|
||||
- `movies`, `tv`, `music`, `anime`, `podcasts` — jellyfin libraries
|
||||
- `audiobooks`, `audiobookshelf-metadata`, `books`, `comics` — audiobookshelf / booklore
|
||||
- `audiobooks`, `audiobookshelf-metadata`, `books`, `comics` — audiobookshelf / grimmory
|
||||
- `downloads` — \*arr stack output
|
||||
- `images` — photoprism / immich / mulita
|
||||
- `roms` — emu frontends
|
||||
@@ -61,6 +61,7 @@ Every LXC that mounts `/mnt/library` participates in a shared `media` group with
|
||||
| 119 | [sophia](../containers/119-sophia.md) | priv | www-data |
|
||||
| 120 | [mule-images](../containers/120-mule-images.md) | priv | www-data |
|
||||
| 122 | [arriman](../containers/122-arriman.md) | priv | www-data, audiobookshelf, radarr, sonarr, lidarr, prowlarr, qbittorrent, bazarr, jellyseerr, mylar, jackett, overseerr, plex, arr |
|
||||
| 130 | [grimmory](../containers/130-grimmory.md) | priv | Docker container uses `GROUP_ID=10000` env var (linuxserver pattern) — no in-LXC group needed |
|
||||
|
||||
> Some entries from earlier snapshots — 100 (arr-yunohost), 107 (marimo), 109 (syncthing), 110 (photoprism), 112 (immich), 116 (heaper) — referenced LXCs that have since been destroyed. See [containers/index](../containers/index.md#recently-destroyed-kept-for-archaeology).
|
||||
|
||||
@@ -68,7 +69,7 @@ Config backups: `/root/101.conf.bak.*`, `/root/109.conf.bak.*` (109 destroyed 20
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **[apps (105)](../containers/105-apps.md) is a Docker host.** Adding `media` to the LXC alone is *not* enough for Docker containers inside. Each Docker container needs its GID passed in explicitly: `--group-add 10000` or `user: "<uid>:10000"` in compose. Booklore, audiobookshelf-in-docker, etc. need this per-container.
|
||||
- **[apps (105)](../containers/105-apps.md) and [grimmory (130)](../containers/130-grimmory.md) are Docker hosts.** Adding `media` to the LXC alone is *not* enough for Docker containers inside. Each Docker container needs its GID passed in explicitly: `--group-add 10000`, `user: "<uid>:10000"`, or `GROUP_ID=10000` (linuxserver images) in compose. Grimmory, audiobookshelf-in-docker, etc. need this per-container.
|
||||
- **`pct exec` does NOT run initgroups.** So `pct exec <id> -- id` shows only the primary group. For interactive verification, use `pct exec <id> -- sudo -i -u root id` or `su - <user> -c id`. Real systemd services work fine.
|
||||
- **systemd `User=root`** skips initgroups — explicit `SupplementaryGroups=media` drop-in needed.
|
||||
- **`pct restore`** or template rebuilds wipe in-container group membership and unprivileged-LXC idmap blocks. Re-apply from this page.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Mesh — Netbird
|
||||
# Mesh — Tailscale → Netbird migration
|
||||
|
||||
The hubris fleet runs on Netbird. Tailscale — the previous overlay — was **fully decommissioned on 2026-06-21**: removed from the 6 LXCs that still ran it (101, 103, 104, 105, 114, 119), apt package + state purged, `tailscaled` disabled. The fleet is now Netbird-only. (Historical migration notes below are kept for context.)
|
||||
The hubris fleet is migrating from Tailscale to Netbird. Netbird is the target end-state. In-progress as of 2026-04-21.
|
||||
|
||||
## Current state
|
||||
|
||||
- **PVE host** uses Netbird (`wt0`, `100.122.38.109/16`). Its resolver is the local netbird daemon, which forwards to LAN/upstream — so the PVE host gets `*.hubris.network → 192.168.8.175` via the system resolver chain.
|
||||
- **Netbird mgmt host** (`82.165.190.79`, FQDN `inspiring-ramanujan.netbird.selfhosted`, NB IP `100.122.165.149`) is now itself a peer on the mesh (joined 2026-04-22 via setup key, netbird 0.69.0). Routes the homelab network (`192.168.8.0/24`) via the PVE peer. This gives the mgmt host LAN access *and* split-horizon DNS for `*.hubris.network`. Useful independently of any Authentik integration.
|
||||
- **All LXCs** now resolve via Technitium (`192.168.8.2`) directly — as of the 2026-06-21 DNS single-source work (Phase 1). The previous mix of router DNS (`192.168.8.1`) / Tailscale MagicDNS (`100.100.100.100`) returned the *public* IONOS A record and is gone. See [dns.md changelog 2026-06-21](dns.md).
|
||||
- **Most LXCs** still run Tailscale or use router DNS (`192.168.8.1`) / Tailscale MagicDNS (`100.100.100.100`), both of which return the *public* IONOS A record `*.hubris.network → 82.165.190.79`. The VPS only routes hostnames it actually publishes (today, `artifacto` + `blog`), so this path is a dead end for any LAN-only service.
|
||||
|
||||
## ICE / STUN / TURN
|
||||
|
||||
@@ -45,8 +45,6 @@ Pre-migration, the bundled `netbirdio/netbird-server` combined image silently ig
|
||||
|
||||
## Consequence — every LXC wired to Authentik needs an internal override
|
||||
|
||||
> **RESOLVED 2026-06-21 (DNS single-source, Phase 1).** Every homelab LXC now points its resolver directly at **Technitium (`192.168.8.2`)**, which answers the full split-horizon zone (`auth → 82.165.190.79`, everything else → Caddy `192.168.8.175`). The per-LXC `/etc/hosts` overrides and Tailscale-MagicDNS/dead-`.180`/router resolvers below were removed; `hubris-hosts-override.service` disabled where present. The section is kept for history. See [dns.md changelog 2026-06-21](dns.md).
|
||||
|
||||
Until each LXC is migrated to Netbird, anything that needs to reach `auth.hubris.network` (Authentik), `cloud.hubris.network` (Nextcloud), etc., must override the public answer with `192.168.8.175`.
|
||||
|
||||
Two techniques. Pick by HTTP-client behavior.
|
||||
@@ -92,7 +90,7 @@ pct set <id> --nameserver "127.0.0.1 192.168.8.1 1.1.1.1"
|
||||
## Adding new LXCs
|
||||
|
||||
- Don't add new LXCs to Tailscale; add them to Netbird. Tailscale is being decommissioned on hubris.
|
||||
- When wiring a new app into Authentik: `cat /etc/resolv.conf` on the target LXC. It should be `192.168.8.2` (Technitium), which returns correct split-horizon answers — no `/etc/hosts` override needed. (Historically, boxes on `192.168.8.1`/`100.100.100.100` needed an override; those resolvers were removed 2026-06-21.)
|
||||
- When wiring a new app into Authentik: `cat /etc/resolv.conf` on the target LXC. If nameserver is `192.168.8.1` or `100.100.100.100`, add the hosts override. If it's the netbird daemon IP, skip.
|
||||
|
||||
## Long-term fix
|
||||
|
||||
|
||||
@@ -64,14 +64,14 @@ No NAT on Proxmox — traffic flows without double-NAT.
|
||||
|
||||
## Remote access
|
||||
|
||||
- **NetBird mesh** — the remote-administration path. Authenticated via [Authentik on the VPS](../vps/).
|
||||
- Tailscale (the previous overlay) was **decommissioned 2026-06-21**. See [mesh.md](mesh.md).
|
||||
- **NetBird mesh** — primary path for remote administration. Authenticated via [Authentik on the VPS](../vps/).
|
||||
- **Tailscale** — legacy, being phased out. See [mesh.md](mesh.md).
|
||||
|
||||
## Related
|
||||
|
||||
- [DNS](dns.md) — split-horizon config and entry list
|
||||
- [Ingress](ingress.md) — public entry points via VPS traefik
|
||||
- [Mesh](mesh.md) — NetBird VPN overlay
|
||||
- [Mesh](mesh.md) — NetBird / Tailscale VPN overlay
|
||||
- [hosts/hubris.md](../hosts/hubris.md) — Proxmox host (vmbr0/vmbr1 config)
|
||||
- [CT 107 — dns](../containers/107-dns.md) — Technitium DNS + DHCP server
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@ are managed by `ssh/deploy-keys.sh`. SSH user is `root`.
|
||||
| 120 | mule-images | `192.168.8.136` | photo-management |
|
||||
| 121 | caddy | `192.168.8.175` | reverse-proxy |
|
||||
| 122 | arriman | `192.168.8.132` | arr-stack |
|
||||
| 126 | plato | `192.168.8.190` | app |
|
||||
|
||||
### Workstations
|
||||
|
||||
|
||||
104
inventory.yaml
104
inventory.yaml
@@ -11,8 +11,8 @@
|
||||
# ("external" is reserved for hosts the homelab CLI manages via ssh but
|
||||
# that aren't homelab clients themselves — e.g. the IONOS netbird VPS
|
||||
# with no /etc/age/key.txt and no /opt/homelab-context clone.)
|
||||
# - `mesh:` lists addresses the host is reachable at via `netbird`.
|
||||
# (Tailscale was decommissioned 2026-06-21 — see infrastructure/mesh.md.)
|
||||
# - `mesh:` lists addresses the host is reachable at. Both `netbird` and
|
||||
# `tailscale` are accepted during the migration (see infrastructure/mesh.md).
|
||||
# Prefer netbird FQDNs over raw IPs.
|
||||
# - `age_pubkey:` provisioned by secrets-issuance on first bootstrap and
|
||||
# committed back via `homelab client add --finalize-pubkey <key>`.
|
||||
@@ -32,6 +32,7 @@ mesh:
|
||||
primary: netbird
|
||||
accepted:
|
||||
- netbird
|
||||
- tailscale
|
||||
netbird_subnet: 100.122.0.0/16
|
||||
netbird_domain: netbird.selfhosted
|
||||
services:
|
||||
@@ -68,15 +69,16 @@ services:
|
||||
photos:
|
||||
url: https://photos.hubris.network
|
||||
backend: mule-images
|
||||
plato:
|
||||
url: https://plato.hubris.network
|
||||
backend: plato
|
||||
arr_stack:
|
||||
backend: arriman
|
||||
note: jellyseerr / qbit / sab on docker compose
|
||||
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
|
||||
@@ -116,6 +118,27 @@ 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
|
||||
house:
|
||||
kind: lxc
|
||||
pve_id: 129
|
||||
host: hubris
|
||||
os: linux
|
||||
role: family-planner
|
||||
lan_ip: 192.168.8.212
|
||||
public_host: house.hubris.network
|
||||
notes:
|
||||
- Docker host for Yuvomi (family planner). Created 2026-06-26.
|
||||
- Runs Yuvomi container + WebDAV doc bridge to paperless
|
||||
age_pubkey: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
jellyfin:
|
||||
kind: lxc
|
||||
pve_id: 101
|
||||
@@ -124,6 +147,9 @@ hosts:
|
||||
role: media-server
|
||||
lan_ip: 192.168.8.206
|
||||
public_host: media.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: jellyfin
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
@@ -142,6 +168,9 @@ hosts:
|
||||
role: document-archive
|
||||
lan_ip: 192.168.8.130
|
||||
public_host: paperless.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: paperless
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
@@ -154,6 +183,9 @@ hosts:
|
||||
lan_ip: 192.168.8.121
|
||||
public_host: git.hubris.network
|
||||
backend_port: 3000
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: gitea
|
||||
mounts:
|
||||
- /mnt/library
|
||||
notes:
|
||||
@@ -168,6 +200,10 @@ hosts:
|
||||
lan_ip: 192.168.8.205
|
||||
public_hosts:
|
||||
- artifacto.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
ip: 100.121.171.122
|
||||
fqdn: apps
|
||||
mounts:
|
||||
- /mnt/library
|
||||
runs:
|
||||
@@ -175,7 +211,27 @@ hosts:
|
||||
- plantuml
|
||||
- homelab-mcp
|
||||
- secrets-issuance
|
||||
# booklore removed 2026-06-29 → migrated to grimmory (LXC 130)
|
||||
age_pubkey: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
auth-outpost:
|
||||
kind: lxc
|
||||
pve_id: 106
|
||||
host: hubris
|
||||
os: linux
|
||||
role: authentik-gateway
|
||||
lan_ip: 192.168.8.6
|
||||
notes:
|
||||
- Runs Authentik outpost (reverse-proxy/SSO enforcement) for protected services
|
||||
dns:
|
||||
kind: lxc
|
||||
pve_id: 107
|
||||
host: hubris
|
||||
os: linux
|
||||
role: dns-server
|
||||
lan_ip: 192.168.8.2
|
||||
notes:
|
||||
- Technitium DNS, split-horizon zone for *.hubris.network
|
||||
- Primary DNS for 192.168.8.0/24 LAN (inventory.services.dns references this)
|
||||
nextcloud:
|
||||
kind: lxc
|
||||
pve_id: 114
|
||||
@@ -184,6 +240,9 @@ hosts:
|
||||
role: file-sync
|
||||
lan_ip: 192.168.8.224
|
||||
public_host: cloud.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: nextcloud
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
@@ -195,6 +254,9 @@ hosts:
|
||||
role: matrix-server
|
||||
lan_ip: 192.168.8.239
|
||||
public_host: matrix.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: elementsynapse
|
||||
sophia:
|
||||
kind: lxc
|
||||
pve_id: 119
|
||||
@@ -202,6 +264,9 @@ hosts:
|
||||
os: linux
|
||||
role: workshop
|
||||
lan_ip: 192.168.8.109
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: sophia
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
@@ -213,6 +278,9 @@ hosts:
|
||||
role: photo-management
|
||||
lan_ip: 192.168.8.136
|
||||
public_host: photos.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: muleimage
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
@@ -240,21 +308,28 @@ hosts:
|
||||
- jellyseerr.hubris.network
|
||||
- qbit.hubris.network
|
||||
- sab.hubris.network
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: arr
|
||||
mounts:
|
||||
- /mnt/library
|
||||
age_pubkey: ''
|
||||
# 123 (claudio-bot) — destroyed 2026-06-04, replaced by Hermes Agent
|
||||
plato:
|
||||
grimmory:
|
||||
kind: lxc
|
||||
pve_id: 126
|
||||
pve_id: 130
|
||||
host: hubris
|
||||
os: linux
|
||||
role: app
|
||||
lan_ip: 192.168.8.190
|
||||
public_host: plato.hubris.network
|
||||
role: book-library
|
||||
lan_ip: 192.168.8.213
|
||||
public_host: books.hubris.network
|
||||
mounts:
|
||||
- /mnt/library/documents/plato
|
||||
age_pubkey: ''
|
||||
- /mnt/library
|
||||
notes:
|
||||
- Docker host for Grimmory (community fork of Booklore). Created 2026-06-29.
|
||||
- Migrated from apps LXC 105; MariaDB data carried over (schema-compatible fork).
|
||||
age_pubkey: age1uellsemnjrzgfg9fxw4jefpy05laxzggwnwhh6ny3wl7alyp6v8q0muxet
|
||||
# 123 (claudio-bot) — destroyed 2026-06-04, replaced by Hermes Agent
|
||||
# 126 (plato) — destroyed 2026-06-28, notes workspace decommissioned
|
||||
zimaos:
|
||||
kind: vm
|
||||
pve_id: 100
|
||||
@@ -270,6 +345,9 @@ hosts:
|
||||
os: linux
|
||||
role: home-automation
|
||||
lan_ip: 192.168.8.101
|
||||
mesh:
|
||||
tailscale:
|
||||
fqdn: homeassistant
|
||||
republic-laptop:
|
||||
kind: workstation
|
||||
os: linux
|
||||
|
||||
@@ -18,7 +18,7 @@ operational reference is here.
|
||||
| --- | --- | --- |
|
||||
| Hostname matches an entry in `inventory.yaml` | The bootstrap looks up `hosts/$(hostname).yaml`. | `hostname` (Linux) / `scutil --get LocalHostName` (macOS) |
|
||||
| OS is Linux or macOS | bootstrap detects via `uname -s` | `uname -s` |
|
||||
| On the Netbird mesh **or** on the LAN | issuance is gated to mesh + LAN subnets. **For Netbird: use a setup-key, not interactive auth** — see "Getting onto Netbird" below. | `netbird status` |
|
||||
| On the mesh (Netbird or Tailscale) **or** on the LAN | issuance is gated to mesh + LAN subnets. **For Netbird: use a setup-key, not interactive auth** — see "Getting onto Netbird" below. | `netbird status` / `tailscale status` |
|
||||
| `git`, `python3`, `python3-yaml`, `age`, `sops` | bootstrap preflight; `homelab` CLI imports yaml | See per-OS commands below |
|
||||
| Can resolve `*.hubris.network` | bootstrap calls `https://secrets.hubris.network/issue` and writes `https://mcp.hubris.network/mcp` | `dig +short mcp.hubris.network` (should return `192.168.8.175`) |
|
||||
|
||||
|
||||
202
operations/runbook-budget-from-csv.md
Normal file
202
operations/runbook-budget-from-csv.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Runbook: Budget import from N26 CSV → Yuvomi
|
||||
|
||||
Distil a bank-export CSV into Yuvomi's Budget and Subscriptions modules using
|
||||
the `yuvomi-mcp` tools. Run this whenever a new CSV period needs to be
|
||||
summarised into targets and fixed costs.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `yuvomi-mcp` is running on LXC 129 and connected as an MCP server in Claude.
|
||||
- The CSV is an N26 export (columns: Booking Date, Value Date, Partner Name,
|
||||
Partner Iban, Type, Payment Reference, Account Name, Amount (EUR), …).
|
||||
- API token: `homelab secret yuvomi-api-token` (decrypts on any enrolled client).
|
||||
- Direct API base: `https://house.hubris.network/api/v1`
|
||||
|
||||
---
|
||||
|
||||
## API quirks (Yuvomi ≤ 0.77.x)
|
||||
|
||||
- **Subscriptions live under `/budget/subscriptions`**, NOT `/subscriptions/`.
|
||||
A top-level `/subscriptions` route returns 404.
|
||||
- `GET /budget/subscriptions` → `{ data: { subscriptions: [...], summary: {...} } }`
|
||||
- `GET /budget/subscriptions/meta` → `{ data: { categories: [...], payment_methods: [...] } }`
|
||||
- `POST /budget/subscriptions` → create a subscription (name, amount, billing_cycle,
|
||||
cycle_interval, next_payment_date, currency, category_id, payment_method_id required)
|
||||
- `GET /budget/` (no month) → returns only **non-recurring** base entries.
|
||||
Use `GET /budget/?month=YYYY-MM` to get all entries (recurring + one-time) for a month.
|
||||
- `GET /budget/categories` → expense category keys + income category names (German keys
|
||||
like `"Erwerbseinkommen"`, `"Sozialleistungen"`, `"Geschenke & Transfers"`).
|
||||
- Budget entries: `amount` positive = income, negative = expense.
|
||||
- Recurring entries: set `is_recurring: 1` + `recurrence_interval: "monthly"`.
|
||||
The `date` field sets the start month.
|
||||
- `recurrence_virtual: 1` smooths non-monthly amounts across all months in the summary
|
||||
(e.g. 55.08 € quarterly → shows as ~18.36 €/month).
|
||||
- Custom RRULE strings (`recurrence_rule`) are **not accepted** by the API — use
|
||||
`cycle_interval` on the subscription instead, or `recurrence_interval` on budget entries.
|
||||
|
||||
---
|
||||
|
||||
## Subscription category IDs (as of 2026-06-26)
|
||||
|
||||
| id | name | budget_subcategory_key |
|
||||
|---|---|---|
|
||||
| 1 | Entertainment | subscription_entertainment |
|
||||
| 2 | Productivity | subscription_productivity |
|
||||
| 3 | Utilities | subscription_utilities |
|
||||
| 4 | Health | subscription_health |
|
||||
| 5 | Education | subscription_education |
|
||||
| 6 | Other | subscription_other |
|
||||
|
||||
## Payment method IDs
|
||||
|
||||
| id | name |
|
||||
|---|---|
|
||||
| 1 | Credit Card |
|
||||
| 2 | Debit Card |
|
||||
| 3 | PayPal |
|
||||
| 6 | Bank Transfer / SEPA |
|
||||
| 7 | Other |
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Categorise the transactions
|
||||
|
||||
Skip these as internal/already-covered:
|
||||
- Fixed costs you'll enter as **subscriptions** (Miete, SWM, SYNVIA, Hundefutter,
|
||||
Netflix, Grover, Rundfunk ARD, KuKita, Lillydoo)
|
||||
- Internal transfers (The Joy Pot ↔ Cookie, Hauptkonto, Tagesgeldkonto splits)
|
||||
- Identified income (Cookie Share, Kindergeld, Pocket Money credits, Distributor)
|
||||
- Fun Money pass-throughs (in and out same month → net zero)
|
||||
|
||||
**Variable expense taxonomy:**
|
||||
|
||||
| Category key | Subcategory key | Examples |
|
||||
|---|---|---|
|
||||
| `food` | `groceries` | E-Center, Knuspr, EDEKA, Tegut, VollCorner, Lidl, Netto, REWE, KoRo, Roast Market |
|
||||
| `food` | `restaurants_bars` | Restaurants, Lieferando, Cafes, Zeit für Brot, Baobab, Höflinger |
|
||||
| `personal_health` | `beauty_cosmetics` | DM, Rossmann |
|
||||
| `personal_health` | `pharmacy` | Apotheke, MVZ Dermatologie |
|
||||
| `transport` | `apps_taxi` | Uber, RYD GMBH, MVG, Handyparken |
|
||||
| `shopping_clothing` | `gifts` | Children products: Schlummersack, Catchy Kids, SP EVERY., Dukal, Berger-Lernwelt |
|
||||
| `shopping_clothing` | `clothes_shoes` | Zalando, Ernsting's, Schuhmair, Thalia, Vinted, Airbnb, Hotel at Booking.com |
|
||||
| `shopping_clothing` | `electronics` | Amazon, AMZN Mktp DE |
|
||||
| `housing` | `renovation_maintenance` | IKEA, Markus Festl, Granit, Sostrene Grene, Mol* tischdecken, Gaertnerei, Dehner |
|
||||
| `education` | `courses_college` | Kathrin Orlob (PEKiP), Nerina Aupperle |
|
||||
| `leisure` | `streaming` | WOW wowtv.de |
|
||||
| `financial_other` | `bank_fees` | Unidentified PayPal, Ratepay, N26 fees |
|
||||
| `Geschenke & Transfers` | *(income)* | One-off incoming transfers |
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Create subscriptions
|
||||
|
||||
```
|
||||
get_subscriptions_meta() ← get category_id and payment_method_id
|
||||
```
|
||||
|
||||
**Standard Cookie household subscriptions (as of 2026-07):**
|
||||
|
||||
| Name | Amount | billing_cycle | cycle_interval | category_id | payment_method_id |
|
||||
|---|---|---|---|---|---|
|
||||
| Miete | 1080.00 | monthly | 1 | 6 (Other) | 6 (Bank Transfer) |
|
||||
| Strom (SWM) | 79.00 | monthly | 1 | 3 (Utilities) | 6 |
|
||||
| Internet / TV / Telefon | 29.99 | monthly | 1 | 3 (Utilities) | 6 |
|
||||
| Hundefutter | 75.00 | monthly | 1 | 6 (Other) | 6 |
|
||||
| Netflix | 8.00 | monthly | 1 | 1 (Entertainment) | 6 |
|
||||
| Grover | 16.90 | monthly | 1 | 6 (Other) | 2 (Debit Card) |
|
||||
| Rundfunk ARD / ZDF | 55.08 | monthly | 3 | 1 (Entertainment) | 6 |
|
||||
| KuKita Daycare (Leon) | 503.00 | monthly | 1 | 5 (Education) | 6 |
|
||||
| Lillydoo diapers | 56.70 | monthly | 2 | 4 (Health) | 3 (PayPal) |
|
||||
|
||||
Monthly equivalent total: **1,838.60 €** (Yuvomi applies cycle_interval to prorate).
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Add recurring income entries
|
||||
|
||||
```
|
||||
stage_add_budget_entry(
|
||||
title="Kindergeld",
|
||||
amount=55.00,
|
||||
category="Sozialleistungen",
|
||||
date="YYYY-MM-01",
|
||||
is_recurring=True,
|
||||
recurrence_interval="monthly",
|
||||
)
|
||||
commit_pending(pending_id)
|
||||
```
|
||||
|
||||
**Standard recurring income:**
|
||||
|
||||
| Title | Amount | category |
|
||||
|---|---|---|
|
||||
| Kindergeld | +55.00 | Sozialleistungen |
|
||||
| Cookie Share | +2650.00 | Erwerbseinkommen *(see recommended amount below)* |
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Post variable transactions
|
||||
|
||||
For each non-skipped CSV row, call `stage_add_budget_entry` with the mapped
|
||||
category/subcategory and the actual transaction amount and date. Use the Partner
|
||||
Name + Payment Reference as the title (truncate to 100 chars).
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Verify
|
||||
|
||||
```
|
||||
get_budget_summary("YYYY-MM")
|
||||
list_subscriptions()
|
||||
```
|
||||
|
||||
Expected for a full month with KuKita:
|
||||
- Fixed expenses ≥ 1,838 € (subscriptions)
|
||||
- Variable expenses ≥ 500 € (groceries alone)
|
||||
|
||||
---
|
||||
|
||||
## Cookie Share: how much to transfer monthly
|
||||
|
||||
Calculated from Jan–Jun 2026 data (Cookie account, one-offs stripped):
|
||||
|
||||
| | €/month |
|
||||
|---|---|
|
||||
| **Fixed costs (subscriptions)** | **1,839** |
|
||||
| Miete | 1,080 |
|
||||
| KuKita *(permanent from Jul 2026)* | 503 |
|
||||
| Strom + SYNVIA + Rundfunk + Netflix + Grover + Hundefutter + Lillydoo | 256 |
|
||||
| **Variable (6-month averages)** | **1,032** |
|
||||
| Groceries | 595 |
|
||||
| Children products | 142 |
|
||||
| Dining & cafes | 100 |
|
||||
| Transport | 66 |
|
||||
| Drugstore | 52 |
|
||||
| Clothing, Amazon, Pharmacy | 77 |
|
||||
| **Total monthly spend** | **≈ 2,871** |
|
||||
| Minus Kindergeld (fixed income) | −55 |
|
||||
| Minus Pocket Money (conservative ~600 €) | −600 |
|
||||
| **→ Recommended Cookie Share** | **≈ 2,650 €** |
|
||||
| With 200 € buffer | **≈ 2,850 €** |
|
||||
|
||||
**Current Cookie Share (Jun 2026): 1,995 € — shortfall ~655 €.**
|
||||
|
||||
The gap was covered by irregular Pocket Money top-ups (avg 962 €/mo over 6 months, but
|
||||
highly variable: 121 €–3,000 €). KuKita starting in June is the biggest step-up; raising
|
||||
Cookie Share to **2,650 €** makes the budget self-sufficient without relying on top-ups.
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-06-29 — Corrections from first real import
|
||||
- Subscriptions endpoint is `/budget/subscriptions`, NOT `/subscriptions/` (404).
|
||||
- `recurrence_rule` RRULE strings are rejected by the API; use `cycle_interval` instead.
|
||||
- `GET /budget/` (no filter) returns only non-recurring entries; use `?month=` for full view.
|
||||
- Added Cookie Share recommendation (2,650 €/month) based on 6-month expense analysis.
|
||||
- Added full category taxonomy table.
|
||||
|
||||
### 2026-06-29 — Initial runbook
|
||||
Created from Jan–Jun 2026 N26 Cookie account analysis.
|
||||
107
plans/2026-06-24-trmnl-plugins-lxc.md
Normal file
107
plans/2026-06-24-trmnl-plugins-lxc.md
Normal 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.md) → netbird
|
||||
mesh → [caddy (121)](../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](../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 MiB–1 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)](../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.md): 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`.
|
||||
443
plans/2026-06-25-yuvomi-deployment.md
Normal file
443
plans/2026-06-25-yuvomi-deployment.md
Normal 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
|
||||
```
|
||||
246
plans/2026-06-29-grimmory-migration.md
Normal file
246
plans/2026-06-29-grimmory-migration.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# Plan: Migrate Booklore → Grimmory (LXC 130)
|
||||
|
||||
**Status:** in-progress
|
||||
**Date:** 2026-06-29
|
||||
**Goal:** Replace Booklore on shared apps LXC 105 with Grimmory on a dedicated LXC 130. Grimmory is the community fork/successor of Booklore with the same database schema and port, so the migration is a near-drop-in swap.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight checklist
|
||||
|
||||
- [ ] Note Booklore MariaDB credentials from Portainer compose on LXC 105 (`DATABASE_PASSWORD`, `MYSQL_ROOT_PASSWORD`)
|
||||
- [ ] Confirm `/mnt/library/books` is readable on LXC 105 (`ls /mnt/library/books | head`)
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Dump Booklore MariaDB
|
||||
|
||||
```bash
|
||||
# On hubris — find the MariaDB container name
|
||||
pct exec 105 -- docker ps --format '{{.Names}}' | grep -i maria
|
||||
|
||||
# Dump (replace <CONTAINER> and <PASSWORD> from Portainer compose)
|
||||
pct exec 105 -- docker exec <CONTAINER> \
|
||||
mysqldump -u grimmory -p<PASSWORD> grimmory \
|
||||
> /tmp/booklore-$(date +%Y%m%d).sql
|
||||
|
||||
# Pull to hubris root for safekeeping
|
||||
pct pull 105 /tmp/booklore-$(date +%Y%m%d).sql /root/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Create LXC 130
|
||||
|
||||
```bash
|
||||
# On hubris — list available Debian 13 templates
|
||||
pveam list local | grep debian-13
|
||||
|
||||
# Create LXC
|
||||
pct create 130 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \
|
||||
--hostname grimmory \
|
||||
--ostype debian \
|
||||
--unprivileged 0 \
|
||||
--cores 1 --memory 2048 --rootfs local-lvm:16 \
|
||||
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
|
||||
--onboot 1 \
|
||||
--mp0 /mnt/library,mp=/mnt/library \
|
||||
--features nesting=1
|
||||
|
||||
pct start 130
|
||||
pct exec 130 -- apt-get update -qq
|
||||
```
|
||||
|
||||
Set the static IP directly in PVE (same pattern as all other LXCs — no Fritz!Box reservation needed):
|
||||
```bash
|
||||
pct set 130 --net0 name=eth0,bridge=vmbr0,ip=192.168.8.213/24,gw=192.168.8.1
|
||||
pct reboot 130
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Bootstrap LXC 130
|
||||
|
||||
```bash
|
||||
pct exec 130 -- bash -c '
|
||||
# Media group
|
||||
groupadd -g 10000 media
|
||||
|
||||
# Docker
|
||||
apt-get install -y ca-certificates curl
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
systemctl enable --now docker
|
||||
'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Deploy Grimmory compose
|
||||
|
||||
```bash
|
||||
pct exec 130 -- mkdir -p /opt/grimmory/mariadb/config /opt/grimmory/data /opt/grimmory/bookdrop
|
||||
```
|
||||
|
||||
Write `/opt/grimmory/docker-compose.yml` on LXC 130:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
grimmory:
|
||||
image: ghcr.io/grimmory-tools/grimmory:latest
|
||||
container_name: grimmory
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "192.168.8.213:6060:6060"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- /mnt/library/books:/books
|
||||
- ./bookdrop:/bookdrop
|
||||
environment:
|
||||
- DATABASE_URL=jdbc:mariadb://mariadb:3306/grimmory
|
||||
- DATABASE_USERNAME=grimmory
|
||||
- DATABASE_PASSWORD=${GRIMMORY_DB_PASSWORD}
|
||||
- USER_ID=0
|
||||
- GROUP_ID=10000
|
||||
- TZ=Europe/Berlin
|
||||
- FORCE_DISABLE_OIDC=false
|
||||
extra_hosts:
|
||||
- "auth.hubris.network:192.168.8.175"
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
|
||||
mariadb:
|
||||
image: lscr.io/linuxserver/mariadb:11.4.8
|
||||
container_name: grimmory-mariadb
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./mariadb/config:/config
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
|
||||
- MYSQL_DATABASE=grimmory
|
||||
- MYSQL_USER=grimmory
|
||||
- MYSQL_PASSWORD=${GRIMMORY_DB_PASSWORD}
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
Write `/opt/grimmory/.env` on LXC 130 (fill real passwords):
|
||||
```
|
||||
GRIMMORY_DB_PASSWORD=<same_password_as_booklore>
|
||||
MYSQL_ROOT_PASSWORD=<root_password>
|
||||
```
|
||||
|
||||
Start:
|
||||
```bash
|
||||
pct exec 130 -- bash -c 'cd /opt/grimmory && docker compose up -d mariadb'
|
||||
# wait ~15s for MariaDB to init, then start grimmory
|
||||
pct exec 130 -- bash -c 'cd /opt/grimmory && docker compose up -d'
|
||||
```
|
||||
|
||||
Verify Grimmory responds (before DB restore — will show setup wizard):
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}' http://192.168.8.213:6060
|
||||
# expect 200 or 302
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Restore Booklore DB
|
||||
|
||||
```bash
|
||||
# Stop Grimmory (keep MariaDB running)
|
||||
pct exec 130 -- docker stop grimmory
|
||||
|
||||
# Copy dump to LXC 130
|
||||
pct push 130 /root/booklore-$(date +%Y%m%d).sql /tmp/booklore.sql
|
||||
|
||||
# Restore (replace <PASSWORD>)
|
||||
pct exec 130 -- docker exec -i grimmory-mariadb \
|
||||
mysql -u grimmory -p<GRIMMORY_DB_PASSWORD> grimmory \
|
||||
< /tmp/booklore.sql
|
||||
|
||||
# Restart Grimmory
|
||||
pct exec 130 -- docker start grimmory
|
||||
```
|
||||
|
||||
Verify books appear:
|
||||
```bash
|
||||
curl -s http://192.168.8.213:6060 | grep -i grimmory
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Authentik OIDC update
|
||||
|
||||
In Authentik Admin UI (`https://auth.hubris.network`):
|
||||
|
||||
1. Providers → find `Booklore` provider
|
||||
2. Edit:
|
||||
- Name: `Grimmory`
|
||||
- Client Type: **Public** (Grimmory uses PKCE — no secret needed)
|
||||
- Redirect URIs: `https://books.hubris.network/oauth2-callback`
|
||||
- Scopes: openid, profile, email, offline_access
|
||||
- Back-channel logout URL: `http://192.168.8.213:6060/api/v1/auth/oidc/backchannel-logout`
|
||||
3. Note the **Client ID** and **Application slug** for Grimmory's OIDC settings
|
||||
|
||||
In Grimmory Admin UI (`http://192.168.8.213:6060` → Settings → Authentication → OIDC):
|
||||
- Issuer URI: `https://auth.hubris.network/application/o/<slug>/` (trailing slash required!)
|
||||
- Client ID: (from Authentik)
|
||||
- Client Secret: leave blank (PKCE)
|
||||
- Click **Test Connection** — all checks should pass (container reaches Authentik via extra_hosts)
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — Caddy cutover
|
||||
|
||||
In the `dtoro/caddy-conf` repo, update `books.hubris.network`:
|
||||
|
||||
```caddy
|
||||
books.hubris.network {
|
||||
reverse_proxy 192.168.8.213:6060
|
||||
}
|
||||
```
|
||||
|
||||
Git push → Caddy webhook auto-reloads (see [caddy (121)](../containers/121-caddy.md)).
|
||||
|
||||
Test:
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://books.hubris.network
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — Enroll as homelab client
|
||||
|
||||
```bash
|
||||
homelab client add grimmory --lan-ip 192.168.8.213
|
||||
```
|
||||
|
||||
Commits the `age_pubkey` back to `inventory.yaml`.
|
||||
|
||||
---
|
||||
|
||||
## Step 9 — Verify end-to-end
|
||||
|
||||
- [ ] `https://books.hubris.network` loads Grimmory
|
||||
- [ ] OIDC login via Authentik works
|
||||
- [ ] Library books from `/mnt/library/books` are visible
|
||||
- [ ] Reading progress / metadata from Booklore is present
|
||||
|
||||
---
|
||||
|
||||
## Step 10 — Decommission Booklore on LXC 105
|
||||
|
||||
1. Portainer → navigate to the Booklore stack → Stop → Remove
|
||||
2. Keep the dump at `/root/booklore-<date>.sql` on hubris (or archive to `/mnt/library/documents/`)
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
If something goes wrong before Caddy cutover: no user-visible impact, just shut down LXC 130.
|
||||
|
||||
If Caddy already cut over: revert the `books.hubris.network` block to `192.168.8.205:6060` and push. Booklore still running on LXC 105 until Portainer stack is removed.
|
||||
@@ -6,6 +6,9 @@ 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) | In Progress |
|
||||
| 2026-06-25 | [Yuvomi deployment — house.hubris.network](2026-06-25-yuvomi-deployment.md) | Done |
|
||||
| 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
|
||||
|
||||
@@ -13,38 +13,47 @@ sops:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXaWJhdFAxdThGSVZRN2I1
|
||||
VjE1djFtdjdnSFVJU3lMZC9YSzNSeGVLaVJBCjlId3pvcFRadUdwamxaa00zNUVl
|
||||
VUJFaDRRLzdSNDNVekR3eWdmREh3eUEKLS0tIEVMMmh2eUlSL0FhRzNnZXJyWk9I
|
||||
MEY3dlVDQktRR1VUajM4WmVwYS80TVUKA0bkns4IE093PvF5Ka3HNWmi+Htqs66H
|
||||
BBEAETnQZOdM/Ca+oySDnhLYYT4rD46m4d3H0YQfXQhlVk/h9CTshQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaRWN5ZzIrNCtYeVRVNWdB
|
||||
b0M3SHF1WVRTZzhhZ3NqK0llaVQ4SWxvSndNClprREJjWm80aGViTktSbGV0ZjFJ
|
||||
R2thaXNXLzM5UmY2UHhuYXNITys2eGcKLS0tIGJkZ0RJS1lFTUhCZFlQTmd6M0NH
|
||||
d0N1RWRRNTkzcGR2Zit3cnN2TXd0dm8KpvHCBO1gejHD0okrivBzC0qmfcFDQgHY
|
||||
8ZLi83Mv7PflCZpcv67d7mai1F89DGDCsoo7TMGHh6rNU+Mpy/g5+A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5bVZma09OVWkrVmtVNEda
|
||||
MnRxdDZuRTV3RmhieTB2ZXVTcjY2VWtTOW5rCkw2STNWTHluRGZhT3B4TmtFWHUv
|
||||
L2hyd3QwMW1iaHRCRWMycmxHVW5qMlEKLS0tIHNrTjdYb3NCdU05WjR3RFdvdFBR
|
||||
Z3NRRVRRcnBhdXVmRmprbk1iL0lWS28KTvN7Z3VyXKCnbJBD9N+FtC5UDCxofPIU
|
||||
WERW/eV7+2F6SeClSu7iE+pDMbjiKrFBVKBAU8OA8yd8VmcnD8IWdw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2YVQ0WnVDUEl3eU1kdGw1
|
||||
aE81eU9iUExJZkg2ZElNR3Z6b3VCWkZSNHk4CkVGZFVZUnpxZEZRd2t3eDBhcmd3
|
||||
VWIwSGhWS2JKSXQ2K0xva1NTYVB5bDAKLS0tIEFEc0Q4TXBXeWV6eW93eWxSMkdM
|
||||
TllXK2xIL0R6T3dHOGdDNVdDTzdycHMKntYg1r5tOHWxpkce89ixirQBOBpIdAuN
|
||||
PBAdd7vY9zL4tq+AB5Goz7gj2I56Fw3tM970YX/JaThCCiyquk2OPQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBublRwajRDeUtmdmtpN3pj
|
||||
VVE0VWJhVWRXeE1UM0xHdEJKUVhHVWpNTG00CnZjNUg3ZkUvcFZTRHc4UDMrOVhV
|
||||
QTNPMmphTkhxZjgzNXN6REtTTTZUU00KLS0tIEk0MXFBNS8wVmpMZFhoYUpsOGlG
|
||||
ZE1OaXdhZVVGQXUrdUhJT09UU21DNkUK4DcLYPqf3ojRotaa95KIUhKYQwWorsum
|
||||
IletEBZtyJdOJgpwN/eZLe12a4E5thI245jSjQlw2B8RKFqOCDyASw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDTnlKU2J0RFU0YU43TzJt
|
||||
MGpQdy9Gb1J5cnd2K2Nvc0FnTUVCSzZudHpvCndmUThtaStCTWFqUzhMVlZ3dmZZ
|
||||
ZHpsTUFmWEVCYWRpT29RNHRVSk5iQlkKLS0tIERpY1RUalA4UkQvSFR4SXNlZ3Z0
|
||||
VG1xSDJCV0ZZR3gwczcxYlNsLzArOVkKvnPBHgk24SBvAsQWw+2FxCVLUdKMNyb3
|
||||
D/Zk0EnwdT6JitDTHcQ2PlDoKBSK6BpW2Bk1gnaC9doiXSz6ykziWw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPVEdGR0VwUjk3MUROOHRJ
|
||||
aXdFMTFDRFZBek5FSWJSbWJDU2F2ODFyTVFzCndTbStrQmFSSWJwbFc0M1Nia2FS
|
||||
eGZEMGsrYkNnT3M2aEhOS0IzOVNOVncKLS0tIENtOEx4SVJQTFcxR3NIM0NHVDI5
|
||||
Z1RIMWRBNGhCOFF4MkdHRlpya25ybGMKsyLYsmqxuXvJ4ZF97Jh5D8BoepehSdKi
|
||||
yGzLaaQo0gW/wu7n0fq7S7HhbiTcPZ8lQboVvhYWU2lTx1p8npUsAQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Y2c3Z1NUT0NxQlpabTlP
|
||||
SGtwK3JwcHorUDRXKzlFTjBhUno0SUsrK0VzCkdVQzAzSHJuRG9BRmJ1QW9aWFk0
|
||||
MHcxL044b2VxYnpOTlJtNFN3NjNsek0KLS0tIGovbGtSOWdmclVKMytHZU5tUmhl
|
||||
WFJLbFVlQjQ5U2pQenhWN1dUd1dWcUUKxb2yYZFys0AqY7+M0/gTIDK+1Bl0FIEj
|
||||
DSqbAb2UYfNXKVEScJl4QeRLEYuTUKejD0WT4cYJzyjRAZzc7PnykA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRU0RhSWZpTVdFelVTeXF1
|
||||
aXhPSGFLdWxMQ2J6UkVEQlN5aUJLYm0xN21ZCnhTYWpvQ1B1Z0dnamQ5SWk3NjVR
|
||||
bGs5TGdZb2NkVkROS0hYQ2ZDVXpIWVEKLS0tIENyblo2L203OUNISUVKMkhXWmg1
|
||||
NllKUW1mUVRldU03RUM1dThUeDJ5WjAKiy7aGBpQImoMdmFkNwR33Aksx8YDrjOg
|
||||
DfOpNOiGHWWNfmLHngvGKzCwjqV0nsl1rM2khXcZ21w/9L8USNCceQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-20T16:24:18Z"
|
||||
mac: ENC[AES256_GCM,data:d3qcjEqpPIFeut634ImvISJLdit0MQWcdFYomrsqriqJ0NUKdyq3XCk85+vvPvDWikB4WEApk6HMXssSY8mhDci95q5Ssmr+JRAhLebMZjs9yXUf9A7vXpNFswmdldu3CKBiDrhm2GE08qUfsCkcf3jjihEqsfHhxMuWSd5fZpU=,iv:VZgvZ3bBArRChgRX38U6/43XoX5aTnts7Kd4S9spBPk=,tag:/5oOw5yvulvFT6KXv2zj0g==,type:str]
|
||||
|
||||
@@ -9,38 +9,47 @@ sops:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUanM5MzFZYnkxVHVHSlYy
|
||||
U1Q1ejNJb3ZORklKM2xjK0p2cytRU0NNRUZJClBTUDJ5dFM2eUFjV0t1YzBCcXcv
|
||||
cmE3czJST1RuMHJVN0tmVTZNdGNRS1kKLS0tIFUydktvK0tBU2U3czRhWHVySGZR
|
||||
ck9KTVR6RS9pOHQ4WkZneG1Tbk5OSWMKwOQ+CWkuLSqfhle9fgDw4XXIp0ojZssw
|
||||
9YWK/suEAb6u9nzbw7zuEmZxhhDV0Y61UAeSSbSmygy7MD7dvxrvgQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFMWU0K3V5NmZBNUk2TWNO
|
||||
NmdRWjlBV3p5dWNhUWhTOFlEdCtkMVh5STJBCkwzbUNkenJjei9TVWVUMi81d0ZX
|
||||
b3dTTnJudVZiV0YzajNPcXlUWUtxdWMKLS0tIEo1eUR3dmJQRWM0bHRtMFc5Q290
|
||||
QmwyMk44SVBXQ0k5QjN3ZWtNS1FSWTgKcc/F1HfMfnwun+mIUq1Ds+DZPk7F1ohe
|
||||
OIVt5aJliApDYesRJ14K1ZEdi4YqCqO+1cLi69iWKHNT3PcOYrepUA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCbm5MOG1Bd0FZbVZTbUU1
|
||||
VGpGVWNnUk03RURPNURNNjdQejY2WkxkNzBRClpuMGFaQzZhaENJckZvbUI3VmUy
|
||||
ekV0eFMza1p4S2w5L0hvNndjN3RpdTQKLS0tIDRTdExxTlZQK0FKV294dnh4dmd4
|
||||
a21yK0VabzhOcDhRajlxbVE1dTdtcWcKQorqkwUs7lppaDJuCDn+KtH+76xPIvsr
|
||||
9axkrDLumeY9LtDhunlaEpax7zdvvAiC4DzJdbJX0LgFCaurtgubOA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFNVUyajV1NFo5VFRyMDlG
|
||||
dWFlRlVJZDlJTHdYOFJLRi81OU9BdkI2RG13ClMwLzFHQ0VxSXJzQ3V2S2pjVFRS
|
||||
dEQvK1VMZ3FDZDkxSHdYb3NHeHE4QTgKLS0tIDNyZnV5OFVQNXVEUUlzK3RYMytt
|
||||
Nnl5bTZuMWFLY28vZnBPWlFEYmFyK00KtwcrF0W91FIi+9nn8rF2G8xP2/ca2h3R
|
||||
DagqhSmpaEC7QHtYGP9SAChhBrDuWdVtK0Gdbc4m/31gW7Fh815zfA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaS1JrL3lpa1c4QUwxWlZT
|
||||
VWQ1ZDVPVjN6QTdFNGQzNkV5WllOK3dYWHhzCllTbVdzRVFTbDZ4TU9PZWpKNnJu
|
||||
cWJ6YWs5amVsR2xZZFpaQlErOXBKZVEKLS0tIFFzVG9QWmdNWGl5UEkxaXZvMUxx
|
||||
SExXL1gxNzVud2JrZ04zTnI4cUlNSVEKPF2zoSnYEt/zeG8QW1454Mcr8u8JTCl7
|
||||
jDyghcRw94enbPbA43zsKGn6QALQ40PfXmZ/MlEbdnf6U3zI7zvygQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrTlFZYVFHZHNCUkllZC94
|
||||
ZUFaS3JZTmdPclA4dlR0QklxMmVxNWNyZUhvCnVoY0YvWGlsbm9CaURkQzZUMTRs
|
||||
ZVNmcDJxZWN2WG9nUUNtT0I4S1FIem8KLS0tIHBRWFhLN3p2Z0ZaWDEyeEw4WUhs
|
||||
Z0RwQ290OWloQzh1eXVLenlrOTZyRzQKQEnY4KC8ReKGFDzklK/A6uIGMRIhMfLW
|
||||
IzogPQiWYcDerMDd14kktBOcbjKRkI9gPSDZdmieS9z/wA5J37DNNA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwMlVSajFiT2loclFzeEQ5
|
||||
ekxqUjBkSEh5M2FJMXA2VGkycllYa1R1Qm40Clh0L0RwSzZqeXlQVXMvRVlsZ05L
|
||||
RHkyMlJlc1BmV0tjQk1uaWJLemR2cmMKLS0tIDlXT1dhRkpUdDRpRkJhL2NHSjN5
|
||||
QU5Pc25lSUV4OWROZENXQzB4Y0o2Y1kKzhOY5jN4gi+u5tl9rAHGRb8Bh9DESl1K
|
||||
qNiD+wauApal8iRrIkGdGsrWrWThSwy7vjnUsoB8JDrBaqLj2usuFA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCSUlxdTFhR1dxaDU2MGdL
|
||||
dVJoYVQwZm9DbEVOYXd4Ymd0dk56R283WjJFCi9MS05CVkxYY2NPc0pjeGNacm8w
|
||||
ZDZGYUMrdDdKS05BckhoRDl4OTVxYjgKLS0tIHlPaUg4Yk96UVJacDFhSm1FaGZo
|
||||
RVVSWVRteGcwN2dHOVhuWFpmU0p5aEkKXetFyc9PKB6dCljl0c/+JJrEyDvYJP0Y
|
||||
1fbPf7WBJsLTaQgrjsGHU4wqlg0Se5GHMeriOPYaRjiL9locKW2SiQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwcWxaenJybWpqQ0RpM3ov
|
||||
cEszZDI4YkswY1hpUmZDcmZ1Ky84N0xUUlh3CjJLcm5nVE0rSHhyeEhNSmFIaE00
|
||||
RkpqVHFEdVR5VmdETnhnQ0JiSlFyNTQKLS0tIGNXdkMzaHBtTWU3azdDVm15NkpZ
|
||||
TWlYZXdBYTROcHBqMCtFcEtJRUJtOEEKdBbB5a5teUnvIYaLJaLp3KZkEXhnkFor
|
||||
pbnL/WrHQViLdu8pJ5nscIO9ryQt7dS6aAu6gP3Tbs7XdREOTFgKmg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-20T14:39:14Z"
|
||||
mac: ENC[AES256_GCM,data:CAoIW1sJkIbNoXMaVlXwfHdREzATNqDKpSN5ecaSiRARThB0/tcLNbkazXKyV1Rx5b+b3s+yCpBzwmNnH6ytaCzG5pKPcnfd6YJq7C5rm/EQHyaVGrPdBsSfqiz4yBXKClej//v3+qkD5Ls2PMppv5KmqigOKNQC/ot3ht2c7lM=,iv:eOmDDclksr9f9CDu6dJprSFwlWuoggzVFjyfqWf5Uhg=,tag:IU3sMlIi92ohM86zOIJdAg==,type:str]
|
||||
|
||||
50
secrets/hermes-house-users.yaml
Normal file
50
secrets/hermes-house-users.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
members:
|
||||
"+491726924525": ENC[AES256_GCM,data:RA==,iv:FLOi3HmBPFtS8BAqPR9Epihs868OwAWs1t2LGjg9kGU=,tag:bf8PGNipOeepfnzHUE5KMA==,type:str]
|
||||
"+4917622791635": ENC[AES256_GCM,data:Mw==,iv:onlvwM9nlAlXnL6f+BTRZlOi4+C6pryyiay421GY9WA=,tag:KiYrTcRlBNbB5mYYlb7GHg==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmYW9VS2FLb1BTVTk2OFZZ
|
||||
Wnd1U2tnanR5N2d6SmNqdFYrekpzZTRMRGpZCjZhREcrY29qc2pFZUhnQnc3U1dE
|
||||
aU5Kd1Robkk4OVd2M0ZkalBWMlQzR0EKLS0tIDJOTjkwR2htaDhDR0hqN0R4cCtB
|
||||
dldLSHBOVjZxNVFZVUtkZm1INkdvM1kK2iaqr75MZ+4QlWaNOjH1X11zHbi4Ahy0
|
||||
ely7Fakx6wSoQtl803q6UyNJyIqCOavJKoLQw48FhAl2yGmv9KsvrQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4eXFvMjZXVWkvZnNLSGhS
|
||||
eWJ3Q0M0QzlnYnJ2dTVhZGRtZ3Y4Ylh5NHc0CkVTRnNLa0NzK2czcjFSSmgxQ081
|
||||
ZVo2MzR4S2ZlY1AxZC8rWTJ4d25vekUKLS0tIEFnK2gvTVBUWk5jOW1NaUtXTmRL
|
||||
U1FCaHZjaTUwZ1RGVlZkZ0JzT1NOek0KceYGFDlpcpAQte0yqebyuQBxr00/Rurh
|
||||
McQ607wCQWbGKkwkHCDi1VGqqokvbU7MRT9iOBLLBlxEF+KS9UiYuA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6aXRkd3d4ZTV0RmltR1JN
|
||||
bFg4VU0vMDVuR0cxc2I0b0NRRUQyQlE0RHk4CnpPaUpGMEh0Unk4d3BDeE84Q0px
|
||||
RnZnUHRQa3BJQUpGWUFZd2dLczhmc3MKLS0tIE9NOUNacDM5dktuWWZ2WWRxajZu
|
||||
Uk5Gb216dmdvUkJzYkoyWE5yQ044eWMKSGR5dDya0gyBWDMB+NSudXK38VQYP88y
|
||||
M6lyeZUAJ3cFydjyE1PjyllBehX9tYh/rh9RS67y/zRSmkXH5e7eAg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkM2I2MkV6NlhydURwYk8w
|
||||
ajB1cEtiQjlLYkRGbDZXTHkzTWxuNW53NTJVCmJuMzJZSlNtU1BxVFNpOWpSVWp3
|
||||
TmxOeTRhRmZlanp4QmVmRjRSOElHL3cKLS0tIHp1MzZQUlUxNXFlS1B6TWhQdzhE
|
||||
ekFibGF3ZE95K2FjZmZmVGkzek54R1UK7Au0Op4P+JZjHIiJJ5effjxjeAJItXw1
|
||||
Wrq8Z6D9NZTAZ2TBUGpdqIZcZhBiOuqxdNV88ZHOu8sjT1SLwm17GQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-28T14:50:45Z"
|
||||
mac: ENC[AES256_GCM,data:7gWwrXY9q7zpiDZgFidEMa4VbxJ0uM7HdG6Ea8P4fbISgRnOx+aoAwznEQ+K/fIBsa1qtiR82lIxsNr6b44W4jmFF3+rPAm9DMs6ImX1KIVCW3GVptM9fMqOxnCI7RGMKKwWYxkvfE2lwl5EsBljqqlsuxDfX26etfqukIhbOIY=,iv:6iI7Q8wFKguICPtFvZWshok7BkEUUITp5XcSYt9hwFM=,tag:jIOzsy4jiSvuHh5GNQsjpQ==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
@@ -8,38 +8,47 @@ sops:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvdUxUdzd2VmN1MWU2dXdM
|
||||
N0FWcXkveVdHYk5aYzFrOVZaRzVYZWs1RFZBCkFwRy9xVFBrVjU0NmhkMlpyN0pp
|
||||
ZlZKenhoNFVRdGg4Q0ZTMS9YUDFKMHcKLS0tIG1GYkZaYkhJNk5zdWZBMEtNL041
|
||||
MWhEQ1RmQ1k0d2Y2dzh2YnRvRm1IOEUKJ64/tNyLe2qBIL4CetRlpLaxhfOL93D5
|
||||
BjeHLwtfXB2fNCJSdCY8H4KsQP+epwriUQPtZVId32n9xDEt71obSw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxZjBPQ3M3NkJwZ2dDZlA3
|
||||
MGR2cTM1b0NBN2ZETFJZUW95M2lITXo4dlNJCjY5NEMrc2xNaEpCVUtkVHJYa1Rr
|
||||
citGL3ZQQnlMR3BpVFpmU3d0eEhiZG8KLS0tIFZGejF0UDJZTUVUMU9KL2hySm9L
|
||||
aEx2ZWRuTldDdFJrNUlmMHZVay9vRnMK0XMd/S3VSdD71eh2MUJGs8c7Jdxmfpua
|
||||
p4ZN4Y4MARz2XMA8xqDpw1SrMevaceD5+p+R+zhunb99aFfhtV7WxQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRNi9kMkt5Vk4rS0pqY2ZP
|
||||
ODErMlRxNmlWaHBvYjNaQ3lRT1RSQitMVlFBCk5HQ2o0QUg4dWFodTNEVGJFTlF0
|
||||
Y2JIVEFPZGVUdXpNS2lGOVQzeFRvMFEKLS0tIGtQY0dicE91R0J3eEM3bEdMZHJ4
|
||||
TndiYVFQWGZyRkF1bWYrRlZ6N0JBRm8KEUKY3chev13KjnGKdTR8tvyYV3s0W1rI
|
||||
8LeJSIocSX58PexqgcKsT8pGpuIiOetEzjzv2WPa0MzEwtFCVgwKYQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3aXdya1RpSGdsQVNMYXlQ
|
||||
cFhCZDUvTnFUMWdpQzAreGprTm9ZOE8vZUI4CmF4K2ZGQ29QOU1kMlhUV3N6RUNk
|
||||
bzJwZDNmR2NITUlzY2hSekozdDBsQzAKLS0tIHI4cy9IR1gvWVo1emcxbzFURWVM
|
||||
YzJvRjlNeE5KancrUUtSZndQNWxhYkEKAedJLVpc47R8rgm4YRyT7Z9G4tfbBnqQ
|
||||
c1UObcIa551wR79n7vJvVb2cSLz3VEURe6sNi4OqJmg017qrMOS5Wg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFcktzeGx5VWZaeFVNc2Mv
|
||||
aWt1dzNUVko5YytOTTdxMWVEVm85N29TMDBrClc3ODdVVDRVRFI3K3BiUGd1TFp1
|
||||
SE5nalY0RkwzNmZQOWU1THhGQ3Q0UkUKLS0tIHNZdHhxdDY0a1AyY0g3WUx3bUJh
|
||||
U0RqRWRFRXNLTWFwemNOYk8vTmxZeTAKaZI6WSNM022xtZQx5yOYXj5mesAjWsu/
|
||||
ZIDG6MayoyrhwAkuZsWOKtKp6zbRCcnm7s9OHdlNsx7PWG1ODEFpOQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrazAvYnovQ3ZFQ3YvcTh3
|
||||
eGhYdFFTZ2ZkTElzVmFta3ROMGpCMlE3VEVvCjNXd2x2QTREQ2NVMEk5OU1JY2Z6
|
||||
NHQzQVVaMVR0bWM4R0lxVVpwTEY4YXMKLS0tIHFTT1dRejNyQlVoUEREUnhlM09V
|
||||
ZEF5WFlnYmgyVDRUYXBTVTgyV2laWkkKdE7hAUxtDZJt8P4LrfOomK8rMTlXeXd0
|
||||
fyY29wRnXqn8s0IkpibWkByWLgyXHe1nNHHgsiPsxYMFbCRW1iwZoQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVbFFxSkhscUV2bm93Z01O
|
||||
WDl3MU11K2lrNzAwUVN5MDhodDlveFZ6QlYwCjNaNjd3bG9QMTQxcVA5TTNQU3Za
|
||||
bWdZTDdGZzJObnJ6NlBxKzVYcmpVNk0KLS0tIEJTcFVTRmkxQlJWSzVIM0ErOVNN
|
||||
c290WkI1ZzBlYkg3QzJJUlQwOFZPYmMKQFp4vqFwh0GpYOur4kGMCeENnuNZnN0x
|
||||
sbNmM9rRm/10T4tHF9aR2/WIjEqm6M6+yXd3phhB8rIggDPlVk2bCg==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNdzIxYUlkY1YvTVdrREgz
|
||||
bEhrTlpXRDFST2MxTUFpSWwrMzF6ZTVsUFRNCnB5WVJKaHNFL0hQak9ZbTEwNjF6
|
||||
YmFEdVJ2YkEyS3BDQkUwZjBJNXh4UUUKLS0tIEpIcGV4N1R6aUU5S0l5dGZOdnNV
|
||||
Njh1WlAwR2tsTXZoUS9JMmlYZm1ZUzQKcqXYn1KgSzAXS+m13/wLmeriNf+fTzY6
|
||||
TSBcB3OxgqvYAWwZutXr6UScVauZPuatSjwE1va4HJQuhSVoCGwqcQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4blY0TEdsU3VoSmR3M0FK
|
||||
dlg4S1JCRVpZRnc4SURNaWdSUjVQdnpCazJnCjhobHN2dmpGZzhrMWs4UUFFdVF6
|
||||
RmtJb0VaQlFIeGhOMFA3Ui9iVmI1K2cKLS0tIHk3M3RhaUtSSm5vajFJUUprRzNa
|
||||
c3JiWC9WNVRYOXJIQmMzUHZxbUZtS0EKsE5cG85lsDFABMbR/A80TQMlurf8qHDR
|
||||
tVxZuMQPIXBdg6Ov72xnQeMGC//apWO40gnfjGn+P3oZGy7m8XJBuw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-21T20:13:20Z"
|
||||
mac: ENC[AES256_GCM,data:bi4o17skIOtZoxJGzLFJ7IAv+X265qIhgnQz6wr4bch2xofwPZZfzECck0I0xDhcwxcDtx3VZ5E2VBdvHWk+iSan+clRq7735k9+DIpjdWaGxIHuylmelZl6aE5kE36UO7fQdnwmiqCPHxiVq/sKuqknFXkl1+FvO1ryrtSHQsU=,iv:xxm+5rK7o2c43iGS1j2Q274QCRsbDn+k+wskpp5tzeU=,tag:sZHk9eth/TwqM8tIWSrv3w==,type:str]
|
||||
|
||||
@@ -1,34 +1,48 @@
|
||||
api_key: ENC[AES256_GCM,data:TbPkuLCidS7cg52DYb0MOCui3TsaiwkOWyYLhp6gcvXI8TqvXej1tNsCF0TNAIr9vvnFaYno/UTEPiOKe/EQqR8uQMyWuvsmMg==,iv:bahy9ae4Qxvkv1OAjx6LwzK5ggqDh2GVHDj95PVP1Mc=,tag:J7PajLaeqFOdz3FESYZmSQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- enc: |
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3djYvT3JJUWh3cE1ZbFc0
|
||||
SiswOGlKNEdFNlJhTVlVSkhqZGlJdGl4U2pvCnV0aEhud0VaOHVGbkVVVkJ3b2Js
|
||||
K1liaEFscGJxMXVVNjNJNW9NeGR6Um8KLS0tIEt2TlFCV09CWDJKbmFySm1EUXc3
|
||||
Zm9IT21QN0lkSlh0UVdhYkVrTDdiUWcK8IO+gylIYKDlSADOtj2gSpM7Af/JG/7Q
|
||||
6y1dT2d82cEu7KuXXuog6gP3sADe/6SDHGgC6Ot6EWM+5dwMN6WwTA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4TFVsb3VYWnVFcnhRL2Vz
|
||||
SVp6cG9CYzdqSFNkTGhHSmtlRDJjVWwyK0RNCkdEcEJtNjB0aHBZQVZBbTlHMTla
|
||||
cHduT1lyTWlESmQrVHB6NU9LTmU2MTQKLS0tIHkwQXM5TzZCRXFsOHdjYThZQ0Z2
|
||||
bS9wOTBDenRMb2hGcmgrcTl5R3dnTG8KZfSI1pnfpcqtD0Z5N6hONSeuAaggCkZI
|
||||
VJgh6TF7soOHbvddcNc5RINHQI6XLOZAeSOzvKm3zT8y5xe48Ff+3g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
- enc: |
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKY2xVSUVBUTd0dVhxekhY
|
||||
d2t1U00xNnRjTC9PT3puVGo1U2ViekhmQng4CnJlZWYxK0tyQ2U0Y3lXRUZSTEti
|
||||
cU9QdWhjalJhb3dJSk4vK1hWZGFGNEkKLS0tIG51aFRGcW9xdmdmSkt0TTZXSll4
|
||||
dkJMYjdPbUgyL1Rmc0tQbHgyR3RLZTAKllFoX3m/zntAtfGkSGFmRzXuk4pHALkR
|
||||
XeQaAl33n5dMiZHtynoDNN3eBXtDDWiKZAhZeWI5SMoLpWBEXapNag==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkb3ZmZmpPZDAzZzB5SmNx
|
||||
SHFKT3JENnVwZUttcFNYd3RoTHI2MCttQUNFCllZc2Z5Y1dSSnV0bW1LeGJvaURh
|
||||
TlZGWjhseUdjU1FRWE41aTlMczI0U28KLS0tIHpzajRWWmlUU3FCaEFWMlBsVjBT
|
||||
bGVRMGF2YjVwcVNCYkNEZVM1TmsyUncKYLscCS6BD3fwVmK3JnkISsEhXvhuRBnr
|
||||
tJ+v7mcEXj4pz8FjIyY6MG3T+EXx6+HwuWtPyxbmEfqnxu2Ocfg5Dg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
- enc: |
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsdUpxd0RYRld3L1BYdllp
|
||||
R3RwVStLUysyR1lXQ0RlcW4vWGRINzB3WUd3CktTSXJzUWsxcEdVajVjS3N1d0NE
|
||||
WHU4QkVvU3dyYWZ0VVpmYnB2blVWaGcKLS0tIG5sa1I2YW1tS0hLeGc1SWtjV0NM
|
||||
TkFoL0ZZbmhXdElFNytLclByK0s1LzgKVrBvlaryZXYn43v1ukjGZnhSPuwfslf7
|
||||
Ruy7z7EzVaCXRn+F9gABD4mhIQaUIRswiut7aF9lrPOR72/y8s/1JA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUSCtGaUdaR2pVekZCUUkz
|
||||
am1pRTJSdzgvUjJsTlpXVE0rVlczOFZ3aGpzClJNVXRJV2JqbHB5ZEUvUFJXR2cy
|
||||
MUpad0ZMYUI2MmtoUm8zcG1RS01WcmMKLS0tIGsvVGhITGRsQkJXcUdmQllOVEVr
|
||||
QnN5QVZXcFR0cDVab09hcEFiWjRoMmsKYo5ThhNgdp9GUXZkyF6JGGdWnmHmZK5c
|
||||
QIqk5L5fYq+Wif8bKUJBEv2fGyJJZD74AOCFjQUIDgBnLx2tK3Kf4Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiVHRnMTZXK1FNWmFJZlF5
|
||||
MWk5cjRHQ2NuRWJrMlhQVXhPL094elF1MW5VCkx5TklkR1VQSXc1OTljaXBFR2FQ
|
||||
WEtudU5DNUxTWGNpbUFESjBoSkh6bVUKLS0tIFFiV2NkcktOdnF1djY2UC93OVdF
|
||||
Unhpa0h5RU9xUHNic0hXVGhqSUthN3MKMAOF3gxkJVFU9aKqeUaViDs+Ka8NI4YQ
|
||||
Bb8wPf9BcQe0NPjVMrxxP7L9c5oACiiNuA/46YxMYu7K1dOj0KapGw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
lastmodified: "2026-06-01T21:23:34Z"
|
||||
mac: ENC[AES256_GCM,data:DXvUZUOMKQA0aDpN1foKy+aHhBh6daGSRJM1FagTh76qutk4XF9Een9iyy9sQ7olabCjf3oUezvU7XWB83bSDD3e23CFuweOl0RreRFqIsbXBi55Dbcxw8+9fvMyOK4PXu6mkT/PRao/XR5sSMqiHt3FoJbjazSaJlI5teeVXbM=,iv:IICO5ay+KqkTikfxW4kqB2qcSRpbsOP1NjGWtCB9uqI=,tag:MX2TqUxre+ff3RszCfXeFg==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
|
||||
@@ -8,38 +8,47 @@ sops:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGbTFyeCtJVWNOUXlSNXQ0
|
||||
MEF6aG84NEVNUzlrMnoxcEF0ZFpEVC9LK1ZvCk04TkNkVWtVMWhhL2QwVEVEMi9X
|
||||
M3Jkd1NoVFBFaDczMGhtN0svR284UzgKLS0tIE5OdTlXWXptTS9TZWdUNzFKU1VW
|
||||
QXl2N2I3dFFORlFEbUVSK2ppSmNHSk0KzeS7uJCvQLd97XI/MfKuhJi4pQ/jKx2d
|
||||
5veiGfnmI/j/WCBdH35PMTEotbBBQ26uFt+uvcShK2gO62MVbht8PQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVZnc3SXVxWlJHV2FlZU91
|
||||
Q2pVRXEvbFNOODVaQ2F3dzVva3o5RTI0TTFnCkI4MWN2TDR1dnR4OEhmZzhZMW1Z
|
||||
a2wxaGR5MlVNWmh5YkZwb2dTbGJJZncKLS0tIEUyVkhYZWJiQUhNNTNtS095ellm
|
||||
UnJBZGVPNC9lY0xKTGNldWs1aFJGSk0KvKBYSxJ0jwddf9OycUfrfYYmH8MoxIbC
|
||||
oO8nk2sfY9enGziu+A0GKEmCIzhBkD+Cj/8jyqTH56NKFykKNpFQyg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1duyl8mkpgu80uv934dy8q7enqjms6yvdz264hme8uryuxmvvqesq6rusq0
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjWG9PME1XQ1dSNkVEME9N
|
||||
ZlBNL0FnclpoeEVLTVk5emlnTmpFVWtLcVF3CmQrY1ExOG5wMmRoMkV4Y3hXSjU5
|
||||
OTZidFVWcklmZ0dHU0pJeTdiUTM5RzQKLS0tIHBGbncxRkpnUTVlNitRZkhyRjBE
|
||||
bjdVSkZRZ241OE9Xd240OExWVkFrczAK+loPUAs5aBMC6XRY+yu0r3bqnWozldP6
|
||||
m6mnRhUl2+JGm5RHglDWibUwPZ9I1EVlufDwDCABBnoXnY/HWuOlAw==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZMXpiYlVadUgwei8xU0tv
|
||||
bkpoK1RzS0tnYlJFN3hqRmJiK0Q2VVhyN1NjClZLRVZMcldNQ01yOVhQdnV2QkRE
|
||||
bUl3YTQwSHJPSmlpeEpqUG5tSnZRZ0UKLS0tIFVodEZtaVFoYzFkdGd6VkdIeW5P
|
||||
bHQvbnhYNnFuR0JGbkljdVRjVEZ6a3cKXccTdlgR94QmaeLGYnXXKOYFuJYkJ08Q
|
||||
JYkpwRlYzYMfqlYNuANi2LYTZePQITIYZ+A9sHajCXQlUGjs9PCWsA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxOG96YzNTQk1sQitQNnV3
|
||||
V3FsUEN5b00wcys5TmJSY0lTTm1qaEV1MGtZCnlYa3ZMNTFTdkliM0NjVW9BLzhj
|
||||
ZytCMDQrcjVmdEx0cjhNRllZY2xuWDAKLS0tIHhoMVR0KzhZb1dVMldIUjUyc2JS
|
||||
OHBSbVliajNSTDR5VW9odkdpN01iT0UKzifxPVQc98lEB4sXoDcDw7t3R396iwa7
|
||||
RENR54u5GYUHHySq14v4k+7zhV42+xaXW45ZPhcA4BKuaAw8ygSDGQ==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0bkppcWFWZkVxWGQzRzli
|
||||
bkMvemZ4SGJodEUycGVxNlZFYzJqQlpnZml3Cm9UNFBjd29MTm1NTXI2OWljNFRZ
|
||||
bGErNC8ydVFjTHNRcWpWTFFvNFQ1V2MKLS0tIHpXUzR1WGpPRnJNa3JEWFhFQ0N0
|
||||
ZWhnMzNEaTJ5RHRaRi9lMDZyVkxFd00K/zh7XlSjvO/we8GNDhKvhmPBAPZTi1o9
|
||||
kBruJ1OAFIJU4h/6dOmgGsj1Jxx/KZwMMAcHNzUBzm8x282MlFzM4g==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5R1EvVlZOOVMxSSsya2Fn
|
||||
Zm5iWWN6d2RFNzU5aXBNbHNkcnYwM1VLY1Z3CjlEazNpcnhQdTRFMTVxdWliV0Ju
|
||||
eEZMMnliS3BnU0RMSy9lNDZhVUw4K28KLS0tIG4vN01xZjRNSmVMVWx0VEJhd29Q
|
||||
QTR3cEhvTVowU2ZXNDY3ZjhSVktSdEEKTxL2M/OgOMcptliInoED6aRDEQaqGnXF
|
||||
N7dg2UnQsuFplJIfsW8KRoDQkcuXbdjn/W5aQh76+OC8wuRPVNWcEA==
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqUjZXMmpXTitCam1HbkIz
|
||||
Y1UxY0xLR25GbnJucTMxV2x5Wk9SZFVVUVFrCkJzQmE4cUkxSGV3VjAyVlZEQkda
|
||||
aW9SRmdGbWwxd0o3OTYvV2pmUHJxN2sKLS0tIG44cXZRTC9kSmJLcE1JSVVqWjYw
|
||||
QkQzTFZZWno5dHhmN2pEWXpycDdvUjQKmcbWAZh85vSozzsX4CndtrxneA3em3JG
|
||||
nhESflBPc/E2KckyTHOEJkPBq48UXiAvM3pY0s6FGgkhXHriTVJ7xA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNdWNGZi9JWkZnSjF5eUZN
|
||||
OUhtZ3B5aDVaV2dpU0liOHdtZitQSUcwbGpZCmttMEI3Mm94SnZwNEd4bUYrRHlD
|
||||
aGJBa3pKZ0VRVXlkL1JHVG41U24rU1kKLS0tIFEvYlliOE9iSHhIbHR3RVZNc0dx
|
||||
VDVkN3dYVEJXb2dPYjAzOFU5Z1VaU2MKzVcJ9/z4+9phsdwyowwK//Tpb0Ga8rwo
|
||||
nTcLYw50wOKKG4ju1ISB5RuuUckU34dmUV7k4se9/oxnnectFwYVgQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-05-21T20:13:20Z"
|
||||
mac: ENC[AES256_GCM,data:PXXEpDbJIoIwWuNwMetpALtxrcB7yJhDYqp6LSKV8WFQI2TJSDgDHiNQ2cngfB9PXKmUNNhlvhmB52Hi6WQSWQdXjiy79T1fOjMhbUAKmykTaZDrwvlrDGkPgiZ1cFSb8+hx/5aZw7YhdQO9+7LAmymeIldZIFEpEO2pvuPZ6MM=,iv:mJb3K8TnsnY183OX7o9pF7oVMDcwVU/rOgX74KTRpr4=,tag:f+M+FT0Vjozm7a8LFDI8lA==,type:str]
|
||||
|
||||
48
secrets/yuvomi-api-token.yaml
Normal file
48
secrets/yuvomi-api-token.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
token: ENC[AES256_GCM,data:LgiLFEsli412rVxSo9YU69wx9Lh7eDGBw6nmROh6Qbw7bYN8lylNjSDWkjNJ/UJvDg==,iv:vNmiV/D+SQaMtgwTK/mFDQIwSQ/L0JRatmoj5AWfBiw=,tag:W5P98TlmevQ0j051044LPQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQYVBwS250T0ZFa2VGbGow
|
||||
R0MwMzNsY0t2N1ZBVnlnbkF6UEIyUStaUEhJCnBuNWNZT1VxRFlKNFBjOTZHNEd0
|
||||
cCtxN1p3TjkzWUZNZkdZeXRJVEhaQzgKLS0tIFVSWUpVQ2k3VE83eHU4VmR4SnVH
|
||||
RVgxdDNnKzMwM2c2bm1YS1ZSRDdsMVkK8PVncSpesaxIEz7a39rVBzB0V3RphmAA
|
||||
ANV8fY4W4tLuxo+EhB90ciy2dnAHaKEbxHBWMMjN4u22yZRT4lPWQw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4Sk1hdENFQWIvZXhIK0U0
|
||||
TmhCWkw5TWhSVXdIbDN5SmVNdHkwYm1aOGxvCmlnQmZYWitxUmdURzNYRDBFUUVM
|
||||
TjdIN2FEd2ptWG5UVlFCb1IyVzFjMmsKLS0tIDJMZHlXVHFNUlY2bnhlS3hFT3FY
|
||||
U2szdVB4MGxxUnpJN1BBaUhKVE5WQkEKz7BkFIfs2RSyAww73dH5+PyDNjo4Ocor
|
||||
mX5pLhSRGGf13bfA5lMQiGvqtd3jw1c4zKVZD9BcthIk5H/oqallNQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmVWI2L05GbjdLR1k3a280
|
||||
N2RRWmZsZWZPeWdmd3I3aThrV1ppOEw4VTJRCit6aXgwTVIwQldkSmpRNkM4UTh4
|
||||
UEcyQjkrMFk2UGVBL1JZdGg2WDI3c0EKLS0tIGV5VkRWYWpXZXZGN1FXNmhTSmR5
|
||||
cjQ0TWg0TkFmMnIyMDNBNVZXa1l0REUKVHdvbXFwLgMECi2JLg8aoYIkOWHwYyZh
|
||||
v3mVVaaki5KceuyUhcGsdthNadrM0RDi89uUw8O2cfADdDgLlha/iQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1s07zs83ehtlg8jtwvr75ltc3c4cdlemfwjuxrwjtwkqxkl9tpggsyrzn2h
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnemkvTUxHZDNxYmJ4eGRK
|
||||
Y09laWNnN0pXN1VHQzUzSWlJZGFqcmkwTWdJCjkxZG9pVVVzWXBTU0lpL0JFR2w3
|
||||
TWhPTDNFSXhPTkVMVUpOY0xHMlN6RkkKLS0tIDB3d0ZyRVpGT3YzSXIrM1pPQ1Uz
|
||||
QkpQYXRWaGtabnU5c1RpeE9vb0MwMWcKVRnpepDJ8a2ECD5uaK6O3fMKwWcLJP8a
|
||||
uFWds5rvwDfrM1aNLXYbPIfXtqLx4fbxkoRZvHcuZ2/lYLDBFBrFlQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-06-28T14:02:13Z"
|
||||
mac: ENC[AES256_GCM,data:NDjBNtSzGdm0+SqAvaUlltljXcyXzLQx3b9RUWZIlS/D3ox1BTO6t3t4WT8szbSnKcT51mJGJ8EIqppNWkmOpEaMNGdMw70DmGnf/IJAzScTpRyvhfsGlohCVaekMBjF+wg1uoB91vSKcTFq/wfUW79KICmxMF6woavRQ94yfQs=,iv:1f9sGKyTITSslqDXB3Khk/OGiPaJM2BreSEG6aTbZDs=,tag:UulDl/JQamg/p/q4esvtIA==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.4
|
||||
Reference in New Issue
Block a user