# dns-sync modular A plugin-based DNS reconciliation service. ## Plugin contract The reconciliation runtime is adapter-based. Adapter plugins expose: - `Plugin` - `configured_endpoints(config)` Each adapter declares object-specific discovery, apply, and verification capabilities. Built-in adapters live under `dns_sync/plugins/adapters/`. External adapters are loaded from `DNS_SYNC_PLUGIN_PATH` and must be explicitly enabled through `ENABLED_PLUGINS`. ## Plugins The generic reconciliation engine uses adapter plugins. Built-in adapters: - `npm` - Nginx Proxy Manager proxy hosts and certificates External adapters are loaded from `DNS_SYNC_PLUGIN_PATH` and must also be explicitly enabled through `ENABLED_PLUGINS`. Example: ```text ENABLED_PLUGINS=npm,verizon DNS_SYNC_PLUGIN_PATH=/plugins ``` The Verizon adapter lives under `external_plugins/verizon` and is deliberately not copied into the dns-sync container image. The external plugin directory is mounted read-only at runtime. ## Container runtime The container periodically executes the generic reconciliation engine: ```text python -m dns_sync reconcile ``` The default container mode is read-only. ```text SYNC_INTERVAL=1 DNS_SYNC_APPLY=false DNS_SYNC_STATE_DB=/var/lib/dns-sync/state.sqlite3 DNS_SYNC_PLUGIN_PATH=/plugins ENABLED_PLUGINS=npm,verizon ``` `SYNC_INTERVAL` is expressed in minutes. The interval begins after each reconciliation completes, so runs do not overlap. To permit planned changes to be applied and verified: ```text DNS_SYNC_APPLY=true ``` An optional mutation budget can be specified: ```text DNS_SYNC_MAX_CHANGES=10 ``` When `DNS_SYNC_APPLY=false`, reconciliation still performs discovery, normalization, desired-state reconciliation, planning, and state persistence, but no endpoint mutations are executed. The SQLite database should be stored on persistent storage. External plugins should be mounted read-only at `/plugins`. ## Main environment ```text NPM_ENDPOINTS=192.168.1.3,192.168.1.4,192.168.1.5 PROXY_API_PORT=81 PROXY_API_SCHEME=http DNS_TARGET_IP=192.168.1.101 VERIZON_ENDPOINTS=192.168.1.1 DOMAIN_MATCHING=false DOMAIN=example.com NPM_IDENTITY=... NPM_SECRET=... ROUTER_PASSWORD=... ``` ## Domain filtering Filtering is performed centrally after adapter discovery and before reconciliation, so endpoint-specific adapters get identical behavior. ```text DOMAIN_MATCHING=true DOMAIN=example.com ``` This includes the apex and every subdomain: ```text example.com app.example.com deep.app.example.com *.example.com ``` It excludes unrelated names such as: ```text example.net notexample.com example.com.evil.net ``` When `DOMAIN_MATCHING=false`, `DOMAIN` is ignored and all discovered records are eligible for reconciliation. When `DOMAIN_MATCHING=true`, `DOMAIN` is required.