Add opt-in, driver-based WAN failover/CGNAT guard#2
Open
deviationist wants to merge 2 commits into
Open
Conversation
When a connection fails over to a CGNAT'd path (e.g. a 4G USB modem as WAN backup), the public IP seen from the internet becomes a shared carrier address that can't route back home. Publishing it would point the domain at a dead address. This adds an optional guard that asks the router whether it's on such a path and, if so, skips the update and leaves records on the last good IP. - src/routers/: driver-based abstraction (registry + factory). Core stays agnostic; all router specifics live in a driver. No `router` config block -> feature is fully inert (opt-in). - AsuswrtMerlin driver: authenticates to the router web UI and reads nvram via appGet.cgi (no extra deps; built on Node http/https, handles self-signed + cookies). Pauses updates on secondary-WAN failover, private/CGNAT WAN IP (incl. 100.64.0.0/10), or WAN-IP vs router realip-probe mismatch. Fails open if the router can't be reached, so a transient glitch never freezes DDNS. - GuardState: de-dupes notifications so pause/resume emails fire only on a state transition, not every cron run. - Password is supplied via an env var named in config (passwordEnv); never stored in config.json. - Docs: README section + AGENTS.md; IPv6/AAAA noted as a TODO. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Logger: create the log directory if missing and swallow write failures (one-line stderr warning) instead of throwing. A missing log dir was crash-looping the cron job before it reached any update. - index.js: guard the IP-change notification behind !dryRun so a dry-run no longer sends real emails; logs "Would send" instead. - Rename the --dryRun flag to --dry-run in the docs (yargs camel-cases it, so both forms work). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an opt-in, driver-based guard that refuses to publish the detected public IP while the connection is on a WAN failover path / behind CGNAT (e.g. a 4G USB modem used as WAN backup). In that state the public IP is a shared carrier address that can't route back home — publishing it would point the domain at a dead address. The guard skips the update and leaves records on the last known-good IP instead.
Design
src/routers/— registry + factory. Norouterblock in config → feature is fully inert (opt-in).AsuswrtMerlindriver — authenticates to the router web UI and reads nvram viaappGet.cgi(no new deps; built on Nodehttp/https, handles self-signed certs + cookies). Pauses on any of: secondary-WAN failover (wan1_primary), private/CGNAT WAN IP (incl.100.64.0.0/10), or WAN IP ≠ the router's ownrealipexternal-IP probe. Fails open if the router is unreachable, so a transient glitch never freezes DDNS.GuardState— de-dupes notifications so pause/resume emails fire only on a state transition.passwordEnv) — never stored inconfig.json.Testing
publishable: true.routerblock is present.Docs
README section +
AGENTS.md. IPv6 / AAAA support noted as a TODO/Roadmap item (separate feature).