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:
ginnoir
2026-06-10 21:21:34 -05:00
parent 225078afbe
commit 7c7f6dfebc
47 changed files with 3105 additions and 1 deletions
@@ -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>