Infrastructure · TypeScript

One update loop.
Many providers.

uDDNS checks the public IP, compares per-host checkpoints, and updates DNS only when an address changes.

Design throughlineSeparate provider complexity from update state, then make network safety part of every connection.

Role
Design and implementation
Interfaces
Daemon · CLI · Docker · MCP
Project
Project site Repository
Jump to a uDDNS case study sectionCONDITIONCAUSECORRECTIONCONFIRM

Condition

One changing address could strand a whole set of hostnames.

Every public IPv4 or IPv6 change had to reach each hostname quickly, even when those records lived across several providers and accounts. A failure for one host could not send already-correct hosts back through the update cycle.

The service also had to tell a genuine address change from a brief discovery outage. Otherwise, a momentary network fault could become a wave of unnecessary DNS writes.

Cause

Providers agree on the outcome, not the route there.

Authentication, record lookup, response shapes, and dual-stack behavior change from one API to the next. Folding every variation into the update loop would make its state logic harder to inspect and easier to break.

A shared checkpoint would let one failed host disturb the rest. Ordinary URL validation also stops too early: a valid-looking endpoint can still resolve to a loopback, private, or metadata address.

Correction

Give every provider an adapter and every host its own memory.

Each provider implements one registered update interface, leaving the core loop concerned only with discovery, comparison, and state. Schema validation rejects unknown configuration early, while an independent atomic checkpoint records the last successful address for each host.

Public-IP and provider requests use pin-on-connect HTTPS. The client verifies the resolved address before connecting, rejects unsafe destinations by default, and retries transient failures with bounded jitter while honoring Retry-After.

Confirm

One small core now speaks to fifteen provider families.

The same engine runs as a long-lived daemon, a one-shot CLI, a Docker deployment, or an optional MCP transport. Multi-account YAML can also describe ordered failover when more than one provider serves a hostname.

Documentation-contract tests compare provider IDs, defaults, commands, and security allowlists with the implementation. The public instructions cannot quietly drift away from what the service actually accepts.