feat(claude-workstation): off-site headless Claude Code host on nas-2 #247

Merged
k.renner merged 3 commits from feat/claude-workstation-nas2 into master 2026-07-17 11:00:29 +00:00
Member

Stands up an always-on off-site Debian VM (Synology VMM on nas-2) running Claude Code, per the agreed split-scope design (Fable-reviewed).

Scope

  • On the VM: reattachable interactive tmux sessions + an unattended, hardened, bounded notes-distill timer.
  • Not on the VM: scheduled repo-automation + cluster-reports stay in-cluster (Woodpecker cron) — the VM holds no cluster-write creds.

What's in this PR (reviewable, no live impact)

  • ansible/roles/claude-workstation — two credential-isolated users (interactive vs unattended), Node 22 + Claude Code, per-user claude-bot creds + OAuth token (never --bare), Syncthing notes sync, daily distill timer (systemd hardening + wall-clock kill switch), staged deny-default nftables egress allowlist that ships DISABLED with a DNS-refreshed allow set.
  • ansible/plays/install-claude-workstation.yml + .woodpecker/deploy-claude-workstation.yml + inventory group, mirroring the off-site netbird-proxy pattern.
  • kubernetes/apps/claude-workstation/ — read-only view ServiceAccount + token. No talosconfig, no SOPS age key (Fable #1); view excludes Secrets.
  • RUNBOOK.md — manual steps: VM creation (cloud-init), networking (mesh + scoped site-to-site backup), SA kubeconfig assembly, vault keys, egress cutover, rotation, cattle DR.

Deliberately deferred (documented, not in this PR)

  • Netbird-ingress CNP egress edit + dashboard route for the redundant mesh path to the k8s API (edits a security-critical CNP; can't validate offline).
  • Network-bound at-rest encryption (clevis/Tang) — flagged as a gap.

Validation

ansible-playbook --syntax-check clean; kubectl kustomize kubernetes/apps builds. Not applied anywhere — VM doesn't exist yet.

🤖 Generated with Claude Code

Stands up an always-on **off-site Debian VM (Synology VMM on nas-2)** running Claude Code, per the agreed split-scope design (Fable-reviewed). ## Scope - **On the VM:** reattachable interactive tmux sessions + an unattended, hardened, bounded notes-distill timer. - **Not on the VM:** scheduled repo-automation + cluster-reports stay in-cluster (Woodpecker cron) — the VM holds no cluster-write creds. ## What's in this PR (reviewable, no live impact) - ansible/roles/claude-workstation — two credential-isolated users (interactive vs unattended), Node 22 + Claude Code, per-user claude-bot creds + OAuth token (never --bare), Syncthing notes sync, daily distill timer (systemd hardening + wall-clock kill switch), staged deny-default nftables egress allowlist that ships DISABLED with a DNS-refreshed allow set. - ansible/plays/install-claude-workstation.yml + .woodpecker/deploy-claude-workstation.yml + inventory group, mirroring the off-site netbird-proxy pattern. - kubernetes/apps/claude-workstation/ — read-only view ServiceAccount + token. No talosconfig, no SOPS age key (Fable #1); view excludes Secrets. - RUNBOOK.md — manual steps: VM creation (cloud-init), networking (mesh + scoped site-to-site backup), SA kubeconfig assembly, vault keys, egress cutover, rotation, cattle DR. ## Deliberately deferred (documented, not in this PR) - Netbird-ingress CNP egress edit + dashboard route for the redundant mesh path to the k8s API (edits a security-critical CNP; can't validate offline). - Network-bound at-rest encryption (clevis/Tang) — flagged as a gap. ## Validation ansible-playbook --syntax-check clean; kubectl kustomize kubernetes/apps builds. Not applied anywhere — VM doesn't exist yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-07-15 17:08:27 +00:00
Always-on Debian VM (Synology VMM, off-site nas-2) running Claude Code for
reattachable tmux sessions plus an unattended, hardened, bounded notes-distill
timer. Scheduled repo/report jobs stay in-cluster (Woodpecker cron); this host
holds no cluster-write creds.

- ansible/roles/claude-workstation: two credential-isolated Unix users
  (interactive vs unattended), Node 22 + Claude Code, per-user claude-bot creds
  and OAuth token (never --bare), Syncthing notes sync, daily distill timer with
  systemd hardening + wall-clock kill, staged deny-default nftables egress
  allowlist (ships DISABLED) with a DNS-refreshed allow set.
- ansible/plays/install-claude-workstation.yml + .woodpecker pipeline +
  inventory group, mirroring the netbird-proxy off-site host pattern.
- kubernetes/apps/claude-workstation: read-only `view` ServiceAccount + token
  (no talosconfig, no age key; view excludes Secrets).
- RUNBOOK.md: manual VM creation (cloud-init), networking (mesh + scoped
  site-to-site backup), SA kubeconfig assembly, vault keys, egress cutover,
  rotation, and cattle DR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot added 1 commit 2026-07-16 11:43:05 +00:00
feat(claude-workstation): Netbird enrolment via CI
ci/woodpecker/manual/deploy-netbird-client Pipeline was successful
48bc888d94
- inventory: claude-ws at its current DHCP lease (10.3.40.78). Switch to the
  stable Netbird mesh IP post-enrolment; the obg.renner.uno FQDN is
  split-horizon and resolves to the public proxy from home, so it must not be
  used as ansible_host.
- group_vars/claude_workstation/vault.yml: vault'd netbird_setup_key, scoped to
  the group so install-netbird-client.yml only enrols this host.
- .woodpecker/deploy-netbird-client.yml: pipeline running
  install-netbird-client.yml --limit claude_workstation (manual + master-path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot added 1 commit 2026-07-16 12:00:54 +00:00
VMM has no cloud-init and only makes blank disks, so replace the cloud-init VM
step with the real method: remastered netinst ISO auto-booting a preseed that
powers off at end (breaks the guest-reboot->CD reinstall loop). Debian 13, DHCP,
Default VM Network. Document the Netbird mesh-reachability caveat (CI/home not
peers) and the obg.renner.uno split-horizon DNS trap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 808e832ad3 into master 2026-07-17 11:00:28 +00:00
Sign in to join this conversation.