# Changelog

All notable changes to nxdns are recorded here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Sections are written by hand. Nothing here is generated from commit messages:
the point of the file is to say what changed for an operator, which a commit
subject rarely does.

## [Unreleased]

### Added

- **Declarative configuration for IaC.** `nxdns run --config=<file>` makes the
  file the sole source of configuration: every boot converges the database to
  it in one transaction, preserving blocklist downloads, compiled lists and
  client history, so an unchanged file costs zero downloads and zero writes.
  Bare `nxdns run` keeps the database (and the web UI) in charge, exactly as
  before. In file mode the web UI is read-only for configuration and says so;
  runtime actions (pause, blocklist refresh, certificate reload) stay live.
  `GET /api/settings` reports which authority governs the process.
- `nxdns import` now refuses a file whose application would delete
  configuration rows, names the tables and counts, and applies it only with
  the new `--allow-delete` flag. Additive and edit-in-place imports need no
  flag.

### Changed

- **Breaking: `nxdns run --config <file>` changed meaning.** It used to seed
  the database once and then ignore the file; it now makes the file the
  authority on every boot, which deletes any configuration the file does not
  declare — including edits made through the web UI since the seed. Before
  upgrading a unit that carries `--config`: either drop the flag to keep the
  database in charge, or adopt file mode with the sequence in the upgrade
  guide. Order matters there: export the file with the NEW binary (stopped).
- **Breaking: 0.0.1 exports are refused by this version.** A 0.0.1
  `nxdns export` writes both `.password = ""` and the stored
  `.password_hash`, and this version refuses a file that carries both. This
  bites any old export — an adoption file or a configuration backup fed to
  `nxdns import` alike. Fix an existing export by deleting its
  `.password = ""` line (keep the `.password_hash` line). Take fresh backups
  with the new binary.
- **Breaking: the offline password-change recipe changed.** Setting
  `.password = "new"` together with `.password_hash = ""` is now refused
  (empty `password_hash` is an explicit "disable authentication", and the two
  fields cannot both be present). To change the password in the file: set
  `.password` and delete the `.password_hash` line entirely.
- `nxdns import --force` is renamed `--allow-delete`.
- A fresh install no longer seeds from `/etc/nxdns/config.zon` by presence.
  Use `nxdns import` once, or run in file mode with `--config`.

## [0.0.1] - 2026-08-09

First release. Everything below is new.

### Added

- **Forwarding DNS server.** UDP and TCP listeners with a wire-format parser and
  encoder written against RFC 1035 and EDNS(0), a bounded worker model, per-client
  rate limiting and a `pause` control that stops filtering without stopping
  resolution.
- **Encrypted upstreams.** DNS-over-HTTPS and DNS-over-TLS clients over a pool
  that tracks per-upstream health and fails over, with SNI and certificate
  verification driven by a per-upstream TLS name.
- **DoH and DoT endpoints.** nxdns also answers as an encrypted resolver, with a
  certificate store that reloads on disk changes and through the API, so renewals
  do not need a restart.
- **Blocklist filtering.** Subscriptions in hosts, plain-domain and
  Adblock-Plus-style formats, compiled into a compact matcher; per-group allow
  and block rules with wildcards; safe-search enforcement.
- **Per-client policy groups.** Clients are identified by address and assigned to
  groups, so the filtering a device gets depends on which device it is.
- **Local DNS.** Local A/AAAA/CNAME/PTR records and conditional forwarding of
  internal zones to another resolver.
- **Cache.** A bounded in-memory cache that respects upstream TTLs and expires
  entries rather than serving them stale.
- **Query log.** Queries land in SQLite under a retention policy in both rows and
  days, with disk-full self-protection that degrades instead of corrupting, and a
  live SSE stream of the same events.
- **Web UI and REST API.** A React single-page admin UI embedded in the binary,
  a REST API with a served OpenAPI document, session authentication, API rate
  limiting and Prometheus-style `/metrics`.
- **Configuration.** A ZON configuration file seeds the database on first boot;
  after that the database is the truth, and `nxdns export` / `nxdns import` move
  configuration in and out. `nxdns check` validates a file without starting.
- **CLI.** `run`, `check`, `export`, `import`, `version` and `help`.
- **Packaging.** A hardened systemd unit with a sysusers fragment, and a
  `FROM scratch` container image holding the binary, a CA bundle and the licence
  files, assembled by a builder stage pinned to `alpine:3.22` by digest. Nothing
  from Alpine ships in the published image except that CA bundle.
- **Releases.** Tags publish five assets — static musl tarballs for
  `x86_64-linux-musl` and `aarch64-linux-musl`, `IMAGE-DIGEST.txt` naming the
  multi-architecture container image by digest, `SHA256SUMS.txt` over those
  three, and `SHA256SUMS.txt.asc`, a detached signature over the checksum file.
  `zig build dist` and `zig build verify-dist` produce and check the same
  artifacts on a laptop.
- **Licensing.** EUPL-1.2, with a `THIRD-PARTY-NOTICES` file in every tarball and
  image assembled from a reviewed inventory of what the artifacts contain.
- **Documentation.** A Diátaxis split — tutorial, how-to, reference, explanation —
  with drift guards that fail the build when the reference pages fall behind the
  code.
