diff --git a/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/MEMORY.md b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/MEMORY.md new file mode 100644 index 0000000..f3c946f --- /dev/null +++ b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/MEMORY.md @@ -0,0 +1,3 @@ +- [SSH deployment approach](feedback_ssh_scripts.md) — stage on pve, distribute from there, don't nest SSH +- [Newsletter stack status](project_newsletter_stack.md) — Ghost+Listmonk+n8n+Postmark partially wired, Substack under consideration +- [Don't confirm memory edits](feedback_no_confirm_memory.md) — just edit memory files directly, no prompting diff --git a/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_no_confirm_memory.md b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_no_confirm_memory.md new file mode 100644 index 0000000..a46ed24 --- /dev/null +++ b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_no_confirm_memory.md @@ -0,0 +1,11 @@ +--- +name: Don't ask permission to edit memory files +description: Just edit memory files directly — they're under Claude's control, no need to confirm with user +type: feedback +--- + +Never prompt the user about memory file changes — no confirmation questions, no tool approval popups, nothing in the user's face. If a tool permission prompt would fire, that counts as asking. + +**Why:** Memory files are Claude's workspace. Any interruption about them — whether a question or a tool approval dialog — is unwanted friction. + +**How to apply:** Edit memory files silently. Don't deflect blame onto tool permissions — if it's in the user's face, it's Claude's fault. diff --git a/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_ssh_scripts.md b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_ssh_scripts.md new file mode 100644 index 0000000..c22bd26 --- /dev/null +++ b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/feedback_ssh_scripts.md @@ -0,0 +1,14 @@ +--- +name: Use scripts on jump host instead of long SSH chains +description: Download binaries once and distribute, write scripts to pve and run from there instead of nested SSH commands +type: feedback +--- + +Don't run long nested SSH commands or download the same binary on every node separately. Instead: +- Download binaries once (on pve or locally) and scp to target nodes +- Write deployment scripts to pve and run them from there +- Avoid the weird background task temp paths — keep things simple and visible + +**Why:** Long nested SSH chains are fragile (env vars get lost, quoting breaks), slow, and hard to debug. Downloading the same 60MB binary 7 times is wasteful when you can download once and distribute. + +**How to apply:** When deploying to multiple nodes, stage files and scripts on the jump host (pve), then distribute from there. Prefer simple, visible approaches over clever one-liners. diff --git a/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/project_newsletter_stack.md b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/project_newsletter_stack.md new file mode 100644 index 0000000..9e4aa5a --- /dev/null +++ b/.claude/projects/-home-samantha-private-Knowledge-repos-homelab/memory/project_newsletter_stack.md @@ -0,0 +1,17 @@ +--- +name: Newsletter stack status and frustrations +description: Ghost+Listmonk+n8n+Postmark newsletter pipeline — partially wired, user considering Substack as alternative +type: project +--- + +Ghost CMS hard-codes Mailgun for newsletter sending — `bulk_email__provider: smtp` only handles transactional one-off emails (password resets, signup confirmations), NOT newsletters. Mailgun and SendGrid both rejected signup. Postmark works but account is in sandbox (under review, can only send to verified addresses). + +Current stack: Ghost (blog) → n8n (webhook automation) → Listmonk (newsletter sending) → Postmark (SMTP). Ghost fires webhooks on member.added and post.published to n8n. n8n workflow for member.added is partially built — webhook trigger works, HTTP Request node to Listmonk API not yet configured. + +Listmonk API user creation is confusing — no password field shown for API-type users. Admin credentials work for API access. + +User is frustrated with the complexity and seriously considering Substack for newsletters. The self-hosted stack requires chaining 4 services to do what Substack does natively. + +**Why:** Ghost's Mailgun lock-in is a design flaw that forces this complexity. User wants to own the stack but the overhead is high. + +**How to apply:** Don't push self-hosted over Substack — respect the tradeoff. If user continues with self-hosted, minimize friction. The n8n→Listmonk integration needs finishing: HTTP Request node with Basic Auth to listmonk:9000/api/subscribers. diff --git a/.gitignore b/.gitignore index 6686406..7a880c7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # IDE .idea/ .vscode/ +.remember/ *.swp *.swo *~ @@ -15,5 +16,4 @@ .DS_Store Thumbs.db -# Claude Code -.claude/ + diff --git a/K3s-SESSION-STATE.md b/K3s-SESSION-STATE.md index 8450a79..22fe0d2 100644 --- a/K3s-SESSION-STATE.md +++ b/K3s-SESSION-STATE.md @@ -1,11 +1,11 @@ # K3s Session State -# Saved: 2026-04-06 (end of session 3) +# Saved: 2026-04-14 ## Current State -New Proxmox-based K3s cluster in progress. VirtualBox cluster retired. -All 7 Proxmox VMs created and on WireGuard mesh. K3s not yet installed. -Old VirtualBox services (ghost, forgejo, postgres, mariadb) still running on old cluster until migration complete. +K3s v1.34.6 cluster fully operational on Proxmox VMs + KVM worker over WireGuard mesh. +fat_mama migrated from VirtualBox to KVM/libvirt on workstation 2026-04-14. +All Proxmox K3s VMs have onboot: 1 set (fixed 2026-04-12). ## Proxmox VMs @@ -18,6 +18,7 @@ Old VirtualBox services (ghost, forgejo, postgres, mariadb) still running on old | game-control | 10.10.10.158 | 10.0.0.10 | game | k3s control plane | | game-worker-hdd | 10.10.10.186 | 10.0.0.11 | game | k3s worker (local-lvm/HDD) | | game-worker-ssd | 10.10.10.153 | 10.0.0.12 | game | k3s worker (game-ssd/NVMe) | +| fat_mama | 192.168.40.220 | 10.0.0.13 | workstation (KVM/libvirt, macvtap enp4s0) | k3s worker | WG IPs 10.0.0.2–10.0.0.5 reserved (old VirtualBox nodes, do not reuse). Hub: DO droplet at 138.197.87.251:51820, WG IP 10.0.0.1 @@ -33,37 +34,40 @@ Hub: DO droplet at 138.197.87.251:51820, WG IP 10.0.0.1 | game-control | 2 | 2GB | 20G | local-lvm | | game-worker-hdd | 6 | 8GB | 200G | local-lvm (HDD) | | game-worker-ssd | 10 | 8GB | 200G | game-ssd (NVMe) | +| fat_mama | 12 | 20GB | 200G | /var/lib/libvirt/images (qcow2) | ## Network Architecture -- All VMs on vmbr1 (10.10.10.0/24), DHCP +- Proxmox VMs on vmbr1 (10.10.10.0/24), DHCP +- fat_mama on LAN (192.168.40.0/24) via macvtap on enp4s0 — workstation host cannot directly ping/SSH to it; reachable from rest of LAN and via WireGuard at 10.0.0.13 - WireGuard mesh via DO hub — all nodes have static WG IPs (10.0.0.0/24) - Full mesh: all nodes have each other as explicit WireGuard peers (not just hub-and-spoke) -- K3s will use --flannel-iface=wg0 so all cluster traffic runs over WireGuard +- K3s uses --flannel-iface=wg0 so all cluster traffic runs over WireGuard - Caddy at DO hub proxies external traffic to any node's WG IP + NodePort - Tailscale/Headscale abandoned — too unreliable for cluster networking ## Proxmox Host Specs -- pve: workstation i9-13900KF, 96GB RAM -- adder: Proxmox node with RTX 2070, 4TB NVMe available -- game: Proxmox node with RTX 2070, 16GB RAM, 256GB NVMe (game-ssd) + 2TB HDD (local-lvm) +- pve: Meerkat NUC, 64GB RAM, 4TB NVMe +- adder: Adder WS laptop, 32GB RAM, 2TB NVMe, RTX 2070 +- game: old gaming PC, 16GB RAM, 256GB NVMe (game-ssd) + 2TB HDD (local-lvm) +- workstation: i9-13900KF, 96GB RAM, RTX 4090, Fedora (runs fat_mama via KVM/libvirt) ## VM Provisioning ### Template & Clone Scripts Scripts at `~/private/Knowledge/repos/homelab/proxmox/scripts/`: -- `create-debian-template.sh [STORAGE] [BRIDGE]` +- `create-debian-template.sh [STORAGE] [BRIDGE]` - Defaults: STORAGE=local-lvm, BRIDGE=vmbr1 - Bakes in: qemu-guest-agent, curl, wget, nano, rsync, htop, tmux, emacs-nox, nfs-common, tailscale - Zeroes /etc/machine-id, removes /etc/ssh/ssh_host_* (Cloud-Init regenerates on first boot) - Does NOT create .ssh or set keys — done post-boot via qm set -- `clone-vm.sh [CORES] [MEMORY_MB] [DISK_SIZE] [STORAGE]` +- `clone-vm.sh [CORES] [MEMORY_MB] [DISK_SIZE] [STORAGE]` - Defaults: 2 cores, 2048MB RAM, 20G disk, local-lvm storage - Full clone, auto-starts the VM ### Post-Clone Formula (confirmed working) -1. Clone: `./clone-vm.sh