Files
claude-projects/claude-config/config/prompts/wireguard/W2_build_prep.md
T
Backtalk6858 d9419109a5 docs(claude-config): 2026-09-27 preflight prompts, owner decisions, redactions
Saved prompts: W2, OLLAMA-1, BOOT-1, S1, AS0, AS1, JH-1, V0, V1, VS-1.
DECISIONS.md 2026-09-27 entry (sudo-bridge retired, Jenkins deploys via
agent-sudo deploy_service, Chatterbox-Turbo, vault-sandbox auto-unseal).
Voice A1/A2 superseded. Redacted two plaintext secrets in agent-builder
context (still in history; rotation tracked under #192).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:46:31 -05:00

6.6 KiB

W2 BUILD-PREP — cloudflare-ddns + wg-easy IPv4-only fix (#274) — write files, deploy NOTHING

Tracks: personal_projects #274. Design: /opt/appdata/docker/research/diy_wireguard_design.md (LOCKED 2026-09-25). Written 2026-09-27 (infrastructure general questions conversation) for the 2026-09-28 parallel-agent day. Safe to run in parallel with S1, AS0, JH-1, BOOT-1, OLLAMA-1 (no shared files).


You are a bounded background BUILD-PREP agent for personal_projects #274 (DIY WireGuard home-access tunnel), PHASE W2-PREP. Budget: --max-turns 30. If you issue the same tool call twice with identical arguments, STOP and output the wrap-up block with status=partially_succeeded.

HARD RULES

  • WRITE FILES ONLY. Do NOT start, create, pull or restart any container; do NOT run docker compose up; do NOT install, enable or start any systemd unit; no sudo; no router/DNS/Cloudflare API writes; no git add/commit/push; no Postgres writes; no ntfy sends. The owner deploys after review.
  • Read-only on the host otherwise (docker ps/inspect image/ports/networks/labels only — NEVER print .Config.Env; never print a secret value). NO Vault writes this phase — both Cloudflare tokens are already stored.
  • If a security hook or permission blocks something, stop with a partial wrap-up (do not work around it). Issue each file write as its own call.

CURRENT STATE (verified 2026-09-27 14:00 by the main session)

  • wg-easy ghcr.io/wg-easy/wg-easy:15.4.0 (digest-pinned in the compose) is Up (healthy) on primary 192.168.1.88, project dir /opt/appdata/docker/docker-compose/wireguard/, published 0.0.0.0:45791->45791/udp AND [::]:45791->45791/udp, UI 127.0.0.1:51821. Laptop client works on LAN. Router forwards UDP 45791 → 192.168.1.88 (done W0).
  • Secrets pattern already in use for wireguard: secretspec.toml + deploy/resolver.sh + wireguard-secretspec-resolver.{service,timer} (installed + enabled). Read all of them first — W2 extends this, it does not invent a new pattern.
  • Vault (already stored, verified): secret/cloudflare/ddns field token (Zone:DNS:Edit on reverseproxyserver.net). secret/cloudflare/traefik-dns01 is for W3 — do NOT wire it now.
  • Public IP is dynamic (66.164.12.179 on 09-25, 66.164.11.87 on 09-17). Not CGNAT.

LOCKED DECISIONS — do not revisit

  1. DDNS client = favonia/cloudflare-ddns, pinned to an exact current release tag + digest (look it up on Docker Hub / its GitHub releases; record both). Run it as a second service in the SAME wireguard compose project (it is part of the tunnel), on the wg bridge network (NOT host mode — IPv4 only, so bridge is fine).
  2. Record = fuppzd3f9v.reverseproxyserver.net, DNS-only (PROXIED=false), IPv4 only (IP6_PROVIDER=none), TTL 60, update every 2 minutes (favonia's UPDATE_CRON=@every 2m or whatever its README documents). The DDNS client must manage ONLY that one record (never the zone apex or other hosts; if favonia offers a "delete on stop" option, set it OFF so a stopped container never deletes the record).
  3. Token via SecretSpec: add CLOUDFLARE_API_TOKEN (or the _FILE variant if favonia recommends it and the resolver pattern supports it — prefer whatever keeps the value out of docker inspect; explain your choice) sourced from Vault secret/cloudflare/ddns field token, exactly the way INIT_PASSWORD is mapped today.
  4. Hardening per favonia's README: read_only: true, cap_drop: [ALL], security_opt: [no-new-privileges:true], non-root user: as documented, restart policy per template, healthcheck only if the image supports one (do not invent one that can't run in the image — check the image for a shell first via docker manifest inspect/docs; if none, omit and say so).
  5. wg-easy IPv4-only publish fix: change the port mapping to "0.0.0.0:45791:45791/udp" so Docker stops publishing on [::]. Edit only that line (+ a one-line comment). This takes effect at the owner's next recreate.
  6. Compose MUST keep following the template /opt/appdata/docker/non-docker-python-scripts/Docker Template/docker-compose.yml (read it first). Remove the W2 placeholder comment the W1 agent left for ddns (keep the W3 dnsmasq placeholder).

VERIFY FAVONIA FACTS FROM THE SOURCE — WebFetch https://github.com/favonia/cloudflare-ddns (README) once and use its exact env var names (e.g. CLOUDFLARE_API_TOKEN / _FILE, DOMAINS, PROXIED, IP6_PROVIDER, TTL, UPDATE_CRON, DELETE_ON_STOP). Do not guess names from memory.

DELIVERABLES

  1. Edited /opt/appdata/docker/docker-compose/wireguard/docker-compose.yml (ddns service + IPv4-only fix).
  2. Edited secretspec.toml (+ deploy/resolver.sh only if the new var needs a resolver change — keep it minimal).
  3. README.md — add a "W2 — owner steps" section: (a) how to apply: run the resolver service once (sudo systemctl start wireguard-secretspec-resolver.service), confirm both containers healthy/running, docker logs of the ddns container shows the record set to the current public IP (compare curl -4 -s https://ifconfig.me), dig +short fuppzd3f9v.reverseproxyserver.net @1.1.1.1; ss -lnup | grep 45791 shows IPv4 only; (b) phone (Android 9) + tablet: create clients in the wg-easy UI, scan QR, AllowedIPs split-tunnel per design; (c) the W2 tests from design §5: off-LAN handshake over cellular, the silence test (from off-LAN, nmap -sU -p 45791 <public ip> shows open|filtered and wg-easy logs nothing for a non-peer), and the IP-change drill (how to force a DDNS re-check); (d) rollback (remove the ddns service, docker compose up -d re-applies; the DNS record stays pointing at the last IP — harmless).
  4. Validate without deploying: docker compose -f <file> config -q (unset secretspec vars warnings expected — note them), bash -n deploy/resolver.sh if touched.

SCOPE ALLOWLIST: /opt/appdata/docker/docker-compose/wireguard/ (docker-compose.yml, secretspec.toml, deploy/resolver.sh, README.md, .claude/context.md). Nothing else.

PERSIST BEFORE YOU FINISH

  • Append (one cat >> heredoc; append only) "## #274 W2-prep — 2026-09-28 (background agent)" to /opt/appdata/docker/docker-compose/wireguard/.claude/context.md: What was done / Current state / Owner steps (short) / Next step (W3 dnsmasq + Traefik DNS-01 prep).
  • Run: python3 /opt/appdata/docker/.claude/scripts/embed_memory_dir.py --only-recent 5
  • FINAL message = wrap-up JSON only, always: {"status":"succeeded|partially_succeeded|failed","project":"diy-wireguard #274","phase":"W2-prep","actions_taken":[],"actions_failed":[],"files_touched":[],"containers_restarted":[],"unverified_claims":[],"ddns_image_tag":"","ddns_image_digest":"","token_mapping":"env|file — why","validation":{"compose_config":"","resolver_syntax":""},"owner_steps":[],"next_step":"","notes":""}