--- name: porkbun-dns-ops description: "Runbook for mutating Porkbun DNS records for franksdank.cloud via API from the target host, covering the relative-name edit quirk, rate-limit failure shape, and verify steps." --- # Porkbun DNS ops (franksdank.cloud) Operate the public DNS zone for `franksdank.cloud` via the Porkbun API v3, run **on Dankclaw** so the API key/secret sourced from `/home/dank/serverAppConfigs/.env` (`PORKBUN_API_KEY`, `PORKBUN_API_SECRET_KEY`) never leaves the host. Pipe scripts to the target with `ssh -o BatchMode=yes dankclaw 'bash -s' < /tmp/script.sh`. ## Endpoints (all POST, JSON body) - List: `https://api.porkbun.com/api/json/v3/dns/retrieve/franksdank.cloud` — auth via headers `X-API-Key` + `X-Secret-API-Key` (Bearer is wrong here). Response shape is **top-level `.records[]`**, NOT `.content.records[]` as some docs claim — parse with `jq -r '.records[] | [.id,.name,.type,.content,(.ttl|tostring)] | @tsv'`. - Delete: `/dns/delete/{domain}/{id}` (body carries apikey+secretapikey). - Edit: `/dns/edit/{domain}/{id}` — pass the full record (`id`, `name`, `type`, `content`, `ttl`, `prio`). ## Quirks that bite (verified live 2026-08-31) 1. **`name` is a RELATIVE label on edit.** Passing `ai.franksdank.cloud` yields the record `ai.franksdank.cloud.franksdank.cloud`. Pass the bare label (`ai`) and the API appends the zone itself. Verify after every rename with a retrieve before declaring done. 2. **Rate limit answers as an HTML 404-style page, not JSON.** A batch of rapid mutations makes some calls return `...` where jq then errors (`Cannot iterate over null` / `Invalid numeric literal`). Do not assume the call succeeded — sleep ~8 s and retry the failed ids; verify by re-listing the zone at the end. 3. Deletes/renames are per-record-id: always retrieve first, map name→id, then mutate by id. ## Rename procedure (vhost rename = DNS + Caddy together) 1. `retrieve` → collect ids for the renamed names. 2. `edit` each id with the new **relative** label, preserving type/content/ttl. 3. Re-retrieve and confirm `.franksdank.cloud A `. 4. In-repo lockstep before deploy: rename the Caddyfile vhost block AND the Pi-hole `dankclaw/pihole/addn-hosts` line in the same commit — test 178 (`tests/pihole.bats`) fails the suite if any Caddyfile vhost lacks an addn-hosts A record on 192.168.0.143. 5. Deploy via `scripts/deploy.sh dankclaw` (plans pihole+caddy from changed paths). Capture the suite's true exit with `out=$(./tests/run.sh 2>&1); rc=$?` — piping to `tail` masks the exit code behind `tail`'s own status and has shipped untested commits here. 6. Verify live: `dig +short @192.168.0.143` then `curl -o /dev/null -w %{http_code} https://` (expect 200/302; first hit triggers dns-01 issuance, which works without inbound port 80).