Add Kopia backup stack with Backblaze B2 offsite target.
Protects /config, selective labdata, and nightly DB dumps; documents homelab improvement plan briefs.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Homelab improvements — plan brief
|
||||
|
||||
**How to use this:** Don't read everything. Pick something that sounds interesting and say it in chat — e.g. *"let's talk about backups"* or *"TB-003"*. We'll have a short conversation first. You can do, defer, drop, or change any item.
|
||||
|
||||
**Status:** Draft · **Host:** valhalla
|
||||
|
||||
---
|
||||
|
||||
## Talk first — plan level
|
||||
|
||||
Before any phase or task, we should answer:
|
||||
|
||||
1. **What feels urgent?** Backup? Security? Finishing romhacks? Something else entirely?
|
||||
2. **What can wait or die?** Throwing items out is success — tell me what doesn't interest you.
|
||||
3. **How do you want to work?** One item per session, or a theme (e.g. "media day")?
|
||||
|
||||
### What we decided (plan level)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Priority theme** | Backups (TB-001 in progress) |
|
||||
| **Explicitly dropped** | _(none yet)_ |
|
||||
| **Notes** | _(conversation notes go here)_ |
|
||||
|
||||
---
|
||||
|
||||
## Menu — pick one to talk about
|
||||
|
||||
Each link opens an **interview-first** task brief. The conversation is the task.
|
||||
|
||||
### Safety & quick wins
|
||||
|
||||
| Say this… | About |
|
||||
|-----------|--------|
|
||||
| [TB-001](tasks/TB-001-backup-stack.md) | Automated backups — you don't have any yet |
|
||||
| [TB-033](tasks/TB-033-public-surface-audit.md) | What's actually public on the internet? |
|
||||
| [TB-003](tasks/TB-003-recyclarr.md) | Sync *arr quality settings automatically |
|
||||
| [TB-004](tasks/TB-004-pin-infra-tags.md) | Stop `:latest` from breaking databases |
|
||||
| [TB-002](tasks/TB-002-jd-caddy-block.md) | JDownloader web UI on jd.ginnoir.com |
|
||||
|
||||
### Security & cleanup
|
||||
|
||||
| Say this… | About |
|
||||
|-----------|--------|
|
||||
| [TB-005](tasks/TB-005-obsidian-hardening.md) | Your Obsidian vault is public-facing |
|
||||
| [TB-006](tasks/TB-006-authentik-forward-auth.md) | Login on admin UIs via Authentik |
|
||||
| [TB-013](tasks/TB-013-code-server-mount.md) | code-server sees your whole home dir |
|
||||
| [TB-014](tasks/TB-014-registry-auth.md) | Docker registry has no password |
|
||||
| [TB-008](tasks/TB-008-retire-deluge.md) | Maybe remove Deluge (you have qBit too) |
|
||||
| [TB-009](tasks/TB-009-bookstack-decision.md) | BookStack vs Obsidian — pick one? |
|
||||
| [TB-010](tasks/TB-010-dev-stack-env-migration.md) | dev stack env file cleanup |
|
||||
| [TB-011](tasks/TB-011-reap-orphan-volumes.md) | Delete old Docker volumes |
|
||||
|
||||
### Finish what's started
|
||||
|
||||
| Say this… | About |
|
||||
|-----------|--------|
|
||||
| [TB-007](tasks/TB-007-romhacks-handoff.md) | ROM hacks pipeline → RomM (incomplete) |
|
||||
| [TB-012](tasks/TB-012-maintainerr.md) | Clean stale Overseerr requests |
|
||||
| [TB-016](tasks/TB-016-document-plex.md) | Write down how Plex fits in |
|
||||
|
||||
### New services (only if you want them)
|
||||
|
||||
| Say this… | About |
|
||||
|-----------|--------|
|
||||
| [TB-019](tasks/TB-019-immich.md) | Family photos |
|
||||
| [TB-020](tasks/TB-020-mealie.md) | Recipes |
|
||||
| [TB-021](tasks/TB-021-paperless-ngx.md) | Scan/OCR documents |
|
||||
| [TB-022](tasks/TB-022-readarr.md) | Automate ebooks |
|
||||
| [TB-023](tasks/TB-023-autobrr.md) | Torrent announce automation |
|
||||
| [TB-024](tasks/TB-024-syncthing.md) | Device file sync |
|
||||
| [TB-025](tasks/TB-025-metrics-beszel.md) | CPU/disk metrics dashboard |
|
||||
| [TB-026](tasks/TB-026-igir-container.md) | Automate ROM library cleanup |
|
||||
|
||||
### Maybe later (low pressure)
|
||||
|
||||
| Say this… | About |
|
||||
|-----------|--------|
|
||||
| [TB-015](tasks/TB-015-vault-secrets-integration.md) | Actually use Vault |
|
||||
| [TB-017](tasks/TB-017-minio-consolidation.md) | One MinIO instead of three |
|
||||
| [TB-018](tasks/TB-018-plane-evaluation.md) | Is Plane worth 7 containers? |
|
||||
| [TB-027](tasks/TB-027-jellyfin.md) | Replace Plex with Jellyfin? |
|
||||
| [TB-028](tasks/TB-028-home-assistant.md) | Home automation |
|
||||
| [TB-029](tasks/TB-029-firefly-iii.md) | Personal finance |
|
||||
| [TB-030](tasks/TB-030-adguard-home.md) | Network ad blocking / DNS |
|
||||
| [TB-031](tasks/TB-031-pingvin-share.md) | Temporary file share links |
|
||||
| [TB-032](tasks/TB-032-wikijs.md) | Wiki.js (only if keeping a wiki) |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Background (optional — skip if overwhelming)</summary>
|
||||
|
||||
Homelab is ~50 containers, 15 stacks, Caddy ingress, git-push deploys. Main gaps from analysis: no backups, Obsidian public, Authentik barely used, romhacks pipeline half-done.
|
||||
|
||||
Repo conventions: `stack.env` secrets, `edge` network, `internal_only` for LAN admin UIs, `./scripts/gen-bookmarks.ps1` after Caddy changes.
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,627 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate interview-first task briefs from structured data."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
TASKS = [
|
||||
{
|
||||
"id": "TB-001",
|
||||
"slug": "backup-stack",
|
||||
"title": "Backup stack",
|
||||
"sentence": "Set up automatic nightly backups of configs and databases, with a copy off the server.",
|
||||
"why": "Right now nothing backs up /config or your databases — ZFS snapshots don't help if the whole machine dies.",
|
||||
"questions": [
|
||||
"Does losing valhalla overnight scare you, or is 'I'll deal with it' okay for now?",
|
||||
"Do you already have offsite storage (Backblaze, Wasabi, another machine)?",
|
||||
"Kopia (has a UI) vs Restic (simpler CLI) — want a recommendation or don't care?",
|
||||
],
|
||||
"stack": "`backup` new stack",
|
||||
"steps": [
|
||||
"Pick Kopia or Restic",
|
||||
"New stacks/backup/ with nightly schedule + ntfy on failure",
|
||||
"Script dumps for all Postgres/MariaDB/CouchDB",
|
||||
"One restore test so we know it works",
|
||||
],
|
||||
"done": [
|
||||
"Nightly backup runs without you",
|
||||
"At least one restore tested",
|
||||
"Failures ping ntfy",
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "TB-002",
|
||||
"slug": "jd-caddy-block",
|
||||
"title": "jd.ginnoir.com",
|
||||
"sentence": "Add a LAN-only web address for JDownloader's UI.",
|
||||
"why": "It's documented but never added to the Caddyfile — you use port 8998 instead.",
|
||||
"questions": [
|
||||
"Are you actually using the romhacks/JDownloader pipeline, or is this premature?",
|
||||
"Is LAN-only enough, or do you need it over Tailscale too (already covered by internal_only)?",
|
||||
],
|
||||
"stack": "Caddyfile",
|
||||
"steps": [
|
||||
"Add jd.ginnoir.com block with internal_only",
|
||||
"gen-bookmarks.ps1 + push",
|
||||
],
|
||||
"done": ["jd.ginnoir.com works on LAN", "403 from outside"],
|
||||
},
|
||||
{
|
||||
"id": "TB-003",
|
||||
"slug": "recyclarr",
|
||||
"title": "Recyclarr",
|
||||
"sentence": "One container that keeps Sonarr/Radarr/Bazarr quality settings in sync with TRaSH guides.",
|
||||
"why": "Your *arr configs probably drift over time; this automates the boring sync.",
|
||||
"questions": [
|
||||
"Have you hand-tuned quality profiles you'd hate to have overwritten?",
|
||||
"Want this, or are your *arr settings 'set and forget' already?",
|
||||
],
|
||||
"stack": "stacks/media/",
|
||||
"steps": [
|
||||
"Add recyclarr container on media network",
|
||||
"Point at sonarr/radarr/bazarr/prowlarr APIs",
|
||||
"Run first sync, spot-check Sonarr",
|
||||
],
|
||||
"done": ["Recyclarr syncs without errors", "Profiles visible in Sonarr or Radarr"],
|
||||
},
|
||||
{
|
||||
"id": "TB-004",
|
||||
"slug": "pin-infra-tags",
|
||||
"title": "Pin infra image tags",
|
||||
"sentence": "Stop Postgres, MariaDB, Caddy, etc. from silently updating to a broken :latest.",
|
||||
"why": "Watchtower auto-updates containers; one bad DB image pull is painful.",
|
||||
"questions": [
|
||||
"Have you been burned by a surprise update before?",
|
||||
"Okay to manually bump pinned versions every few months?",
|
||||
],
|
||||
"stack": "All compose files",
|
||||
"steps": [
|
||||
"Audit :latest on infra images",
|
||||
"Pin versions + watchtower.enable=false on those",
|
||||
"Document policy in CLAUDE.md",
|
||||
],
|
||||
"done": ["No :latest on DB/Caddy/Vault/CouchDB/MinIO", "Stacks still deploy clean"],
|
||||
},
|
||||
{
|
||||
"id": "TB-033",
|
||||
"slug": "public-surface-audit",
|
||||
"title": "Public surface audit",
|
||||
"sentence": "A short conversation + checklist: what's reachable from the internet, and should it be?",
|
||||
"why": "Some things are public on purpose (Foundry, famapp); some might surprise you (Obsidian).",
|
||||
"questions": [
|
||||
"Want to walk through the list together in chat instead of reading a table?",
|
||||
"Any service you *know* should be public that we'd flag as wrong?",
|
||||
],
|
||||
"stack": "Caddyfile review only",
|
||||
"steps": [
|
||||
"Walk each public domain in conversation",
|
||||
"Mark: keep public / lock down / drop",
|
||||
"Save decisions here and in TB-005/TB-006 as needed",
|
||||
],
|
||||
"done": ["Every public site has your yes/no", "Action items linked to other TBs"],
|
||||
},
|
||||
{
|
||||
"id": "TB-005",
|
||||
"slug": "obsidian-hardening",
|
||||
"title": "Obsidian / CouchDB hardening",
|
||||
"sentence": "Your whole PKM vault syncs over a public URL — let's decide how exposed you're okay with.",
|
||||
"why": "obsidian.ginnoir.com has no LAN-only or SSO gate; LiveSync needs remote access from phones.",
|
||||
"questions": [
|
||||
"Do you sync Obsidian from your phone without Tailscale always on?",
|
||||
"Would 'Tailscale only' work, or do you need public access?",
|
||||
"Authentik login in front — okay if we test LiveSync still works?",
|
||||
],
|
||||
"stack": "Caddyfile, stacks/notes/",
|
||||
"steps": [
|
||||
"Pick model: Tailscale-only, Authentik, or hybrid",
|
||||
"Implement + test sync from each device",
|
||||
"Rotate CouchDB password",
|
||||
],
|
||||
"done": ["You can still sync where you need to", "Admin/_utils not wide open"],
|
||||
},
|
||||
{
|
||||
"id": "TB-006",
|
||||
"slug": "authentik-forward-auth",
|
||||
"title": "Authentik on admin UIs",
|
||||
"sentence": "Require Authentik login before Portainer, code-server, registry, etc.",
|
||||
"why": "LAN-only IP checks aren't the same as identity — Authentik is already running for famapp.",
|
||||
"questions": [
|
||||
"Which admin sites do you actually use? (No point gating ones you never open)",
|
||||
"Portainer git webhooks must keep working — okay to test carefully?",
|
||||
"Do this for everything internal, or just the scary ones (Portainer, code, vault)?",
|
||||
],
|
||||
"stack": "Caddyfile + Authentik UI",
|
||||
"steps": [
|
||||
"Authentik proxy provider + outpost",
|
||||
"Caddy forward_auth snippet",
|
||||
"Tier 1: portainer, code, registry-ui, vault, minio console",
|
||||
],
|
||||
"done": ["Tier 1 sites require login", "Portainer webhooks still work"],
|
||||
},
|
||||
{
|
||||
"id": "TB-007",
|
||||
"slug": "romhacks-handoff",
|
||||
"title": "Romhacks → RomM handoff",
|
||||
"sentence": "Finish the pipeline so downloaded ROM hacks land in RomM with cover art.",
|
||||
"why": "Orchestrator + JDownloader exist; moving files into the library isn't built yet.",
|
||||
"questions": [
|
||||
"Is the Discord/JDownloader pipeline armed, or still theoretical?",
|
||||
"Worth finishing before arming, or drop the whole romhacks stack?",
|
||||
"How much automation vs 'I'll move files manually'?",
|
||||
],
|
||||
"stack": "stacks/romhacks/orchestrator/",
|
||||
"steps": [
|
||||
"Complete handoff.py — incoming → Emulation/roms/<platform>",
|
||||
"RomM custom cover API",
|
||||
"Test with one staged folder",
|
||||
],
|
||||
"done": ["Test ROM in RomM after handoff", "DRY_RUN still safe"],
|
||||
},
|
||||
{
|
||||
"id": "TB-008",
|
||||
"slug": "retire-deluge",
|
||||
"title": "Retire Deluge",
|
||||
"sentence": "Remove Deluge if qBittorrent is doing all the torrent work.",
|
||||
"why": "You run Deluge, qBit, and NZBGet — two torrent clients may be leftover cruft.",
|
||||
"questions": [
|
||||
"When did you last use Deluge?",
|
||||
"Are Sonarr/Radarr pointed at qBit only? (We can check together)",
|
||||
],
|
||||
"stack": "stacks/media/, Caddyfile",
|
||||
"steps": [
|
||||
"Confirm zero Deluge torrents + no *arr dependency",
|
||||
"Remove service + Caddy block",
|
||||
],
|
||||
"done": ["Deluge gone", "qBit/NZBGet still fine"],
|
||||
},
|
||||
{
|
||||
"id": "TB-009",
|
||||
"slug": "bookstack-decision",
|
||||
"title": "BookStack vs Obsidian",
|
||||
"sentence": "Decide if docs.ginnoir.com (BookStack) stays or goes — Obsidian is already your main PKM.",
|
||||
"why": "Two wiki systems = drift and an extra MariaDB to feed.",
|
||||
"questions": [
|
||||
"Do you use BookStack at all? When did you last open it?",
|
||||
"If we export and remove it, anything lost?",
|
||||
"Or keep it for a specific use case — what?",
|
||||
],
|
||||
"stack": "stacks/dev/",
|
||||
"steps": [
|
||||
"Your call: retire, keep with boundary, or migrate to Wiki.js (TB-032)",
|
||||
"If retire: export → remove containers",
|
||||
],
|
||||
"done": ["Decision recorded", "If retired: BookStack containers gone"],
|
||||
},
|
||||
{
|
||||
"id": "TB-010",
|
||||
"slug": "dev-stack-env-migration",
|
||||
"title": "dev stack.env migration",
|
||||
"sentence": "Make the dev stack use stack.env like everything else — no Portainer UI env vars.",
|
||||
"why": "Every other stack commits secrets to stack.env; dev still needs UI mirroring (footgun).",
|
||||
"questions": [
|
||||
"Have you been bitten by dev env drift before?",
|
||||
"Worth an afternoon, or low priority housekeeping?",
|
||||
],
|
||||
"stack": "stacks/dev/",
|
||||
"steps": [
|
||||
"Move all ${VAR} into stack.env",
|
||||
"Deploy, verify Gitea/Plane/BookStack/code-server",
|
||||
"Clear Portainer UI env vars",
|
||||
],
|
||||
"done": ["Dev stack runs with env_file only"],
|
||||
},
|
||||
{
|
||||
"id": "TB-011",
|
||||
"slug": "reap-orphan-volumes",
|
||||
"title": "Reap orphan volumes",
|
||||
"sentence": "Delete old pre-split Docker named volumes that bind mounts replaced.",
|
||||
"why": "htpc-download-box_* and valhalla-lab_* volumes waste disk and confuse what's canonical.",
|
||||
"questions": [
|
||||
"Want me to SSH and list them with you before deleting anything?",
|
||||
"Backups done first? (TB-001)",
|
||||
],
|
||||
"stack": "SSH on valhalla only",
|
||||
"steps": [
|
||||
"docker volume ls — confirm orphans",
|
||||
"Spot-check bind mounts healthy",
|
||||
"docker volume rm",
|
||||
],
|
||||
"done": ["Orphan volumes gone", "All containers still healthy"],
|
||||
},
|
||||
{
|
||||
"id": "TB-012",
|
||||
"slug": "maintainerr",
|
||||
"title": "Maintainerr",
|
||||
"sentence": "Auto-clean stale Overseerr requests so the queue doesn't fill with junk.",
|
||||
"why": "requests.ginnoir.com is public; old unfulfilled requests pile up.",
|
||||
"questions": [
|
||||
"Is your Overseerr queue messy now, or is this preventive?",
|
||||
"How aggressive on age — 30 days? 90?",
|
||||
],
|
||||
"stack": "stacks/media/",
|
||||
"steps": [
|
||||
"Add maintainerr on media network",
|
||||
"Connect Overseerr + Sonarr/Radarr APIs",
|
||||
"Conservative cleanup rule first",
|
||||
],
|
||||
"done": ["Maintainerr connected", "One rule configured"],
|
||||
},
|
||||
{
|
||||
"id": "TB-013",
|
||||
"slug": "code-server-mount",
|
||||
"title": "Narrow code-server mount",
|
||||
"sentence": "Stop code-server from mounting your entire home directory.",
|
||||
"why": "code.ginnoir.com currently exposes /home/ginnoir — SSH keys, everything.",
|
||||
"questions": [
|
||||
"What folders do you actually edit in code-server?",
|
||||
"homelabstack + a projects dir enough?",
|
||||
],
|
||||
"stack": "stacks/dev/docker-compose.yml",
|
||||
"steps": [
|
||||
"Change mount to scoped path",
|
||||
"Symlink homelabstack if needed",
|
||||
],
|
||||
"done": [".ssh and unrelated home dirs not visible in IDE"],
|
||||
},
|
||||
{
|
||||
"id": "TB-014",
|
||||
"slug": "registry-auth",
|
||||
"title": "Registry authentication",
|
||||
"sentence": "Put a password on your private Docker registry.",
|
||||
"why": "registry.ginnoir.com is LAN-only but anyone on LAN/tailnet can push/pull.",
|
||||
"questions": [
|
||||
"Do you push images to it regularly, or is it barely used?",
|
||||
"Will famapp CI or github-runner need login updated?",
|
||||
],
|
||||
"stack": "stacks/dev/",
|
||||
"steps": [
|
||||
"htpasswd + registry auth env",
|
||||
"Test docker login/pull/push",
|
||||
],
|
||||
"done": ["Unauthenticated pull fails", "You can pull with creds"],
|
||||
},
|
||||
{
|
||||
"id": "TB-016",
|
||||
"slug": "document-plex",
|
||||
"title": "Document Plex",
|
||||
"sentence": "Write down where Plex lives and how it connects to Tautulli — it's not in this repo.",
|
||||
"why": "Tautulli reads /storage1/PMS logs but Plex isn't in compose; easy to forget how it's managed.",
|
||||
"questions": [
|
||||
"Is Plex on valhalla itself, or another box?",
|
||||
"Do you want it in compose eventually, or happy as host-native?",
|
||||
],
|
||||
"stack": "Docs only",
|
||||
"steps": [
|
||||
"You tell me: install path, systemd?, library paths",
|
||||
"Add short section to CLAUDE.md or EXTERNAL-SERVICES.md",
|
||||
],
|
||||
"done": ["Plex documented with paths"],
|
||||
},
|
||||
{
|
||||
"id": "TB-019",
|
||||
"slug": "immich",
|
||||
"title": "Immich (photos)",
|
||||
"sentence": "Self-hosted Google Photos — family photo backup with a phone app.",
|
||||
"why": "Natural fit next to famapp; you don't have photo backup today.",
|
||||
"questions": [
|
||||
"Is phone photo backup something you actively want?",
|
||||
"Public with Authentik, or LAN/tailnet only?",
|
||||
"Storage growth on ZFS — okay?",
|
||||
],
|
||||
"stack": "New photos stack",
|
||||
"steps": [
|
||||
"New stacks/photos/",
|
||||
"photos.ginnoir.com + optional Authentik",
|
||||
"Test upload from phone",
|
||||
],
|
||||
"done": ["Mobile upload works", "In backup scope"],
|
||||
},
|
||||
{
|
||||
"id": "TB-020",
|
||||
"slug": "mealie",
|
||||
"title": "Mealie (recipes)",
|
||||
"sentence": "Recipe manager with meal plans and shopping lists.",
|
||||
"why": "Pairs with famapp/family theme; lightweight stack.",
|
||||
"questions": [
|
||||
"Would you actually use this, or is it 'nice in theory'?",
|
||||
"Shared with family or just you?",
|
||||
],
|
||||
"stack": "New kitchen stack or famapp",
|
||||
"steps": ["New stack", "recipes.ginnoir.com", "Import one recipe as test"],
|
||||
"done": ["You'd use it — or we drop it"],
|
||||
},
|
||||
{
|
||||
"id": "TB-021",
|
||||
"slug": "paperless-ngx",
|
||||
"title": "Paperless-ngx",
|
||||
"sentence": "Drop PDFs in a folder → OCR → searchable archive.",
|
||||
"why": "ownCloud stores files; this adds document search and tagging.",
|
||||
"questions": [
|
||||
"Do you scan receipts/documents often enough to matter?",
|
||||
"Or is ownCloud + Obsidian enough?",
|
||||
],
|
||||
"stack": "New docs stack",
|
||||
"steps": ["New stack", "consume folder on ZFS", "Test one PDF OCR"],
|
||||
"done": ["Search finds text in test doc — or we skip"],
|
||||
},
|
||||
{
|
||||
"id": "TB-022",
|
||||
"slug": "readarr",
|
||||
"title": "Readarr (ebooks)",
|
||||
"sentence": "Automate ebook downloads like Sonarr does for TV.",
|
||||
"why": "You have /storage1/Books in ownCloud; no *arr for books yet.",
|
||||
"questions": [
|
||||
"Do you acquire ebooks automatically, or buy/download manually?",
|
||||
"Audiobooks too? (Different tool — Audiobookshelf)",
|
||||
],
|
||||
"stack": "stacks/media/",
|
||||
"steps": [
|
||||
"Add readarr + Caddy internal block",
|
||||
"Hook Prowlarr + download client",
|
||||
],
|
||||
"done": ["Test grab lands in Books — or drop"],
|
||||
},
|
||||
{
|
||||
"id": "TB-023",
|
||||
"slug": "autobrr",
|
||||
"title": "Autobrr",
|
||||
"sentence": "Monitor private tracker IRC/RSS and auto-push to qBittorrent.",
|
||||
"why": "For active private tracker use — overkill if you're mostly public indexers via Prowlarr.",
|
||||
"questions": [
|
||||
"Are you on private trackers that need announce monitoring?",
|
||||
"Or skip — Prowlarr enough?",
|
||||
],
|
||||
"stack": "stacks/media/",
|
||||
"steps": ["Add autobrr", "Connect qBit", "One filter if proceeding"],
|
||||
"done": ["Worth it for your tracker habits — or dropped"],
|
||||
},
|
||||
{
|
||||
"id": "TB-024",
|
||||
"slug": "syncthing",
|
||||
"title": "Syncthing",
|
||||
"sentence": "Continuous folder sync between devices — ROM saves, dotfiles, etc.",
|
||||
"why": "Different from ownCloud's model; good for device-to-device without 'upload to server'.",
|
||||
"questions": [
|
||||
"What folders would you sync? (Be specific — not all of /storage1)",
|
||||
"Conflict risk with RomM/ownCloud on same paths?",
|
||||
],
|
||||
"stack": "New sync stack",
|
||||
"steps": ["New stack", "Pair two devices", "One test folder"],
|
||||
"done": ["Sync works on chosen folder — or drop"],
|
||||
},
|
||||
{
|
||||
"id": "TB-025",
|
||||
"slug": "metrics-beszel",
|
||||
"title": "Metrics (Beszel/Netdata)",
|
||||
"sentence": "See CPU/RAM/disk graphs — Uptime Kuma only tells you up/down.",
|
||||
"why": "Homelab has no metrics dashboard today.",
|
||||
"questions": [
|
||||
"Do you ever wonder 'why is the server slow' without an answer?",
|
||||
"Beszel (minimal) vs Netdata (detailed) — or skip?",
|
||||
],
|
||||
"stack": "stacks/monitoring/",
|
||||
"steps": ["Add chosen tool", "metrics.ginnoir.com internal"],
|
||||
"done": ["Dashboard shows host + container usage"],
|
||||
},
|
||||
{
|
||||
"id": "TB-026",
|
||||
"slug": "igir-container",
|
||||
"title": "IGIR container",
|
||||
"sentence": "Schedule your existing ROM cleanup scripts instead of SSH ad-hoc.",
|
||||
"why": "igir-*.sh scripts exist; romhacks handoff will add more ROMs to maintain.",
|
||||
"questions": [
|
||||
"Do you run IGIR often enough to automate?",
|
||||
"Or manual is fine?",
|
||||
],
|
||||
"stack": "stacks/roms/",
|
||||
"steps": [
|
||||
"Container wrapping existing scripts",
|
||||
"Weekly dry-run report → ntfy",
|
||||
],
|
||||
"done": ["Scheduled dry-run runs — or drop"],
|
||||
},
|
||||
{
|
||||
"id": "TB-015",
|
||||
"slug": "vault-secrets-integration",
|
||||
"title": "Use Vault for real",
|
||||
"sentence": "Vault runs but secrets live in stack.env — integrate Vault for CI/rotation without changing git-canonical deploy.",
|
||||
"why": "Optional complexity; only worth it if you want rotation or CI secret centralization.",
|
||||
"questions": [
|
||||
"What problem would Vault solve that stack.env doesn't?",
|
||||
"If 'nothing really' — drop this.",
|
||||
],
|
||||
"stack": "portainer-compose.yml, scripts",
|
||||
"steps": [
|
||||
"Define Vault vs stack.env boundary",
|
||||
"Migrate one token (e.g. github-runner)",
|
||||
],
|
||||
"done": ["Clear use case — or dropped"],
|
||||
},
|
||||
{
|
||||
"id": "TB-017",
|
||||
"slug": "minio-consolidation",
|
||||
"title": "One MinIO instead of three",
|
||||
"sentence": "resume, famapp, and Plane each run their own MinIO — could be one server, three buckets.",
|
||||
"why": "Less ops; slightly higher blast radius if MinIO dies.",
|
||||
"questions": [
|
||||
"Do the three MinIO instances cause you pain today?",
|
||||
"If no — drop; isolation is fine.",
|
||||
],
|
||||
"stack": "resume, famapp, dev",
|
||||
"steps": ["Decision", "If yes: migrate buckets, update env vars"],
|
||||
"done": ["Consolidated or explicitly kept separate"],
|
||||
},
|
||||
{
|
||||
"id": "TB-018",
|
||||
"slug": "plane-evaluation",
|
||||
"title": "Is Plane worth it?",
|
||||
"sentence": "Plane is 7 containers for project management — do you use it?",
|
||||
"why": "Gitea has issues; Homarr is your dashboard; Plane may be shelfware.",
|
||||
"questions": [
|
||||
"When did you last open plane.ginnoir.com?",
|
||||
"Gitea issues enough, or need a kanban?",
|
||||
],
|
||||
"stack": "stacks/dev/",
|
||||
"steps": ["Your usage answer", "Keep, or remove and reclaim resources"],
|
||||
"done": ["Decision recorded"],
|
||||
},
|
||||
{
|
||||
"id": "TB-027",
|
||||
"slug": "jellyfin",
|
||||
"title": "Jellyfin instead of Plex",
|
||||
"sentence": "Self-host streaming in Docker instead of host-native Plex.",
|
||||
"why": "Only if you want to move off Plex licensing / centralize in compose.",
|
||||
"questions": [
|
||||
"Happy with Plex today?",
|
||||
"Family/clients okay switching apps?",
|
||||
"Hardware transcoding needed?",
|
||||
],
|
||||
"stack": "media or new streaming",
|
||||
"steps": ["Decision", "If yes: jellyfin + GPU passthrough eval"],
|
||||
"done": ["Stay on Plex or migrate plan documented"],
|
||||
},
|
||||
{
|
||||
"id": "TB-028",
|
||||
"slug": "home-assistant",
|
||||
"title": "Home Assistant",
|
||||
"sentence": "Home automation hub — only makes sense if you have smart devices to control.",
|
||||
"why": "Zero home automation in the stack today.",
|
||||
"questions": [
|
||||
"How many smart devices do you have?",
|
||||
"Zigbee/Z-Wave USB stick on valhalla?",
|
||||
"If <5 devices — probably skip.",
|
||||
],
|
||||
"stack": "New home stack",
|
||||
"steps": ["Go/no-go", "If go: HA + USB passthrough"],
|
||||
"done": ["Go/no-go with reason"],
|
||||
},
|
||||
{
|
||||
"id": "TB-029",
|
||||
"slug": "firefly-iii",
|
||||
"title": "Firefly III (finance)",
|
||||
"sentence": "Self-hosted budgeting and transaction tracking.",
|
||||
"why": "Niche personal finance; sensitive data.",
|
||||
"questions": [
|
||||
"Do you want to self-host finances, or use a SaaS/bank app?",
|
||||
"If yes — internal-only + Authentik mandatory.",
|
||||
],
|
||||
"stack": "New finance stack",
|
||||
"steps": ["New stack", "internal-only Caddy"],
|
||||
"done": ["Using it — or dropped"],
|
||||
},
|
||||
{
|
||||
"id": "TB-030",
|
||||
"slug": "adguard-home",
|
||||
"title": "AdGuard Home",
|
||||
"sentence": "Network-wide ad blocking and local DNS for *.ginnoir.com.",
|
||||
"why": "Stops LAN hairpin through Cloudflare; blocks ads if router DNS points here.",
|
||||
"questions": [
|
||||
"Does your router already do ad blocking / local DNS?",
|
||||
"Willing to point DHCP DNS at valhalla?",
|
||||
],
|
||||
"stack": "New network stack or mgmt",
|
||||
"steps": ["Check router first", "If gap: AdGuard + DNS rewrites"],
|
||||
"done": ["Router sufficient — or AdGuard deployed"],
|
||||
},
|
||||
{
|
||||
"id": "TB-031",
|
||||
"slug": "pingvin-share",
|
||||
"title": "Pingvin Share",
|
||||
"sentence": "Temporary password-protected file links — lighter than ownCloud shares.",
|
||||
"why": "One-off 'here's a file' drops; ownCloud is heavier.",
|
||||
"questions": [
|
||||
"Do you send ad-hoc files to people often?",
|
||||
"Or rare enough to skip?",
|
||||
],
|
||||
"stack": "stacks/share/",
|
||||
"steps": ["Add to share stack", "drop.ginnoir.com or similar"],
|
||||
"done": ["Test share link — or drop"],
|
||||
},
|
||||
{
|
||||
"id": "TB-032",
|
||||
"slug": "wikijs",
|
||||
"title": "Wiki.js",
|
||||
"sentence": "Modern wiki — only relevant if TB-009 says 'keep a structured wiki' but not BookStack.",
|
||||
"why": "Depends on TB-009 decision; don't start here first.",
|
||||
"questions": [
|
||||
"Did TB-009 land on 'keep wiki'?",
|
||||
"If BookStack retired or Obsidian-only — skip this entirely.",
|
||||
],
|
||||
"stack": "stacks/dev/",
|
||||
"steps": ["Only if TB-009 = Option C", "Import + replace BookStack"],
|
||||
"done": ["TB-009 dependency satisfied — or N/A"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def render(task: dict) -> str:
|
||||
qs = "\n".join(f"{i}. {q}" for i, q in enumerate(task["questions"], 1))
|
||||
steps = "\n".join(f"{i}. {s}" for i, s in enumerate(task["steps"], 1))
|
||||
done = "\n".join(f"- [ ] {d}" for d in task["done"])
|
||||
return f"""# {task["id"]} — {task["title"]}
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do {task["id"]}"** or **"{task["title"].lower()}"**.
|
||||
|
||||
**In one sentence:** {task["sentence"]}
|
||||
|
||||
**Why it came up:** {task["why"]}
|
||||
|
||||
### Questions
|
||||
|
||||
{qs}
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
{task["stack"]}
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
{steps}
|
||||
|
||||
### Done when
|
||||
|
||||
{done}
|
||||
|
||||
</details>
|
||||
"""
|
||||
|
||||
|
||||
def main() -> None:
|
||||
out_dir = Path(__file__).parent / "tasks"
|
||||
for task in TASKS:
|
||||
path = out_dir / f"{task['id']}-{task['slug']}.md"
|
||||
path.write_text(render(task), encoding="utf-8", newline="\n")
|
||||
print(f"wrote {path.name}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,69 @@
|
||||
# Task menu
|
||||
|
||||
**Don't read a table and pick.** Say something in chat — e.g. *"backups"*, *"TB-005"*, *"what's public?"* — and we'll talk through that one item.
|
||||
|
||||
Parent: [PLAN-BRIEF.md](../PLAN-BRIEF.md) · Template: [_TEMPLATE.md](_TEMPLATE.md)
|
||||
|
||||
---
|
||||
|
||||
## How this works
|
||||
|
||||
1. You name a topic (or ask "what should I care about?")
|
||||
2. We go through that task's **Talk first** questions together
|
||||
3. You pick: **Do it / Later / Drop / Tweak**
|
||||
4. I fill in **What we decided** in the brief
|
||||
5. Reference section only matters if we're implementing
|
||||
|
||||
---
|
||||
|
||||
## Quick pick by vibe
|
||||
|
||||
| If you're thinking… | Open |
|
||||
|---------------------|------|
|
||||
| "I could lose everything" | [TB-001](TB-001-backup-stack.md) |
|
||||
| "What's exposed?" | [TB-033](TB-033-public-surface-audit.md) |
|
||||
| "Obsidian / vault security" | [TB-005](TB-005-obsidian-hardening.md) |
|
||||
| "ROM hacks pipeline" | [TB-007](TB-007-romhacks-handoff.md) |
|
||||
| "Family / photos / recipes" | [TB-019](TB-019-immich.md) · [TB-020](TB-020-mealie.md) |
|
||||
| "Media stack tuning" | [TB-003](TB-003-recyclarr.md) · [TB-008](TB-008-retire-deluge.md) |
|
||||
| "Cleanup / cruft" | [TB-009](TB-009-bookstack-decision.md) · [TB-011](TB-011-reap-orphan-volumes.md) · [TB-018](TB-018-plane-evaluation.md) |
|
||||
|
||||
---
|
||||
|
||||
## All tasks
|
||||
|
||||
| ID | One line | Brief |
|
||||
|----|----------|-------|
|
||||
| TB-001 | Automated backups | [→](TB-001-backup-stack.md) |
|
||||
| TB-002 | jd.ginnoir.com | [→](TB-002-jd-caddy-block.md) |
|
||||
| TB-003 | Recyclarr | [→](TB-003-recyclarr.md) |
|
||||
| TB-004 | Pin infra tags | [→](TB-004-pin-infra-tags.md) |
|
||||
| TB-005 | Obsidian hardening | [→](TB-005-obsidian-hardening.md) |
|
||||
| TB-006 | Authentik admin login | [→](TB-006-authentik-forward-auth.md) |
|
||||
| TB-007 | Romhacks handoff | [→](TB-007-romhacks-handoff.md) |
|
||||
| TB-008 | Retire Deluge | [→](TB-008-retire-deluge.md) |
|
||||
| TB-009 | BookStack decision | [→](TB-009-bookstack-decision.md) |
|
||||
| TB-010 | dev stack.env | [→](TB-010-dev-stack-env-migration.md) |
|
||||
| TB-011 | Orphan volumes | [→](TB-011-reap-orphan-volumes.md) |
|
||||
| TB-012 | Maintainerr | [→](TB-012-maintainerr.md) |
|
||||
| TB-013 | code-server mount | [→](TB-013-code-server-mount.md) |
|
||||
| TB-014 | Registry auth | [→](TB-014-registry-auth.md) |
|
||||
| TB-015 | Vault integration | [→](TB-015-vault-secrets-integration.md) |
|
||||
| TB-016 | Document Plex | [→](TB-016-document-plex.md) |
|
||||
| TB-017 | MinIO consolidation | [→](TB-017-minio-consolidation.md) |
|
||||
| TB-018 | Plane evaluation | [→](TB-018-plane-evaluation.md) |
|
||||
| TB-019 | Immich | [→](TB-019-immich.md) |
|
||||
| TB-020 | Mealie | [→](TB-020-mealie.md) |
|
||||
| TB-021 | Paperless-ngx | [→](TB-021-paperless-ngx.md) |
|
||||
| TB-022 | Readarr | [→](TB-022-readarr.md) |
|
||||
| TB-023 | Autobrr | [→](TB-023-autobrr.md) |
|
||||
| TB-024 | Syncthing | [→](TB-024-syncthing.md) |
|
||||
| TB-025 | Metrics | [→](TB-025-metrics-beszel.md) |
|
||||
| TB-026 | IGIR container | [→](TB-026-igir-container.md) |
|
||||
| TB-027 | Jellyfin | [→](TB-027-jellyfin.md) |
|
||||
| TB-028 | Home Assistant | [→](TB-028-home-assistant.md) |
|
||||
| TB-029 | Firefly III | [→](TB-029-firefly-iii.md) |
|
||||
| TB-030 | AdGuard Home | [→](TB-030-adguard-home.md) |
|
||||
| TB-031 | Pingvin Share | [→](TB-031-pingvin-share.md) |
|
||||
| TB-032 | Wiki.js | [→](TB-032-wikijs.md) |
|
||||
| TB-033 | Public audit | [→](TB-033-public-surface-audit.md) |
|
||||
@@ -0,0 +1,62 @@
|
||||
# TB-001 — Backup stack
|
||||
|
||||
**Status:** in progress — stack drafted, needs B2 + Portainer deploy
|
||||
**Your call:** **Do it**
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-001"** or **"backup stack"**.
|
||||
|
||||
**In one sentence:** Set up automatic nightly backups of configs and databases, with a copy off the server.
|
||||
|
||||
**Why it came up:** Right now nothing backs up /config or your databases — ZFS snapshots don't help if the whole machine dies.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Does losing valhalla overnight scare you, or is 'I'll deal with it' okay for now?
|
||||
2. Do you already have offsite storage (Backblaze, Wasabi, another machine)?
|
||||
3. Kopia (has a UI) vs Restic (simpler CLI) — want a recommendation or don't care?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | Do it |
|
||||
| **Notes** | Kopia + B2 TBD. `/config` + dumps (RomM DB). labdata: gitea, minio, romhacks, romm/assets (saves). **Skip:** entire `/storage1/Emulation` (legacy), romm/resources (art), registry, incoming. See `SCOPE.md`. |
|
||||
| **Date** | 2026-06-10 |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
`backup` new stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Pick Kopia or Restic
|
||||
2. New stacks/backup/ with nightly schedule + ntfy on failure
|
||||
3. Script dumps for all Postgres/MariaDB/CouchDB
|
||||
4. One restore test so we know it works
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Nightly backup runs without you
|
||||
- [ ] At least one restore tested
|
||||
- [ ] Failures ping ntfy
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-002 — jd.ginnoir.com
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-002"** or **"jd.ginnoir.com"**.
|
||||
|
||||
**In one sentence:** Add a LAN-only web address for JDownloader's UI.
|
||||
|
||||
**Why it came up:** It's documented but never added to the Caddyfile — you use port 8998 instead.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Are you actually using the romhacks/JDownloader pipeline, or is this premature?
|
||||
2. Is LAN-only enough, or do you need it over Tailscale too (already covered by internal_only)?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
Caddyfile
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add jd.ginnoir.com block with internal_only
|
||||
2. gen-bookmarks.ps1 + push
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] jd.ginnoir.com works on LAN
|
||||
- [ ] 403 from outside
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-003 — Recyclarr
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-003"** or **"recyclarr"**.
|
||||
|
||||
**In one sentence:** One container that keeps Sonarr/Radarr/Bazarr quality settings in sync with TRaSH guides.
|
||||
|
||||
**Why it came up:** Your *arr configs probably drift over time; this automates the boring sync.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Have you hand-tuned quality profiles you'd hate to have overwritten?
|
||||
2. Want this, or are your *arr settings 'set and forget' already?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/media/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add recyclarr container on media network
|
||||
2. Point at sonarr/radarr/bazarr/prowlarr APIs
|
||||
3. Run first sync, spot-check Sonarr
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Recyclarr syncs without errors
|
||||
- [ ] Profiles visible in Sonarr or Radarr
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-004 — Pin infra image tags
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-004"** or **"pin infra image tags"**.
|
||||
|
||||
**In one sentence:** Stop Postgres, MariaDB, Caddy, etc. from silently updating to a broken :latest.
|
||||
|
||||
**Why it came up:** Watchtower auto-updates containers; one bad DB image pull is painful.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Have you been burned by a surprise update before?
|
||||
2. Okay to manually bump pinned versions every few months?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
All compose files
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Audit :latest on infra images
|
||||
2. Pin versions + watchtower.enable=false on those
|
||||
3. Document policy in CLAUDE.md
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] No :latest on DB/Caddy/Vault/CouchDB/MinIO
|
||||
- [ ] Stacks still deploy clean
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,60 @@
|
||||
# TB-005 — Obsidian / CouchDB hardening
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-005"** or **"obsidian / couchdb hardening"**.
|
||||
|
||||
**In one sentence:** Your whole PKM vault syncs over a public URL — let's decide how exposed you're okay with.
|
||||
|
||||
**Why it came up:** obsidian.ginnoir.com has no LAN-only or SSO gate; LiveSync needs remote access from phones.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you sync Obsidian from your phone without Tailscale always on?
|
||||
2. Would 'Tailscale only' work, or do you need public access?
|
||||
3. Authentik login in front — okay if we test LiveSync still works?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
Caddyfile, stacks/notes/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Pick model: Tailscale-only, Authentik, or hybrid
|
||||
2. Implement + test sync from each device
|
||||
3. Rotate CouchDB password
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] You can still sync where you need to
|
||||
- [ ] Admin/_utils not wide open
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,60 @@
|
||||
# TB-006 — Authentik on admin UIs
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-006"** or **"authentik on admin uis"**.
|
||||
|
||||
**In one sentence:** Require Authentik login before Portainer, code-server, registry, etc.
|
||||
|
||||
**Why it came up:** LAN-only IP checks aren't the same as identity — Authentik is already running for famapp.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Which admin sites do you actually use? (No point gating ones you never open)
|
||||
2. Portainer git webhooks must keep working — okay to test carefully?
|
||||
3. Do this for everything internal, or just the scary ones (Portainer, code, vault)?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
Caddyfile + Authentik UI
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Authentik proxy provider + outpost
|
||||
2. Caddy forward_auth snippet
|
||||
3. Tier 1: portainer, code, registry-ui, vault, minio console
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Tier 1 sites require login
|
||||
- [ ] Portainer webhooks still work
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,60 @@
|
||||
# TB-007 — Romhacks → RomM handoff
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-007"** or **"romhacks → romm handoff"**.
|
||||
|
||||
**In one sentence:** Finish the pipeline so downloaded ROM hacks land in RomM with cover art.
|
||||
|
||||
**Why it came up:** Orchestrator + JDownloader exist; moving files into the library isn't built yet.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Is the Discord/JDownloader pipeline armed, or still theoretical?
|
||||
2. Worth finishing before arming, or drop the whole romhacks stack?
|
||||
3. How much automation vs 'I'll move files manually'?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/romhacks/orchestrator/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Complete handoff.py — incoming → Emulation/roms/<platform>
|
||||
2. RomM custom cover API
|
||||
3. Test with one staged folder
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Test ROM in RomM after handoff
|
||||
- [ ] DRY_RUN still safe
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-008 — Retire Deluge
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-008"** or **"retire deluge"**.
|
||||
|
||||
**In one sentence:** Remove Deluge if qBittorrent is doing all the torrent work.
|
||||
|
||||
**Why it came up:** You run Deluge, qBit, and NZBGet — two torrent clients may be leftover cruft.
|
||||
|
||||
### Questions
|
||||
|
||||
1. When did you last use Deluge?
|
||||
2. Are Sonarr/Radarr pointed at qBit only? (We can check together)
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/media/, Caddyfile
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Confirm zero Deluge torrents + no *arr dependency
|
||||
2. Remove service + Caddy block
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Deluge gone
|
||||
- [ ] qBit/NZBGet still fine
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-009 — BookStack vs Obsidian
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-009"** or **"bookstack vs obsidian"**.
|
||||
|
||||
**In one sentence:** Decide if docs.ginnoir.com (BookStack) stays or goes — Obsidian is already your main PKM.
|
||||
|
||||
**Why it came up:** Two wiki systems = drift and an extra MariaDB to feed.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you use BookStack at all? When did you last open it?
|
||||
2. If we export and remove it, anything lost?
|
||||
3. Or keep it for a specific use case — what?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Your call: retire, keep with boundary, or migrate to Wiki.js (TB-032)
|
||||
2. If retire: export → remove containers
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Decision recorded
|
||||
- [ ] If retired: BookStack containers gone
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-010 — dev stack.env migration
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-010"** or **"dev stack.env migration"**.
|
||||
|
||||
**In one sentence:** Make the dev stack use stack.env like everything else — no Portainer UI env vars.
|
||||
|
||||
**Why it came up:** Every other stack commits secrets to stack.env; dev still needs UI mirroring (footgun).
|
||||
|
||||
### Questions
|
||||
|
||||
1. Have you been bitten by dev env drift before?
|
||||
2. Worth an afternoon, or low priority housekeeping?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Move all ${VAR} into stack.env
|
||||
2. Deploy, verify Gitea/Plane/BookStack/code-server
|
||||
3. Clear Portainer UI env vars
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Dev stack runs with env_file only
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-011 — Reap orphan volumes
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-011"** or **"reap orphan volumes"**.
|
||||
|
||||
**In one sentence:** Delete old pre-split Docker named volumes that bind mounts replaced.
|
||||
|
||||
**Why it came up:** htpc-download-box_* and valhalla-lab_* volumes waste disk and confuse what's canonical.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Want me to SSH and list them with you before deleting anything?
|
||||
2. Backups done first? (TB-001)
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
SSH on valhalla only
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. docker volume ls — confirm orphans
|
||||
2. Spot-check bind mounts healthy
|
||||
3. docker volume rm
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Orphan volumes gone
|
||||
- [ ] All containers still healthy
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-012 — Maintainerr
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-012"** or **"maintainerr"**.
|
||||
|
||||
**In one sentence:** Auto-clean stale Overseerr requests so the queue doesn't fill with junk.
|
||||
|
||||
**Why it came up:** requests.ginnoir.com is public; old unfulfilled requests pile up.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Is your Overseerr queue messy now, or is this preventive?
|
||||
2. How aggressive on age — 30 days? 90?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/media/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add maintainerr on media network
|
||||
2. Connect Overseerr + Sonarr/Radarr APIs
|
||||
3. Conservative cleanup rule first
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Maintainerr connected
|
||||
- [ ] One rule configured
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-013 — Narrow code-server mount
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-013"** or **"narrow code-server mount"**.
|
||||
|
||||
**In one sentence:** Stop code-server from mounting your entire home directory.
|
||||
|
||||
**Why it came up:** code.ginnoir.com currently exposes /home/ginnoir — SSH keys, everything.
|
||||
|
||||
### Questions
|
||||
|
||||
1. What folders do you actually edit in code-server?
|
||||
2. homelabstack + a projects dir enough?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/docker-compose.yml
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Change mount to scoped path
|
||||
2. Symlink homelabstack if needed
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] .ssh and unrelated home dirs not visible in IDE
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-014 — Registry authentication
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-014"** or **"registry authentication"**.
|
||||
|
||||
**In one sentence:** Put a password on your private Docker registry.
|
||||
|
||||
**Why it came up:** registry.ginnoir.com is LAN-only but anyone on LAN/tailnet can push/pull.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you push images to it regularly, or is it barely used?
|
||||
2. Will famapp CI or github-runner need login updated?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. htpasswd + registry auth env
|
||||
2. Test docker login/pull/push
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Unauthenticated pull fails
|
||||
- [ ] You can pull with creds
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-015 — Use Vault for real
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-015"** or **"use vault for real"**.
|
||||
|
||||
**In one sentence:** Vault runs but secrets live in stack.env — integrate Vault for CI/rotation without changing git-canonical deploy.
|
||||
|
||||
**Why it came up:** Optional complexity; only worth it if you want rotation or CI secret centralization.
|
||||
|
||||
### Questions
|
||||
|
||||
1. What problem would Vault solve that stack.env doesn't?
|
||||
2. If 'nothing really' — drop this.
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
portainer-compose.yml, scripts
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Define Vault vs stack.env boundary
|
||||
2. Migrate one token (e.g. github-runner)
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Clear use case — or dropped
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-016 — Document Plex
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-016"** or **"document plex"**.
|
||||
|
||||
**In one sentence:** Write down where Plex lives and how it connects to Tautulli — it's not in this repo.
|
||||
|
||||
**Why it came up:** Tautulli reads /storage1/PMS logs but Plex isn't in compose; easy to forget how it's managed.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Is Plex on valhalla itself, or another box?
|
||||
2. Do you want it in compose eventually, or happy as host-native?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
Docs only
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. You tell me: install path, systemd?, library paths
|
||||
2. Add short section to CLAUDE.md or EXTERNAL-SERVICES.md
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Plex documented with paths
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-017 — One MinIO instead of three
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-017"** or **"one minio instead of three"**.
|
||||
|
||||
**In one sentence:** resume, famapp, and Plane each run their own MinIO — could be one server, three buckets.
|
||||
|
||||
**Why it came up:** Less ops; slightly higher blast radius if MinIO dies.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do the three MinIO instances cause you pain today?
|
||||
2. If no — drop; isolation is fine.
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
resume, famapp, dev
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Decision
|
||||
2. If yes: migrate buckets, update env vars
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Consolidated or explicitly kept separate
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-018 — Is Plane worth it?
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-018"** or **"is plane worth it?"**.
|
||||
|
||||
**In one sentence:** Plane is 7 containers for project management — do you use it?
|
||||
|
||||
**Why it came up:** Gitea has issues; Homarr is your dashboard; Plane may be shelfware.
|
||||
|
||||
### Questions
|
||||
|
||||
1. When did you last open plane.ginnoir.com?
|
||||
2. Gitea issues enough, or need a kanban?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Your usage answer
|
||||
2. Keep, or remove and reclaim resources
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Decision recorded
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,60 @@
|
||||
# TB-019 — Immich (photos)
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-019"** or **"immich (photos)"**.
|
||||
|
||||
**In one sentence:** Self-hosted Google Photos — family photo backup with a phone app.
|
||||
|
||||
**Why it came up:** Natural fit next to famapp; you don't have photo backup today.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Is phone photo backup something you actively want?
|
||||
2. Public with Authentik, or LAN/tailnet only?
|
||||
3. Storage growth on ZFS — okay?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New photos stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. New stacks/photos/
|
||||
2. photos.ginnoir.com + optional Authentik
|
||||
3. Test upload from phone
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Mobile upload works
|
||||
- [ ] In backup scope
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-020 — Mealie (recipes)
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-020"** or **"mealie (recipes)"**.
|
||||
|
||||
**In one sentence:** Recipe manager with meal plans and shopping lists.
|
||||
|
||||
**Why it came up:** Pairs with famapp/family theme; lightweight stack.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Would you actually use this, or is it 'nice in theory'?
|
||||
2. Shared with family or just you?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New kitchen stack or famapp
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. New stack
|
||||
2. recipes.ginnoir.com
|
||||
3. Import one recipe as test
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] You'd use it — or we drop it
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-021 — Paperless-ngx
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-021"** or **"paperless-ngx"**.
|
||||
|
||||
**In one sentence:** Drop PDFs in a folder → OCR → searchable archive.
|
||||
|
||||
**Why it came up:** ownCloud stores files; this adds document search and tagging.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you scan receipts/documents often enough to matter?
|
||||
2. Or is ownCloud + Obsidian enough?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New docs stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. New stack
|
||||
2. consume folder on ZFS
|
||||
3. Test one PDF OCR
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Search finds text in test doc — or we skip
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-022 — Readarr (ebooks)
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-022"** or **"readarr (ebooks)"**.
|
||||
|
||||
**In one sentence:** Automate ebook downloads like Sonarr does for TV.
|
||||
|
||||
**Why it came up:** You have /storage1/Books in ownCloud; no *arr for books yet.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you acquire ebooks automatically, or buy/download manually?
|
||||
2. Audiobooks too? (Different tool — Audiobookshelf)
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/media/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add readarr + Caddy internal block
|
||||
2. Hook Prowlarr + download client
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Test grab lands in Books — or drop
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-023 — Autobrr
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-023"** or **"autobrr"**.
|
||||
|
||||
**In one sentence:** Monitor private tracker IRC/RSS and auto-push to qBittorrent.
|
||||
|
||||
**Why it came up:** For active private tracker use — overkill if you're mostly public indexers via Prowlarr.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Are you on private trackers that need announce monitoring?
|
||||
2. Or skip — Prowlarr enough?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/media/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add autobrr
|
||||
2. Connect qBit
|
||||
3. One filter if proceeding
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Worth it for your tracker habits — or dropped
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-024 — Syncthing
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-024"** or **"syncthing"**.
|
||||
|
||||
**In one sentence:** Continuous folder sync between devices — ROM saves, dotfiles, etc.
|
||||
|
||||
**Why it came up:** Different from ownCloud's model; good for device-to-device without 'upload to server'.
|
||||
|
||||
### Questions
|
||||
|
||||
1. What folders would you sync? (Be specific — not all of /storage1)
|
||||
2. Conflict risk with RomM/ownCloud on same paths?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New sync stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. New stack
|
||||
2. Pair two devices
|
||||
3. One test folder
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Sync works on chosen folder — or drop
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-025 — Metrics (Beszel/Netdata)
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-025"** or **"metrics (beszel/netdata)"**.
|
||||
|
||||
**In one sentence:** See CPU/RAM/disk graphs — Uptime Kuma only tells you up/down.
|
||||
|
||||
**Why it came up:** Homelab has no metrics dashboard today.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you ever wonder 'why is the server slow' without an answer?
|
||||
2. Beszel (minimal) vs Netdata (detailed) — or skip?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/monitoring/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add chosen tool
|
||||
2. metrics.ginnoir.com internal
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Dashboard shows host + container usage
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-026 — IGIR container
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-026"** or **"igir container"**.
|
||||
|
||||
**In one sentence:** Schedule your existing ROM cleanup scripts instead of SSH ad-hoc.
|
||||
|
||||
**Why it came up:** igir-*.sh scripts exist; romhacks handoff will add more ROMs to maintain.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you run IGIR often enough to automate?
|
||||
2. Or manual is fine?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/roms/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Container wrapping existing scripts
|
||||
2. Weekly dry-run report → ntfy
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Scheduled dry-run runs — or drop
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-027 — Jellyfin instead of Plex
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-027"** or **"jellyfin instead of plex"**.
|
||||
|
||||
**In one sentence:** Self-host streaming in Docker instead of host-native Plex.
|
||||
|
||||
**Why it came up:** Only if you want to move off Plex licensing / centralize in compose.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Happy with Plex today?
|
||||
2. Family/clients okay switching apps?
|
||||
3. Hardware transcoding needed?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
media or new streaming
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Decision
|
||||
2. If yes: jellyfin + GPU passthrough eval
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Stay on Plex or migrate plan documented
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-028 — Home Assistant
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-028"** or **"home assistant"**.
|
||||
|
||||
**In one sentence:** Home automation hub — only makes sense if you have smart devices to control.
|
||||
|
||||
**Why it came up:** Zero home automation in the stack today.
|
||||
|
||||
### Questions
|
||||
|
||||
1. How many smart devices do you have?
|
||||
2. Zigbee/Z-Wave USB stick on valhalla?
|
||||
3. If <5 devices — probably skip.
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New home stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Go/no-go
|
||||
2. If go: HA + USB passthrough
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Go/no-go with reason
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-029 — Firefly III (finance)
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-029"** or **"firefly iii (finance)"**.
|
||||
|
||||
**In one sentence:** Self-hosted budgeting and transaction tracking.
|
||||
|
||||
**Why it came up:** Niche personal finance; sensitive data.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you want to self-host finances, or use a SaaS/bank app?
|
||||
2. If yes — internal-only + Authentik mandatory.
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New finance stack
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. New stack
|
||||
2. internal-only Caddy
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Using it — or dropped
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-030 — AdGuard Home
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-030"** or **"adguard home"**.
|
||||
|
||||
**In one sentence:** Network-wide ad blocking and local DNS for *.ginnoir.com.
|
||||
|
||||
**Why it came up:** Stops LAN hairpin through Cloudflare; blocks ads if router DNS points here.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Does your router already do ad blocking / local DNS?
|
||||
2. Willing to point DHCP DNS at valhalla?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
New network stack or mgmt
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Check router first
|
||||
2. If gap: AdGuard + DNS rewrites
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Router sufficient — or AdGuard deployed
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-031 — Pingvin Share
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-031"** or **"pingvin share"**.
|
||||
|
||||
**In one sentence:** Temporary password-protected file links — lighter than ownCloud shares.
|
||||
|
||||
**Why it came up:** One-off 'here's a file' drops; ownCloud is heavier.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Do you send ad-hoc files to people often?
|
||||
2. Or rare enough to skip?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/share/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Add to share stack
|
||||
2. drop.ginnoir.com or similar
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Test share link — or drop
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,57 @@
|
||||
# TB-032 — Wiki.js
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-032"** or **"wiki.js"**.
|
||||
|
||||
**In one sentence:** Modern wiki — only relevant if TB-009 says 'keep a structured wiki' but not BookStack.
|
||||
|
||||
**Why it came up:** Depends on TB-009 decision; don't start here first.
|
||||
|
||||
### Questions
|
||||
|
||||
1. Did TB-009 land on 'keep wiki'?
|
||||
2. If BookStack retired or Obsidian-only — skip this entirely.
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
stacks/dev/
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Only if TB-009 = Option C
|
||||
2. Import + replace BookStack
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] TB-009 dependency satisfied — or N/A
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,59 @@
|
||||
# TB-033 — Public surface audit
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-033"** or **"public surface audit"**.
|
||||
|
||||
**In one sentence:** A short conversation + checklist: what's reachable from the internet, and should it be?
|
||||
|
||||
**Why it came up:** Some things are public on purpose (Foundry, famapp); some might surprise you (Obsidian).
|
||||
|
||||
### Questions
|
||||
|
||||
1. Want to walk through the list together in chat instead of reading a table?
|
||||
2. Any service you *know* should be public that we'd flag as wrong?
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
Caddyfile review only
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. Walk each public domain in conversation
|
||||
2. Mark: keep public / lock down / drop
|
||||
3. Save decisions here and in TB-005/TB-006 as needed
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] Every public site has your yes/no
|
||||
- [ ] Action items linked to other TBs
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,58 @@
|
||||
# TB-XXX — Title
|
||||
|
||||
**Status:** not started
|
||||
**Your call:** _(unset — talk first)_
|
||||
|
||||
---
|
||||
|
||||
## Talk first
|
||||
|
||||
> **Stop.** Don't read Reference until we've talked.
|
||||
> In chat, say **"let's do TB-XXX"** and we'll go through this together.
|
||||
|
||||
**In one sentence:** _(what this is)_
|
||||
|
||||
**Why it came up:** _(one line)_
|
||||
|
||||
### Questions
|
||||
|
||||
1. _(question)_
|
||||
2. _(question)_
|
||||
3. _(question)_
|
||||
|
||||
### Your options
|
||||
|
||||
| Option | Meaning |
|
||||
|--------|---------|
|
||||
| **Do it** | We're doing this — I'll implement or walk you through it |
|
||||
| **Later** | Keep on the list, not now |
|
||||
| **Drop** | Remove from plan — totally fine |
|
||||
| **Tweak** | Change scope; tell me how |
|
||||
|
||||
### What we decided
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Decision** | |
|
||||
| **Notes** | |
|
||||
| **Date** | |
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Reference (only open if we're doing it)</summary>
|
||||
|
||||
### Stack / files
|
||||
|
||||
_(where changes live)_
|
||||
|
||||
### If we do it — rough steps
|
||||
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
### Done when
|
||||
|
||||
- [ ] ...
|
||||
|
||||
</details>
|
||||
@@ -0,0 +1,39 @@
|
||||
# backup stack
|
||||
|
||||
Kopia → Backblaze B2. Backs up `/config`, **selected** `labdata` paths (see `SCOPE.md`), and nightly SQL dumps.
|
||||
|
||||
## Before first deploy
|
||||
|
||||
1. **Backblaze B2** (recommended offsite — ~$6/TB/mo)
|
||||
- Create a bucket (e.g. `valhalla-homelab-backup`)
|
||||
- Create an **Application Key** scoped to that bucket
|
||||
- Note the S3 endpoint for your region
|
||||
|
||||
2. **Fill `stack.env`**
|
||||
- `KOPIA_REPOSITORY_PASSWORD` — `openssl rand -hex 32`
|
||||
- `B2_KEY_ID`, `B2_APPLICATION_KEY`, `B2_BUCKET`, `B2_ENDPOINT`
|
||||
|
||||
3. **Host paths** (auto-created on first start if missing)
|
||||
- `/config/kopia/{config,cache,logs}`
|
||||
- `/config/backup/dumps`
|
||||
|
||||
4. **Register** new Portainer git stack → `stacks/backup` (one-time)
|
||||
|
||||
5. **Push** → Portainer redeploys within 5 min
|
||||
|
||||
## After deploy
|
||||
|
||||
- UI: `http://valhalla:51515` (or add `backup.ginnoir.com` internal-only in Caddy)
|
||||
- Subscribe phone to ntfy topic `backup` (or path in `NTFY_URL`)
|
||||
- **Restore test** (do once): pick a small dump from `/config/backup/dumps/`, restore to a throwaway container
|
||||
|
||||
## Schedule
|
||||
|
||||
| Time | What |
|
||||
|------|------|
|
||||
| 02:50 | `backup-dump-databases.sh` — SQL dumps |
|
||||
| 03:00 | Kopia snapshots (paths in `snapshot-paths.txt`) |
|
||||
|
||||
## If B2 creds are empty
|
||||
|
||||
Kopia still starts with UI — configure repository manually, then set policies for each path in `snapshot-paths.txt`.
|
||||
@@ -0,0 +1,49 @@
|
||||
# What we back up (and skip)
|
||||
|
||||
**Rule:** everything irreplaceable or slow to rebuild — **not** ROM files, **not** provider cover art.
|
||||
|
||||
## Always
|
||||
|
||||
| Path | Why |
|
||||
|------|-----|
|
||||
| `/config` | App state, DBs, CouchDB vault — **includes RomM MariaDB** (`/config/romm/mariadb`) |
|
||||
| `/config/backup/dumps` | Nightly SQL dumps — **includes `romm-db`** |
|
||||
|
||||
## RomM
|
||||
|
||||
Per [RomM docs](https://docs.romm.app/): **`assets` = saves/states**; **`resources` = fetched covers**.
|
||||
|
||||
| Included | Path | What |
|
||||
|----------|------|------|
|
||||
| Database | `/config/romm/mariadb` + nightly dump | Scans, hashes, matches (weeks of work) |
|
||||
| Saves & states | `labdata/romm/assets` | RomM-managed save files |
|
||||
| Config/cache | `/config/romm/config`, `/config/romm/redis` | Via `/config` snapshot |
|
||||
|
||||
| Skipped | Path | What |
|
||||
|---------|------|------|
|
||||
| Cover art | `labdata/romm/resources` | IGDB/SteamGridDB — minutes to re-fetch |
|
||||
|
||||
## Emulation (`/storage1/Emulation`) — excluded
|
||||
|
||||
Legacy pre-RomM tree (EmuDeck, ES-DE, 13T `roms/`, symlinks). **Not backed up offsite.** RomM is the source of truth for saves via `labdata/romm/assets` + DB.
|
||||
|
||||
## labdata — included
|
||||
|
||||
| Path | Why |
|
||||
|------|-----|
|
||||
| `gitea/repositories`, `gitea/lfs` | Git + LFS |
|
||||
| `famapp/minio`, `resume/minio`, `plane/minio` | User uploads |
|
||||
| `romhacks/metadata`, `romhacks/library` | Catalog work + guides/spreadsheets |
|
||||
| `romm/assets` | Saves (see RomM above) |
|
||||
|
||||
## labdata — skipped
|
||||
|
||||
| Path | Why |
|
||||
|------|-----|
|
||||
| `romm/resources` | Provider cover art |
|
||||
| `registry` | Re-pull images |
|
||||
| `romhacks/incoming` | Transient download queue |
|
||||
|
||||
## Change scope
|
||||
|
||||
Edit `snapshot-paths.txt`, bind mounts in `docker-compose.yml`, and `SCOPE.md`.
|
||||
@@ -0,0 +1,3 @@
|
||||
# min hour day month weekday command
|
||||
50 2 * * * /bin/sh /scripts/backup-dump-databases.sh
|
||||
0 3 * * * /bin/sh /scripts/backup-run-snapshots.sh
|
||||
@@ -0,0 +1,85 @@
|
||||
# backup stack — Kopia snapshots of /config, selected /storage1/labdata paths, and DB dumps.
|
||||
# Scope: stacks/backup/SCOPE.md + snapshot-paths.txt (excludes RomM art, registry, transient dirs).
|
||||
#
|
||||
# Offsite target: Backblaze B2 (S3-compatible). One-time repo setup — see README.md.
|
||||
# Schedule: backup-scheduler dumps databases ~02:50, Kopia snapshots ~03:00 (policy).
|
||||
#
|
||||
# Env convention: stack.env only (no Portainer UI vars). Kopia UI is internal-only
|
||||
# via Caddy after you add backup.ginnoir.com (optional, README).
|
||||
|
||||
services:
|
||||
kopia:
|
||||
container_name: kopia
|
||||
image: kopia/kopia:0.17
|
||||
hostname: kopia
|
||||
restart: unless-stopped
|
||||
networks: [backup, edge]
|
||||
env_file:
|
||||
- stack.env
|
||||
environment:
|
||||
- KOPIA_PASSWORD=${KOPIA_REPOSITORY_PASSWORD}
|
||||
- TZ=${TZ}
|
||||
- B2_BUCKET=${B2_BUCKET}
|
||||
- B2_KEY_ID=${B2_KEY_ID}
|
||||
- B2_APPLICATION_KEY=${B2_APPLICATION_KEY}
|
||||
- B2_ENDPOINT=${B2_ENDPOINT}
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
security_opt:
|
||||
- apparmor:unconfined
|
||||
devices:
|
||||
- /dev/fuse:/dev/fuse
|
||||
volumes:
|
||||
- /config/kopia/config:/app/config
|
||||
- /config/kopia/cache:/app/cache
|
||||
- /config/kopia/logs:/app/logs
|
||||
- /config:/backup/config:ro
|
||||
- /config/backup/dumps:/backup/dumps:ro
|
||||
- /storage1/labdata/gitea/repositories:/backup/labdata/gitea/repositories:ro
|
||||
- /storage1/labdata/gitea/lfs:/backup/labdata/gitea/lfs:ro
|
||||
- /storage1/labdata/famapp/minio:/backup/labdata/famapp/minio:ro
|
||||
- /storage1/labdata/resume/minio:/backup/labdata/resume/minio:ro
|
||||
- /storage1/labdata/plane/minio:/backup/labdata/plane/minio:ro
|
||||
- /storage1/labdata/romhacks/metadata:/backup/labdata/romhacks/metadata:ro
|
||||
- /storage1/labdata/romhacks/library:/backup/labdata/romhacks/library:ro
|
||||
- /storage1/labdata/romm/assets:/backup/labdata/romm/assets:ro
|
||||
- ./init-kopia.sh:/init-kopia.sh:ro
|
||||
- ./snapshot-paths.txt:/snapshot-paths.txt:ro
|
||||
ports:
|
||||
- "51515:51515"
|
||||
entrypoint: ["/bin/sh", "/init-kopia.sh"]
|
||||
labels:
|
||||
- "com.centurylabs.watchtower.enable=false"
|
||||
|
||||
backup-scheduler:
|
||||
container_name: backup_scheduler
|
||||
image: alpine:3.20
|
||||
restart: unless-stopped
|
||||
networks: [backup]
|
||||
env_file:
|
||||
- stack.env
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- /config/backup/dumps:/dumps
|
||||
- ./scripts/backup-dump-databases.sh:/scripts/backup-dump-databases.sh:ro
|
||||
- ./scripts/backup-run-snapshots.sh:/scripts/backup-run-snapshots.sh:ro
|
||||
- ./snapshot-paths.txt:/snapshot-paths.txt:ro
|
||||
- ./crontab:/etc/crontab:ro
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
apk add --no-cache docker-cli curl tzdata >/dev/null
|
||||
printenv | grep -E '^(NTFY_|TZ=)' > /etc/backup.env || true
|
||||
crond -f -l 8 -c /etc
|
||||
depends_on:
|
||||
- kopia
|
||||
|
||||
networks:
|
||||
backup:
|
||||
name: backup
|
||||
driver: bridge
|
||||
edge:
|
||||
name: edge
|
||||
external: true
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
# One-time (or idempotent) Kopia repo + policy setup, then start server UI.
|
||||
set -e
|
||||
|
||||
CONFIG_DIR=/app/config
|
||||
export KOPIA_CONFIG_PATH="${CONFIG_DIR}/repository.config"
|
||||
|
||||
mkdir -p "${CONFIG_DIR}" /app/cache /app/logs
|
||||
|
||||
# Connect or create B2 repository when credentials are present.
|
||||
if [ -n "${B2_KEY_ID}" ] && [ -n "${B2_APPLICATION_KEY}" ] && [ -n "${B2_BUCKET}" ]; then
|
||||
if [ ! -f "${KOPIA_CONFIG_PATH}" ]; then
|
||||
echo "init-kopia: creating S3 repository on bucket ${B2_BUCKET}"
|
||||
kopia repository create s3 \
|
||||
--bucket="${B2_BUCKET}" \
|
||||
--access-key="${B2_KEY_ID}" \
|
||||
--secret-access-key="${B2_APPLICATION_KEY}" \
|
||||
--endpoint="${B2_ENDPOINT}" \
|
||||
--password="${KOPIA_PASSWORD}"
|
||||
else
|
||||
echo "init-kopia: repository config exists, skipping create"
|
||||
fi
|
||||
|
||||
while IFS= read -r path || [ -n "${path}" ]; do
|
||||
case "${path}" in ''|\#*) continue ;; esac
|
||||
kopia policy set "${path}" \
|
||||
--keep-latest 14 \
|
||||
--keep-daily 30 \
|
||||
--keep-weekly 12 \
|
||||
--keep-monthly 24 \
|
||||
--compression=zstd \
|
||||
--snapshot-time=03:00 \
|
||||
--enable-actions=false \
|
||||
2>/dev/null || kopia policy set "${path}" \
|
||||
--keep-latest 14 \
|
||||
--keep-daily 30 \
|
||||
--keep-weekly 12 \
|
||||
--keep-monthly 24 \
|
||||
--compression=zstd \
|
||||
--snapshot-time=03:00
|
||||
done < /snapshot-paths.txt
|
||||
else
|
||||
echo "init-kopia: B2 credentials empty — start UI only; configure repo manually (see README)"
|
||||
fi
|
||||
|
||||
exec kopia server start --ui --insecure --address=0.0.0.0:51515
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
# Dump all homelab databases to /config/backup/dumps (gzip SQL).
|
||||
# Runs inside backup_scheduler; needs docker.sock.
|
||||
set -eu
|
||||
|
||||
DUMP_ROOT=/dumps
|
||||
STAMP=$(date +%Y%m%d-%H%M%S)
|
||||
DIR="${DUMP_ROOT}/${STAMP}"
|
||||
mkdir -p "${DIR}"
|
||||
|
||||
log() { echo "[backup-dump ${STAMP}] $*"; }
|
||||
|
||||
dump_postgres() {
|
||||
name="$1"
|
||||
user="$2"
|
||||
db="$3"
|
||||
if ! docker ps --format '{{.Names}}' | grep -qx "${name}"; then
|
||||
log "skip ${name} (not running)"
|
||||
return 0
|
||||
fi
|
||||
log "dump ${name} → ${db}"
|
||||
docker exec "${name}" pg_dump -U "${user}" "${db}" | gzip -c > "${DIR}/${name}.sql.gz"
|
||||
}
|
||||
|
||||
dump_mariadb() {
|
||||
name="$1"
|
||||
db="$2"
|
||||
if ! docker ps --format '{{.Names}}' | grep -qx "${name}"; then
|
||||
log "skip ${name} (not running)"
|
||||
return 0
|
||||
fi
|
||||
log "dump ${name} → ${db}"
|
||||
docker exec "${name}" sh -c "mariadb-dump -u root -p\"\$MYSQL_ROOT_PASSWORD\" \"${db}\"" \
|
||||
| gzip -c > "${DIR}/${name}.sql.gz"
|
||||
}
|
||||
|
||||
log "starting database dumps → ${DIR}"
|
||||
|
||||
dump_postgres postgres_gitea gitea gitea
|
||||
dump_postgres postgres_plane plane plane
|
||||
dump_postgres postgres_authentik authentik authentik
|
||||
dump_postgres postgres_famapp famapp famapp
|
||||
dump_postgres postgres_resume postgres postgres
|
||||
|
||||
dump_mariadb mariadb_bookstack bookstack
|
||||
dump_mariadb owncloud_mariadb owncloud
|
||||
dump_mariadb romm-db romm
|
||||
|
||||
find "${DUMP_ROOT}" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
log "done"
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
[ -f /etc/backup.env ] && . /etc/backup.env
|
||||
|
||||
NTFY_URL="${NTFY_URL:-https://ntfy.ginnoir.com/backup}"
|
||||
NTFY_TITLE="${NTFY_TITLE:-ValhallaBackup}"
|
||||
|
||||
notify() {
|
||||
priority="$1"
|
||||
message="$2"
|
||||
curl -sf -H "Title: ${NTFY_TITLE}" -H "Priority: ${priority}" -d "${message}" "${NTFY_URL}" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
if ! docker ps --format '{{.Names}}' | grep -qx kopia; then
|
||||
notify high "Kopia container not running — snapshots skipped"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FAILED=0
|
||||
while IFS= read -r path || [ -n "${path}" ]; do
|
||||
case "${path}" in ''|\#*) continue ;; esac
|
||||
if ! docker exec kopia kopia snapshot create "${path}"; then
|
||||
FAILED=1
|
||||
fi
|
||||
done < /snapshot-paths.txt
|
||||
|
||||
if [ "${FAILED}" -eq 0 ]; then
|
||||
notify default "Snapshots OK ($(date +%Y-%m-%d\ %H:%M))"
|
||||
else
|
||||
notify high "One or more snapshots FAILED — check docker logs kopia"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
# Kopia snapshot roots (one path per line). Edit when scope changes.
|
||||
/backup/config
|
||||
/backup/dumps
|
||||
/backup/labdata/gitea/repositories
|
||||
/backup/labdata/gitea/lfs
|
||||
/backup/labdata/famapp/minio
|
||||
/backup/labdata/resume/minio
|
||||
/backup/labdata/plane/minio
|
||||
/backup/labdata/romhacks/metadata
|
||||
/backup/labdata/romhacks/library
|
||||
/backup/labdata/romm/assets
|
||||
@@ -0,0 +1,15 @@
|
||||
# backup stack — Kopia repository + Backblaze B2 offsite.
|
||||
TZ=America/Chicago
|
||||
|
||||
# Encrypts the Kopia repository (NOT your B2 login).
|
||||
KOPIA_REPOSITORY_PASSWORD=8f4c2a9e1b7d6035f8e2c4a6910d3b7e5f9a8c2d6b4e1f0a3c7d9e2b5f8a1c4
|
||||
|
||||
# Backblaze B2 (S3-compatible API)
|
||||
B2_BUCKET=ginnoir-backup
|
||||
B2_KEY_ID=00452ab80ae44e10000000001
|
||||
B2_APPLICATION_KEY=K004P2lGTtRiCe430O5dVZJiTn7Kwr8
|
||||
B2_ENDPOINT=https://s3.us-west-004.backblazeb2.com
|
||||
|
||||
# ntfy alerts (same pattern as watchtower)
|
||||
NTFY_URL=https://ntfy.ginnoir.com/backup
|
||||
NTFY_TITLE=ValhallaBackup
|
||||
@@ -11,7 +11,7 @@
|
||||
# UI env vars for ${VAR} substitution. See memory portainer-env-interpolation.
|
||||
#
|
||||
# Tiered binds: DB + RomM config + bundled-redis -> /config/romm (SSD);
|
||||
# RomM blobs (downloaded art + user saves/states) -> /storage1/labdata/romm (ZFS).
|
||||
# RomM labdata (ZFS): assets = saves/states; resources = fetched cover art (IGDB etc.).
|
||||
# The labdata/romm/* targets are auto-created by Docker as root on first start.
|
||||
#
|
||||
# Only romm joins `edge` (Caddy proxies romm:8080); romm-db stays on `roms` only.
|
||||
|
||||
Reference in New Issue
Block a user