โœจ Features

  • A provisioned record is managed from birth. POST /v1/dns/upsert wrote to Cloudflare and stopped there, so the monitor never knew the record existed. The next inventory reported it as unmanaged and an operator had to adopt a record the platform itself had just created โ€” the API was manufacturing exactly the drift adoption exists to clean up, once per provisioned tenant. Upsert now declares the record in desired state in the same call, and the response carries declared: true|false so a caller can tell.

    Declaring is never fatal. The DNS record already exists by that point, so a failure to record it is logged and reported, not turned into a failed response for an operation that succeeded. A deployment with no desired-state file configured is a supported configuration and simply reports declared: false.

๐Ÿงน Internal

  • Defdo.DDNS.DesiredStateStore.declare/1 is now the single definition of what declaring a record means. Adoption's promotion path used to carry a private copy of the entry shape; two copies would eventually disagree about what "already declared" means, and the same record could be stored twice or fail to match itself on the next reconcile.
  • declare/1 starts from an empty document when the file is absent. load/0 deliberately refuses to do this โ€” for a read, treating "no file" as "no intent" would silently unmanage the whole estate โ€” but a declaration adds a fact rather than inferring the absence of one, and without it the first record a fresh deployment provisions would be dropped in silence.

๐Ÿ”’ Security

  • Cleared all 28 advisories mix hex.audit reported. Most were removed rather than upgraded: tesla and a second mint came from restlax and cloudflare, neither declared in mix.exs; bypass pulled plug_cowboy โ†’ cowboy โ†’ cowlib โ€” a whole second HTTP server โ€” for tests that never used it. req was pinned ~> 0.5.0, which locked out every fix, and now tracks ~> 0.6.

0.4.3

๐Ÿž Fixes

  • Adoption persists by default. With no DDNS_ADOPTION_PATH configured, the store silently no-oped: refresh reported records added while list returned nothing, because writes went nowhere. It now defaults to /var/lib/defdo_ddns/adoption.json โ€” the same volume the record snapshot uses โ€” so a deployment that mounts it keeps decisions without configuring another path.

0.4.2

โœจ New Features

  • Adoption discovery over the API. POST /v1/adoption/refresh (body {"domain": "..."}) runs an inventory and populates the pending list. The release image has no mix, so without this the pending list could never be filled where DDNS actually runs โ€” list/accept/reject had nothing to act on. Read-only against Cloudflare, same auth as the other adoption endpoints.

0.4.1

๐Ÿž Fixes

  • Desired state seeds itself on first read. With DDNS_DESIRED_STATE_PATH set but no file yet, DesiredStateStore.load/0 now writes the file from the environment config on the first read instead of returning :missing_desired_state. The release image runs no mix, so there was no way for an operator to create the file, and accept (over the API) would have failed against a store that could never be initialized. Still fails loudly when there is genuinely nothing to seed from, and still never overwrites an existing file.

0.4.0

โœจ New Features

  • Record adoption. DDNS can now see records that exist in Cloudflare but were never declared here, and bring them under management through an explicit decision. The live defdo.ninja zone carries 33 such records against the dozen the config declares; until now nothing reported that gap, and nothing could converge it.
    • Reconcile.Inventory classifies a zone's live records as managed, unmanaged or missing. Read-only.
    • Adoption holds undeclared records as pending until an operator accepts or rejects them. Discovery is automatic; adoption never is. Rejections are durable, so a refused record never asks to be adopted again.
    • Accepting promotes the record into desired state atomically โ€” from there the monitor converges it. A failed promotion rolls the decision back rather than marking a record adopted that nothing manages.
    • Operator surface: mix defdo.ddns.adoption.{refresh,list,accept,reject}, plus GET/POST /v1/adoption[/:id/{accept,reject}] behind the existing API gate. Targets are redacted in listings.
  • File-backed desired state. DesiredStateStore moves DNS intent out of environment variables into a canonical file (DDNS_DESIRED_STATE_PATH). The file is authoritative once written; env becomes a one-time seed. Disabled unless the path is set, so adoption is opt-in per deployment. Kept distinct from the runtime record snapshot: intent and observation are not the same thing.

0.3.4

๐Ÿž Fixes

  • Cloudflare edge errors no longer shut the application down. Cloudflare returns 520-527 failures as a plain-text or HTML page rather than the documented JSON envelope. The client indexed into that body directly, so a single transient error code: 521 raised, killed the monitor, and โ€” after the supervisor restarted it into the same failing call and exhausted its restart intensity โ€” terminated the whole application, which then stayed down silently. Zone lookups, record listing, creation and updates now validate the response before reading it and degrade to their documented return value with a log line.
  • get_zone_id/1 no longer raises when Cloudflare returns an empty result list for an unknown domain.
  • The monitor now rescues per-domain and per-checkup, so an unexpected failure is logged and retried on the next tick instead of terminating the process.

Unreleased

โœจ New Features

  • Added safe runtime diagnostics for DDNS record store metadata, counts, source, and persistence flags.
  • Hardened bootstrap behavior for empty-state handling and malformed snapshot/init sources.
  • Documented release/TrueNAS record-store operations and adapter boundaries.

0.3.3

โœจ New Features

๐Ÿ› Bug Fixes

  • Moved DDNS record snapshot serialization out of the store adapter so future host apps can reuse the same snapshot/export primitives for database-backed adapters.

๐Ÿ“š Documentation

  • Updated README to document runtime record store behavior, snapshot/init paths, and the legacy status of CLOUDFLARE_CNAME_RECORDS_JSON.

0.3.2

โœจ New Features

  • Added multi-tenant-light API auth mode with client credentials and domain scope enforcement.
    • New runtime config: DDNS_API_CLIENTS_JSON.
    • New runtime flag: DDNS_API_ALLOW_RUNTIME_CLIENTS (deny-all bootstrap until runtime client injection).
  • Added ETS-backed in-memory auth store (Defdo.DDNS.API.AuthStore) for fast per-request client lookup.
  • Added public runtime API for embedded integrations:

๐Ÿ› Bug Fixes

  • Hardened API auth token verification with timing-safe comparison.
  • Added API auth fail-fast startup validation:
    • when DDNS_API_ENABLED=true, requires DDNS_API_TOKEN or DDNS_API_CLIENTS_JSON
    • unless DDNS_API_ALLOW_RUNTIME_CLIENTS=true.
  • Fixed DNS API params parsing to preserve explicit proxied=false in upsert payloads.
  • Enforced base_domain allowlist validation in client-auth mode before DNS upsert execution.

๐Ÿ“š Documentation

  • Expanded README with:
    • multi-tenant-light auth config and request headers
    • defdo_auth integration guidance (HTTP and embedded runtime injection modes).

0.3.1

๐Ÿ› Bug Fixes

  • Fixed A/AAAA auto-create behavior when a hostname already has non-IP records (TXT, MX, etc.):
    • the monitor now creates missing A/AAAA by record type and only skips when a conflicting CNAME exists.

0.3.0

โœจ New Features

  • Added optional embedded HTTP API (Bandit):
    • GET /health for liveness checks.
    • POST /v1/dns/upsert for authenticated CNAME upserts.
  • Added public dependency facade Defdo.DDNS:
    • One-shot/manual checkup (checkup_once/0) and monitor-aware checkup/0.
    • Runtime helpers for monitor lifecycle and configuration checks.
  • Added monitor runtime controls:
    • DDNS_ENABLE_MONITOR to disable background monitor startup.
    • DDNS_REFETCH_EVERY_MS to tune check interval without code changes.

๐Ÿ› Bug Fixes

  • Added one-shot monitor execution path (Defdo.Cloudflare.Monitor.checkup_once/0) so checkups can run without a running monitor process.
  • Hardened monitor tests to avoid external network/API dependency during test runs.
  • Fixed domain mapping normalization for root marker @:
    • @ is now treated as the zone root (e.g. paridin.net) instead of generating invalid names like @.paridin.net.

๐Ÿ“ฆ Packaging & Release

  • Added Apache License 2.0 file (LICENSE.md).
  • Added GitHub Actions workflow to validate and publish tagged releases to Hex (.github/workflows/publish_hex.yml).
  • Completed Hex package metadata and docs setup (description, package, docs, ex_doc).

๐Ÿ“š Documentation

  • Updated README with dependency/API usage and Bandit runtime configuration.

0.2.1

โœจ New Features

  • Added CLOUDFLARE_PROXY_EXCLUDE to keep selected hostnames in DNS only even when CLOUDFLARE_PROXY_A_RECORDS=true.
    • Supports exact hostnames and wildcard suffix patterns (for example *.idp-dev.defdo.ninja).
  • Added CLOUDFLARE_PROXY_A_RECORDS to force Cloudflare proxy mode (proxied=true) for A/AAAA records during updates and auto-creation.
  • Added CLOUDFLARE_A_RECORDS_JSON with JSON-first parsing for A monitor targets.
    • Supports object and array JSON formats for easier machine-generated config.
    • Falls back to legacy CLOUDFLARE_DOMAIN_MAPPINGS when JSON is empty or invalid.
  • Added CLOUDFLARE_AAAA_RECORDS_JSON for real IPv6 record synchronization.
    • AAAA records now use detected public IPv6 independently from A record IPv4 updates.
    • Supports object and array JSON formats, matching CLOUDFLARE_A_RECORDS_JSON.
  • Added declarative CNAME management via CLOUDFLARE_CNAME_RECORDS_JSON.
    • Supports name, target, optional proxied, ttl, and optional domain scope per record.
    • Enables wildcard/alias record management using a plain-text env var (JSON string), without requiring a database.
  • Added domain posture output in monitor logs:
    • [HEALTH][GREEN|YELLOW|RED] summary per domain with SSL mode, proxied/dns-only counts, and hairpin risk.
  • Added Cloudflare edge SSL mode checks (strict/full/flexible/off) to improve runtime diagnostics.
  • Added ACM advisory warnings for deep hostnames that may require Cloudflare Advanced Certificate Manager when proxied.
    • Logs now include [CERT][ACM] hints and recommendations.

๐Ÿ› Bug Fixes

  • Improved proxy toggle safety when exclusions are used:
    • Excluded records now resolve desired proxied=false during updates.
  • Normalized TTL when switching from proxied to DNS-only:
    • Records with Cloudflare auto TTL (1) are converted to a standard DNS-only TTL (300) to avoid invalid DNS-only state.
  • Normalized logs for container dashboards: removed ANSI/emoji log output and disabled console colors to improve log readability.
  • Removed Cloudflare modified_on timestamp from success log lines to avoid broken rendering in dashboards that misparse ISO8601 text.
  • When CLOUDFLARE_PROXY_A_RECORDS=true, records with correct IP but proxied=false are now patched to enable proxy mode.
  • Set explicit logger format without leading blank line ($time [$level] $message) to improve compatibility with log viewers.
  • Relative wildcard entries in CLOUDFLARE_DOMAIN_MAPPINGS (e.g. *.idp-dev, *.rnu) are now expanded to the current zone (e.g. *.idp-dev.defdo.ninja).
  • Proxy-mode updates now enforce Cloudflare Auto TTL (ttl=1) for proxied records, preventing update failures on some wildcard records.
  • Improved Cloudflare API error handling for DNS create/update calls to log errors whenever the API returns success=false (including result=nil responses).
  • Duplicate A/AAAA records for the same name+type are now handled safely: if one record is already in desired state, conflicting updates are skipped and a warning with record IDs is logged.
  • Improved auto-create safety:
    • When a hostname is declared in CLOUDFLARE_CNAME_RECORDS_JSON, monitor now skips auto-creating A records for that same name to avoid A/CNAME conflicts.
  • Improved auto-create logic for IP records:
    • Missing A and AAAA records are now created independently based on configured mappings and detected address family availability.
  • Added graceful handling when a zone ID cannot be resolved before DNS record operations.
  • Fixed release boot crash caused by runtime helper loading:
    • Moved configuration parsing helper to compiled app code (Defdo.ConfigHelper) so releases do not depend on external .exs files at runtime.

๐Ÿ“š Documentation

  • Expanded README with:
    • Cloudflare SSL/TLS mode behavior (Flexible, Full, Full (strict)).
    • Orange-cloud vs gray-cloud behavior and hairpin implications.
    • New CLOUDFLARE_PROXY_EXCLUDE configuration examples.
    • Health posture and ACM warning log interpretation.
  • Updated roadmap to reflect completed work and next priorities.

0.2.0

๐Ÿšจ Breaking Changes

  • Environment Variable Changes:

    • CLOUDFLARE_DOMAIN and CLOUDFLARE_SUBDOMAINS have been replaced with CLOUDFLARE_DOMAIN_MAPPINGS
    • New format: domain1.com:subdomain1,subdomain2;domain2.com:api,blog;domain3.com:
    • Migration required: Update your environment variables to use the new mapping format
  • Configuration Format:

    • Domain-to-subdomain mapping now uses a structured format instead of separate variables
    • Root domain monitoring requires explicit empty subdomain list (e.g., example.com:)

โœจ New Features

  • Multiple Domain Support: Monitor and update DNS records for multiple domains simultaneously
  • Auto-creation: New AUTO_CREATE_DNS_RECORDS option to automatically create missing DNS records
  • Promotional Comments: Auto-created DNS records include promotional comments
  • Improved Logging: Enhanced status messages with emojis and clearer error reporting
  • Better Error Handling: More robust error handling and validation

๐Ÿ› Bug Fixes

  • Fixed duplicate function definitions causing compilation warnings
  • Resolved configuration parsing issues in runtime.exs
  • Improved DNS record validation and creation logic

๐Ÿ“š Documentation

  • Complete README rewrite with step-by-step setup instructions
  • Added troubleshooting section with common issues
  • Docker Compose and UniFi Dream Machine Pro examples
  • Clear migration guide for breaking changes

0.1.1

  • Support for multiple domains
  • Subdomains must be specific otherwise they are added to the monitor domain even if they didn't exists.

0.1.0

  • Support for only one domain
  • Bootstrap defdo_ddns