docs: P1.1 dynamic self-IP deny (DDNS/trace discovery, keep previous, atomic swap)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 22:06:20 -07:00
co-authored by Claude Opus 5.5
parent 8e3a6cd339
commit d404657820
+28
View File
@@ -193,3 +193,31 @@ sift serve … (the service)
4. Install the skill: `skill/SKILL.md` → `~/.hermes/skills/web/sift/SKILL.md`; add the
known-limitation note (§2) to it.
5. Kyle restarts talos's gateway. Then the exit-criteria evidence (§1).
## 9. P1.1 — dynamic self-IP deny (Kyle, 2026-09-22)
The home WAN address is residential DSL and changes. A static `/32` in `denyPrefixes` goes
stale silently (and the old address gets reassigned to a stranger). Add **self-address
discovery** to sift itself so the hairpin-NAT block follows the real address:
- Config `self_ip` block:
`sources = ["ddns:<hostname>", "trace:https://1.1.1.1/cdn-cgi/trace"]`,
`interval = "10m"`, `keep_previous = "24h"`.
- `ddns:<host>` — resolve the router's DDNS name (preferred if one exists, e.g. MikroTik
IP Cloud `…sn.mynetname.net`); no outbound HTTP needed.
- `trace:<url>` — GET through the **guarded** fetcher, parse the `ip=` line (Cloudflare
trace format). A result that is not a global unicast address is ignored.
- Discovered addresses (v4 /32, v6 /64) are added to the dial-time deny set alongside
`denyPrefixes`; previous addresses are kept for `keep_previous` so a change mid-request
can't open a window.
- Discovery runs **before** the listener opens (bounded wait, e.g. 10 s); on failure sift
starts with the static `denyPrefixes` plus the last persisted value
(`/var/lib/sift/self_ip.json`) and logs WARN; it never serves with an *empty* self set if a
previous value exists.
- Swap the deny set atomically (`atomic.Pointer`); `/healthz` reports `self_ip` age and
source; `/metrics` counts discovery failures.
- Tests: injected resolver/trace returning A then B → both denied until B + keep_previous;
non-global answer ignored; discovery failure keeps the persisted value.
Until P1.1 lands, deploy with the current address as a static entry
(`denyPrefixes = [ "99.73.161.224/32" ]` in `hw/orion`).