0.5.0
Copy Markdownโจ Features
A provisioned record is managed from birth.
POST /v1/dns/upsertwrote 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 carriesdeclared: true|falseso 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/1is 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/1starts from an empty document when the file is absent.load/0deliberately 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.auditreported. Most were removed rather than upgraded:teslaand a secondmintcame fromrestlaxandcloudflare, neither declared inmix.exs;bypasspulledplug_cowboy โ cowboy โ cowlibโ a whole second HTTP server โ for tests that never used it.reqwas 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_PATHconfigured, the store silently no-oped:refreshreported records added whilelistreturned 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 nomix, 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_PATHset but no file yet,DesiredStateStore.load/0now writes the file from the environment config on the first read instead of returning:missing_desired_state. The release image runs nomix, so there was no way for an operator to create the file, andaccept(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.ninjazone carries 33 such records against the dozen the config declares; until now nothing reported that gap, and nothing could converge it.Reconcile.Inventoryclassifies a zone's live records as managed, unmanaged or missing. Read-only.Adoptionholds undeclared records aspendinguntil 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}, plusGET/POST /v1/adoption[/:id/{accept,reject}]behind the existing API gate. Targets are redacted in listings.
- File-backed desired state.
DesiredStateStoremoves 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: 521raised, 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/1no 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
- Added portable DDNS snapshot codec (
Defdo.DDNS.RecordSnapshot) for canonical record normalization, JSON encode/decode, and legacy CNAME seed conversion. - Added runtime record store APIs for release-safe export and persistence:
- Added ETS-backed file store bootstrap precedence for DDNS records:
- snapshot file
- init file
- legacy CNAME env seed
- explicit empty state when allowed
๐ 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).
- New runtime config:
- 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, requiresDDNS_API_TOKENorDDNS_API_CLIENTS_JSON - unless
DDNS_API_ALLOW_RUNTIME_CLIENTS=true.
- when
- Fixed DNS API params parsing to preserve explicit
proxied=falsein upsert payloads. - Enforced
base_domainallowlist validation in client-auth mode before DNS upsert execution.
๐ Documentation
- Expanded README with:
- multi-tenant-light auth config and request headers
defdo_authintegration guidance (HTTP and embedded runtime injection modes).
0.3.1
๐ Bug Fixes
- Fixed
A/AAAAauto-create behavior when a hostname already has non-IP records (TXT,MX, etc.):- the monitor now creates missing
A/AAAAby record type and only skips when a conflictingCNAMEexists.
- the monitor now creates missing
0.3.0
โจ New Features
- Added optional embedded HTTP API (Bandit):
GET /healthfor liveness checks.POST /v1/dns/upsertfor authenticated CNAME upserts.
- Added public dependency facade
Defdo.DDNS:- One-shot/manual checkup (
checkup_once/0) and monitor-awarecheckup/0. - Runtime helpers for monitor lifecycle and configuration checks.
- One-shot/manual checkup (
- Added monitor runtime controls:
DDNS_ENABLE_MONITORto disable background monitor startup.DDNS_REFETCH_EVERY_MSto 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_EXCLUDEto keep selected hostnames inDNS onlyeven whenCLOUDFLARE_PROXY_A_RECORDS=true.- Supports exact hostnames and wildcard suffix patterns (for example
*.idp-dev.defdo.ninja).
- Supports exact hostnames and wildcard suffix patterns (for example
- Added
CLOUDFLARE_PROXY_A_RECORDSto force Cloudflare proxy mode (proxied=true) forA/AAAArecords during updates and auto-creation. - Added
CLOUDFLARE_A_RECORDS_JSONwith JSON-first parsing for A monitor targets.- Supports object and array JSON formats for easier machine-generated config.
- Falls back to legacy
CLOUDFLARE_DOMAIN_MAPPINGSwhen JSON is empty or invalid.
- Added
CLOUDFLARE_AAAA_RECORDS_JSONfor 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, optionalproxied,ttl, and optionaldomainscope per record. - Enables wildcard/alias record management using a plain-text env var (JSON string), without requiring a database.
- Supports
- 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.
- Logs now include
๐ Bug Fixes
- Improved proxy toggle safety when exclusions are used:
- Excluded records now resolve desired
proxied=falseduring updates.
- Excluded records now resolve desired
- 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.
- Records with Cloudflare auto TTL (
- Normalized logs for container dashboards: removed ANSI/emoji log output and disabled console colors to improve log readability.
- Removed Cloudflare
modified_ontimestamp from success log lines to avoid broken rendering in dashboards that misparse ISO8601 text. - When
CLOUDFLARE_PROXY_A_RECORDS=true, records with correct IP butproxied=falseare 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(includingresult=nilresponses). - Duplicate A/AAAA records for the same
name+typeare 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-creatingArecords for that same name to avoidA/CNAMEconflicts.
- When a hostname is declared in
- Improved auto-create logic for IP records:
- Missing
AandAAAArecords are now created independently based on configured mappings and detected address family availability.
- Missing
- 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.exsfiles at runtime.
- Moved configuration parsing helper to compiled app code (
๐ 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_EXCLUDEconfiguration examples. - Health posture and ACM warning log interpretation.
- Cloudflare SSL/TLS mode behavior (
- Updated roadmap to reflect completed work and next priorities.
0.2.0
๐จ Breaking Changes
Environment Variable Changes:
CLOUDFLARE_DOMAINandCLOUDFLARE_SUBDOMAINShave been replaced withCLOUDFLARE_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_RECORDSoption 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