• v0.0.21
    Gates / frontend (push) Successful in 2m1s
    Gates / test (push) Successful in 2m25s
    Gates / test-aarch64 (push) Successful in 8m19s
    Gates / package (push) Successful in 4m22s
    Gates / container (push) Successful in 12s
    CI / gates (push) Successful in 28m50s
    Release / guard (push) Successful in 35s
    Gates / frontend (push) Successful in 1m57s
    Gates / test (push) Successful in 2m24s
    Gates / test-aarch64 (push) Successful in 7m38s
    Gates / package (push) Successful in 54s
    Gates / container (push) Successful in 18s
    Release / gates (push) Successful in 11m18s
    Release / publish (push) Successful in 4m56s
    Stable

    mokhtar released this 2026-09-12 19:12:55 +00:00 | 0 commits to master since this release

    Changed

    • An upstream warning means the pool stopped trusting the endpoint. A Diagnostics episode opens when an upstream reaches the health failure threshold and closes on the success that clears it, instead of one card per lost exchange. Failures during backoff raise the card's occurrence count. The episode detail and the nxdns check report name the concrete cause behind the classification, SendFailed (cause BrokenPipe).

    Artifacts

    26615c4b52cbc1e60468bc40849be025b4214caa5a2b7dcde0a86da7cab3b4ce  nxdns-0.0.21-x86_64-linux-musl.tar.gz
    29746c9a68b7c07c1f89be216b6ba59a750f7d67767781482b100558567f19af  nxdns-0.0.21-aarch64-linux-musl.tar.gz
    d7f0f99e80b7cdd1c621235a2ccb8f1ab9537e25728a69beb9da63e2b97e2f32  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.21@sha256:ce1fef32c7ee6c23656abd94e53e4d4d6b1d34fe88a2c842b82c7202d8b904c3
    

    Compare v0.0.20...v0.0.21

    Commits since v0.0.20
    f2dd6ad build: bump version to 0.0.21
    08cdf86 upstream: a diagnostics episode follows health, and a peer fault carries its cause
    
    Downloads
  • v0.0.20
    Gates / frontend (push) Successful in 1m52s
    Gates / test (push) Successful in 2m22s
    Gates / test-aarch64 (push) Successful in 8m10s
    Gates / package (push) Successful in 4m22s
    Gates / container (push) Successful in 10s
    CI / gates (push) Successful in 28m22s
    Release / guard (push) Successful in 35s
    Gates / frontend (push) Successful in 1m53s
    Gates / test (push) Successful in 2m20s
    Gates / test-aarch64 (push) Successful in 7m26s
    Gates / package (push) Successful in 51s
    Gates / container (push) Successful in 11s
    Release / gates (push) Successful in 10m52s
    Release / publish (push) Successful in 4m56s
    Stable

    mokhtar released this 2026-09-12 13:55:06 +00:00 | 2 commits to master since this release

    Fixed

    • A blocklist source no longer fails on a stale pooled connection. The fetcher reused a keep-alive connection from an earlier pass that the server had closed, and the standard client never retries one; every later download from that host failed with HttpConnectionClosing at 0 ms. Downloads now send connection: close and never enter the pool.

    Artifacts

    ae74e98e8ec9f6e63ea48371278660c1ba94ec4da8ecdd20afdcfb551850899d  nxdns-0.0.20-x86_64-linux-musl.tar.gz
    da9f3a148983822e120c3ce657ac34aa4ae4a7d76d0d1ca2fd930e67c580126e  nxdns-0.0.20-aarch64-linux-musl.tar.gz
    80ded73a588da1dc90cd38c6202f13fb3fd7bcd73b9cd1f9cf545601dc9bdd3d  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.20@sha256:955379672493f64b59ed3f18899fb0808197492e2abd0eedcaa6c6ec6d1f3258
    

    Compare v0.0.19...v0.0.20

    Commits since v0.0.19
    6e9a369 build: bump version to 0.0.20
    f6fa43a filter: blocklist downloads never reuse a pooled connection, the peer closes it between passes
    
    Downloads
  • v0.0.19
    Gates / frontend (push) Successful in 2m3s
    Gates / test (push) Successful in 2m33s
    Gates / test-aarch64 (push) Successful in 8m14s
    Gates / package (push) Successful in 52s
    Gates / container (push) Successful in 14s
    CI / gates (push) Successful in 12m0s
    Release / guard (push) Successful in 33s
    Gates / frontend (push) Successful in 2m36s
    Gates / test (push) Successful in 3m47s
    Gates / test-aarch64 (push) Successful in 7m24s
    Gates / package (push) Successful in 50s
    Gates / container (push) Successful in 15s
    Release / gates (push) Successful in 12m19s
    Release / publish (push) Successful in 6m37s
    Stable

    mokhtar released this 2026-09-12 09:14:36 +00:00 | 4 commits to master since this release

    Changed

    • A failed blocklist download says why. The warning line and the Diagnostics event detail now carry the phase that failed, the concrete cause behind the classification (a reset connection, a truncated chunk, a TLS handshake fault), the HTTP status if a head arrived, the bytes received, and the elapsed time. The separate http status warning is folded into that line. Same one line per failed source per pass as before.

    Artifacts

    4af595f259b225671e54186e00eb6574c6f0180f212f1fe9b8955d6b6b74b028  nxdns-0.0.19-x86_64-linux-musl.tar.gz
    b0d223b272794cf469484ff00edc1bd23ee5632c8c5c23ccc1cec802fe6ccb7b  nxdns-0.0.19-aarch64-linux-musl.tar.gz
    b1afe29eed90b2e87e7fd603ea8e6e6478d96946861151efbd3275adf0ff4fe3  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.19@sha256:b80023379b012db3898d78f716a5e6c6967459416252f528cc207cffae44b0e5
    

    Compare v0.0.18...v0.0.19

    Commits since v0.0.18
    f4562ca filter: skip the read-only directory test as root, ci's runner ignores mode bits
    250bdca build: bump version to 0.0.19
    63f4f96 changelog: date the 0.0.19 section
    5215819 filter: a failed blocklist download names its phase, cause, status, bytes and elapsed time
    bf79a22 docs: renovate's nix manager does not bump a tag ref; the regex manager recipe that does
    
    Downloads
  • v0.0.18
    Gates / frontend (push) Successful in 2m34s
    Gates / test (push) Successful in 2m39s
    Gates / test-aarch64 (push) Successful in 8m16s
    Gates / package (push) Successful in 4m21s
    Gates / container (push) Successful in 16s
    CI / gates (push) Successful in 15m39s
    Release / guard (push) Successful in 35s
    Gates / frontend (push) Successful in 2m1s
    Gates / test (push) Successful in 2m28s
    Gates / test-aarch64 (push) Successful in 7m24s
    Gates / package (push) Successful in 52s
    Gates / container (push) Successful in 11s
    Release / gates (push) Successful in 10m59s
    Release / publish (push) Successful in 5m11s
    Stable

    mokhtar released this 2026-09-09 17:57:40 +00:00 | 9 commits to master since this release

    Fixed

    • The Overview scope pickers no longer sit apart, and a long device name no longer pushes the period picker off a phone screen. The Device and Period dropdowns fill their wrappers and stand side by side; a long name truncates with an ellipsis in the trigger and stays fully readable in the list.

    Artifacts

    b815a397c16f3f85603e2b0983274d6b3c8fbb8d238d06bbfa2eb8f8cfc1e740  nxdns-0.0.18-x86_64-linux-musl.tar.gz
    ed39ae26be9392045d3e462f36210e1557e4ce4a7e7638d64a697ebc051e7bbf  nxdns-0.0.18-aarch64-linux-musl.tar.gz
    26775f1fd8642a6b90ef34016559a917dc459f9cb980d4f2f83e87aeefa802f2  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.18@sha256:2af3899f65c886120267806310c63e319c8bb891d1ab8b72145c43e3a6f5b488
    

    Compare v0.0.17...v0.0.18

    Commits since v0.0.17
    bcd9bce build: bump version to 0.0.18
    2b790c4 changelog: 0.0.18 section
    1860ff5 admin: the overview pickers fill their wrappers and share a narrow row
    
    Downloads
  • v0.0.17
    Gates / frontend (push) Successful in 2m23s
    Gates / test (push) Successful in 3m15s
    Gates / test-aarch64 (push) Successful in 8m38s
    Gates / package (push) Successful in 5m0s
    Gates / container (push) Successful in 19s
    CI / gates (push) Successful in 17m17s
    Release / guard (push) Successful in 37s
    Gates / frontend (push) Successful in 2m15s
    Gates / test (push) Successful in 2m34s
    Gates / test-aarch64 (push) Successful in 7m33s
    Gates / package (push) Successful in 51s
    Gates / container (push) Successful in 10s
    Release / gates (push) Successful in 11m14s
    Release / publish (push) Successful in 8m35s
    Stable

    mokhtar released this 2026-09-08 22:35:07 +00:00 | 12 commits to master since this release

    Added

    • A Nix flake with tag-pinned hashes. flake.nix at the repository root builds nxdns for aarch64-linux and x86_64-linux from the release tarballs, and carries their hashes in a generated block. A consumer pins the flake to a release tag and gets the exact bytes that tag published; docs/how-to/install-with-nix.md covers the input, the nixpkgs follows line, and Renovate.

    Changed

    • The release archive is written by the project's own tool. zig build dist no longer shells out to the runner's tar and gzip; it writes the tar stream and the gzip container itself, with sorted entries, fixed modes, and zero timestamps. The tarball bytes now depend on the source tree, the compiler, and the admin bundle, and on nothing the host supplies — which is what lets a hash be pinned before CI rebuilds it.
    • The cut pins the release hashes before it commits. zig build cut asserts the local Node, npm, and Zig match the versions CI pins, builds the release from the bumped manifest in a normalized environment, writes the resulting hashes into flake.nix, and commits that file alongside build.zig.zon as one commit. CI reverifies the pins on the bump commit and again on the tag, before anything is uploaded.
    • nxdns version no longer reports a git commit. The commit is gone from the command's output, from GET /api/version, and from the admin footer, which now shows nxdns v<version>. A release identifies itself by version, and the bytes are reproducible from the tag, so a commit embedded in the binary told a reader nothing the tag did not.

    Artifacts

    8ee0b1f30b1ee43bcb0a0c8e789469fac794523e31468b54c199d0b571c8ab6b  nxdns-0.0.17-x86_64-linux-musl.tar.gz
    b439ca554968b31e8d2b9a8ba9400fbc89dc27e0bb259d5d3526bc4221333c77  nxdns-0.0.17-aarch64-linux-musl.tar.gz
    35ac38080eaf72f39fa6a97bd18bcfa3cc62df44b3228e52d35dc49682a24c52  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.17@sha256:dde9833df05ce227a13618f3469cccd1be2ad0fcaf03f526c076a89bf7ce5c55
    

    Compare v0.0.16...v0.0.17

    Commits since v0.0.16
    f168247 cut: build the release with the official zig tarball, skip hidden bundle files; re-pin 0.0.17
    d596fd7 build: bump version to 0.0.17
    f067742 cut: the pin stage compiles, and `zig build test` builds the cut binary
    a4eb749 changelog: 0.0.17
    22abcd9 release: nix flake with tag-pinned hashes, reproducible tarballs (milestone 40)
    3e57f43 cut: rerun a flaky release run once, resume after the tag
    299d7af upstream: overlap attribution test builds its schedule with gates
    
    Downloads
  • v0.0.16
    Gates / frontend (push) Successful in 1m57s
    Gates / test (push) Successful in 2m34s
    Gates / test-aarch64 (push) Successful in 8m9s
    Gates / package (push) Successful in 7m14s
    Gates / container (push) Successful in 17s
    CI / gates (push) Successful in 18m17s
    Release / guard (push) Successful in 33s
    Gates / test-aarch64 (push) Successful in 7m22s
    Gates / container (push) Successful in 11s
    Release / gates (push) Successful in 10m35s
    Gates / frontend (push) Successful in 2m8s
    Gates / test (push) Successful in 2m16s
    Gates / package (push) Successful in 44s
    Release / publish (push) Successful in 10m4s
    Stable

    mokhtar released this 2026-09-07 22:05:41 +00:00 | 19 commits to master since this release

    The Overview page is redesigned around the two dashboards people already know — Pi-hole's layout, NextDNS's charts — and every number in the admin is spelled one way.

    Changed

    • Overview reads like an analytics dashboard. Four centred totals (queries, blocked, active clients, and the blocked share), a smooth area chart of total and blocked queries where hovering marks the point and shows its figures beside it, a stacked client-activity chart in eight distinct hues with everything else as one gray band, and a row of cards under them: the cache hit rate as a progress bar, the query types as a single-hue ring, and the upstream breakdown.
    • Overview can be scoped to one device. A device selector sits beside the period selector; both are URL state, so /overview?period=7d&client=192.0.2.30 is a link to exactly that view. The API takes the same client parameter on GET /api/overview, and the tile links carry the scope into Activity.
    • One formatting contract. Counts are thousands-grouped, percentages always show two decimals (15.44%, never 15.4%), rates one decimal, and durations their two largest nonzero units ("6d 4h", "12m 5s", "6d 5s"). A test sweeps the sources for any number formatted outside the one module.
    • A quieter, more consistent chrome. One 4px corner radius everywhere, cards with a prominent title and a one-line description, the sidebar grouped into Monitoring and System with a status block at its foot (protection, queries per minute on Overview, uptime), and a top bar in place of the sidebar on narrow screens. The active navigation item and the accent blue are darkened to clear WCAG AA on their washes.

    Artifacts

    c682f6801aace3e1529b9a5e181abfc24fa3f421395bcc03aee19f9bcc6c908e  nxdns-0.0.16-x86_64-linux-musl.tar.gz
    0bfff7496e26bd10cd175a7e1e93e2ce94c1a2c3efa23348776b6258f44abb7f  nxdns-0.0.16-aarch64-linux-musl.tar.gz
    5ed07ca8e33c299669788e238e260c7f2a3db659f59e23f48c1ecf2331fb7569  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.16@sha256:e5b7360d4cc6d6f32e77094c554103978ce875893613d84aad25935a9e940966
    

    Compare v0.0.15...v0.0.16

    Commits since v0.0.15
    85b8be5 admin: overview redesign, device scope, one formatting contract (milestone 39)
    e656670 admin: ui polish pass, thirty findings from the emil audit
    
    Downloads
  • v0.0.15
    Gates / frontend (push) Successful in 1m51s
    Gates / test (push) Successful in 2m29s
    Gates / test-aarch64 (push) Successful in 7m24s
    Release / guard (push) Successful in 32s
    Gates / frontend (push) Successful in 2m14s
    Gates / package (push) Successful in 4m56s
    Gates / test (push) Successful in 2m20s
    Gates / test-aarch64 (push) Successful in 7m25s
    Gates / package (push) Successful in 1m3s
    Gates / container (push) Successful in 15s
    CI / gates (push) Successful in 26m26s
    Gates / container (push) Successful in 11s
    Release / gates (push) Successful in 16m6s
    Release / publish (push) Successful in 6m43s
    Stable

    mokhtar released this 2026-08-31 17:24:54 +00:00 | 21 commits to master since this release

    The Activity page's history filters become a toolbar you can actually use. Filters apply as you set them, the client field stops asking you to remember IP addresses, and the whole admin picks up one coherent icon set.

    Changed

    • History filters apply live from a toolbar. The form-and-Apply-button row is gone. Domain text filters as you type (debounced), result and time are one-click controls, and a custom date range applies as one change. Every filter state is a bookmarkable URL.
    • The client filter is a picker, not a text field. It lists the clients the server knows — named, sorted, multi-select — instead of asking for an exact address. Selected clients show as removable chips (first three, then a count), and a query can filter on up to 32 clients at once; the API accepts the same list.
    • One icon set. Interface glyphs (dropdown carets, checkbox ticks, the search magnifier, status marks, back arrows) are now Phosphor icons instead of a mix of text characters and hand-drawn shapes.
    • The admin bundle budget rises from 800,000 to 900,000 bytes. The client picker, the accessible menu and dialog primitives behind it, and the Phosphor icon components are the arrivals that spend it; the built assets sit at about 840,000 bytes.

    Added

    • Accessibility pass over the admin. Focus-visible rings on every control, labels wired to their inputs, keyboard-reachable menus and dialogs, and visible text where information previously lived only in hover titles.

    Artifacts

    c0f12429cc4755e8690388ef43d76b28a5a088e4737a294ae89e60a6932a034d  nxdns-0.0.15-x86_64-linux-musl.tar.gz
    739857763e84eb849bbbf4594a6f87f46e1834a4e9870d19f20cdf12fe7901d2  nxdns-0.0.15-aarch64-linux-musl.tar.gz
    71f2261dbc3f073948370683b93768fc92fb1367c6610d86f5f0327a3b052c32  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.15@sha256:b9053a0660b70639572e11697e75b21a3cc47e1fae09d1eeb92e8466ef0c58a3
    

    Compare v0.0.14...v0.0.15

    Commits since v0.0.14
    2ae0c97 admin: per-icon phosphor imports, the barrel loads every icon module under vitest
    2525b01 admin: phosphor icons replace hand-drawn svgs and text-character glyphs
    b774b05 activity: select-only multi-client filter
    d79dd0b admin: prettier formatting for the toolbar-era files, missed before ci ever ran them
    039eeed admin: activity history filters become a live-apply toolbar
    c65d92d admin: title-only information becomes visible text
    207252a admin: enable toggles become rac switches
    f1de804 admin: group sources and safe search become rac checkboxes
    3c67496 admin: the mobile drawer becomes a rac disclosure, asset budget raised to 850,000 bytes
    59d6be9 admin: associate inline field errors with their inputs
    317d5dd admin: live query detail becomes a modal, live/history and period pickers become tabs and radios, client delete confirms in a dialog
    79578e1 admin: reclaim the desktop header, move pause and log out to the sidebar
    d5613ee admin: pointer cursor on every button
    
    Downloads
  • v0.0.14
    Release / guard (push) Successful in 34s
    Gates / frontend (push) Successful in 1m47s
    Gates / test (push) Successful in 2m43s
    Gates / test-aarch64 (push) Successful in 7m29s
    Gates / package (push) Successful in 58s
    Gates / container (push) Successful in 14s
    Release / gates (push) Successful in 11m27s
    Release / publish (push) Successful in 1m30s
    Stable

    mokhtar released this 2026-08-30 10:34:06 +00:00 | 34 commits to master since this release

    Schema changes stop costing you your query history. querylog.db is now version-stamped and migrated in place; the server refuses to start rather than ever reset a healthy file, and the release tooling refuses to ship a schema change that is neither migratable nor explicitly disclosed with recovery steps. Three releases (0.0.6, 0.0.9, 0.0.12) each discarded the log on upgrade; this ends that.

    Changed

    • querylog.db is migrated in place. The file now carries a schema version, and a release that changes the schema ships a migration that runs at startup: one consistent backup (querylog.db.pre-migrate-<timestamp>, mode 0600, only the most recent kept), then every step and the version stamp in a single transaction. A failure before the commit rolls back and leaves your file exactly as it was.
    • The server refuses instead of resetting. A querylog.db it cannot use — newer than the binary, older than 0.0.12, or mid-migration failure — is left untouched and the server exits with a clear message instead of setting the file aside and starting an empty log. The exit code (2) tells systemd not to restart-loop a deliberate refusal. Corruption is the only case that still sets a file aside automatically.
    • The release gate now enforces the contract. A schema change cannot be tagged unless it either ships a working migration (proven in CI against a frozen fixture of the previous schema, with shipped migration files locked byte-for-byte once released) or explicitly declares a break — which requires a version bump the server refuses on, a reset disclosure, and step-by-step restore instructions in this file.

    One hazard to know when downgrading. The first start under this release restamps querylog.db from the old fingerprint to version 1 (contents untouched). If you later downgrade to 0.0.13 or older, that binary treats the new stamp as a schema mismatch, moves your file aside as querylog.db.schema-changed-<timestamp>, and starts an empty log. To recover: return to 0.0.14 or newer, stop the server, move the empty querylog.db away and delete its querylog.db-wal and querylog.db-shm files (leaving them would corrupt the restored file), rename the .schema-changed-<timestamp> file back to querylog.db, and start.

    Artifacts

    bd75dbc880109880f5d9e1ef56862637d771f3dda29fca396371612c48d3e869  nxdns-0.0.14-x86_64-linux-musl.tar.gz
    71e9102d536b6e87c8de892b538521c08e6ad26baf2e7a602755636243edac40  nxdns-0.0.14-aarch64-linux-musl.tar.gz
    776d7b8392db091e0de3f24e17fff89bbd086588ecb2623b4893f2a10ef642f8  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.14@sha256:c729fb4aa37054c5dbafa3a0d668898222e1c1e0364eeb955ef861a04707ebe9
    

    Compare v0.0.8...v0.0.14

    Commits since v0.0.8
    09932b2 build: bump version to 0.0.14
    272655f storage: version querylog.db and migrate it in place, never reset a healthy file
    dd5a9f6 build: bump version to 0.0.13
    72cdbbd upstream: one absolute per-query budget across queueing and failover
    6170571 build: bump version to 0.0.12
    c847072 overview: one endpoint, live projections and a response cache (m36)
    7cdb74e build: bump version to 0.0.11
    4367003 changelog: 0.0.11
    728b8d6 admin: draw the overview charts with visx
    091962a build: bump version to 0.0.10
    4fcbd0a changelog: 0.0.10
    d961b15 db-mode config changes apply live in-process
    17e6e93 admin: only the content region scrolls on wide screens
    8a44d90 cut: schema gate — refuse to release an undisclosed querylog schema change
    03149b5 ci: drop the one-insn-per-tb workaround, the qemu panic is an 11.1.0-only regression
    a972c8d logger tests: join every writer future before its database closes, fix the gate-open race
    78d688a ci: run the aarch64 suite with one qemu insn per tb, tcg optimization falsely trips the ubsan pointer check
    fe5bd96 admin: live ring capacity is injectable, eviction test no longer timing-bound
    1f215ac build: bump version to 0.0.9
    2f86cd7 changelog: 0.0.9 releases today
    51cda8b milestone 33: contract closure — samples, file-authority enumeration, dead code, bundle ceiling
    e5f23d5 querylog: return to the default checkpoint cadence
    24521ab milestone 32: task-shaped configuration, file mode as a rendering, config status api
    c99a37d milestone 31: concurrent upstream exchanges, dot session reuse, queue metrics
    623667e milestone 30: overview as a dashboard, explicit health contract, period aggregations
    0e83477 agents: move opus-coder to the global scope
    fa323c7 milestone 29: activity — history, live and policy simulation on one surface
    0fd6bbd milestone 28: query provenance — every logged query is exactly explainable
    7e6cb50 release cut: bump-kind justfile recipe and a compiled, tested cut tool
    8a17e9e admin: fix dashboard phantom scroll, drop last-failure column from upstream table
    0107df5 spec: measurement window is the deployment side's call
    
    Downloads
  • v0.0.13
    Release / guard (push) Successful in 34s
    Gates / frontend (push) Successful in 1m39s
    Gates / test (push) Successful in 2m27s
    Gates / test-aarch64 (push) Successful in 8m9s
    Gates / package (push) Successful in 4m24s
    Gates / container (push) Successful in 16s
    Release / gates (push) Successful in 15m18s
    Release / publish (push) Failing after 4m55s
    Stable

    mokhtar released this 2026-08-30 10:20:09 +00:00 | 36 commits to master since this release

    The upstream query budget becomes one honest deadline. A busy network no longer blames a healthy standby for running out of time, and a query burst no longer queues invisibly until everything answers SERVFAIL at once.

    Fixed

    • The per-query upstream budget is now one absolute deadline, spent by everything that blocks. Waiting for a free slot on a saturated upstream now spends the query's upstream.total_timeout_ms budget just like the exchange itself, instead of being invisible to it — under a burst, queries used to wait out their whole budget in the queue and then start attempts they could never finish. An attempt near the end of the budget runs truncated, and when a truncated attempt runs out of time that is evidence about the budget, not the upstream: it no longer counts against that upstream's health or success rate, and the query log no longer names an upstream that was given no fair chance. A field incident produced 279 rows blaming a standby whose health counters read zero for zero; those rows now attribute nothing.
    • A query no longer blocks behind a saturated upstream while another has capacity. Admission sweeps the upstreams in priority order and takes the first free slot; priority now means the order among upstreams that can be admitted right now, and a query blocks only when nothing has capacity — on the highest-priority eligible upstream, bounded by the remaining budget.
    • Conditional forward zones spend upstream.read_timeout_ms once per query. A UDP attempt, a truncated answer and the TCP retry now share the one budget instead of taking a fresh one each, so a slow zone resolver can no longer stretch a single query to several times the configured timeout.

    Added

    • nxdns_upstream_budget_exhausted_total. A pool-wide counter of queries whose budget ran out — in the queue or mid-attempt — before any upstream answered. It carries no per-upstream label on purpose: running out of budget is a fact about the pool.
    • A configuration with more than 64 enabled upstreams is rejected at validation with a clear message, instead of tripping an internal limit at startup.

    Artifacts

    ef3d01e57e5a07c729aae2fa1c9ed5973984605fd4aefaf96c40960f1f0c946a  nxdns-0.0.13-x86_64-linux-musl.tar.gz
    daf75bb747e953a498473ce0e6035326f8be4d5a2f66047667c8b83320b6793a  nxdns-0.0.13-aarch64-linux-musl.tar.gz
    bed65338d9ef9dcc72475342ff99735262ffb93e2e15c8805d353f43853a52ab  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.13@sha256:75ee3fa99c48cb2302671cfb2062d01bbc303edc18d526c4f9be0887a472a1d6
    

    Compare v0.0.8...v0.0.13

    Commits since v0.0.8
    dd5a9f6 build: bump version to 0.0.13
    72cdbbd upstream: one absolute per-query budget across queueing and failover
    6170571 build: bump version to 0.0.12
    c847072 overview: one endpoint, live projections and a response cache (m36)
    7cdb74e build: bump version to 0.0.11
    4367003 changelog: 0.0.11
    728b8d6 admin: draw the overview charts with visx
    091962a build: bump version to 0.0.10
    4fcbd0a changelog: 0.0.10
    d961b15 db-mode config changes apply live in-process
    17e6e93 admin: only the content region scrolls on wide screens
    8a44d90 cut: schema gate — refuse to release an undisclosed querylog schema change
    03149b5 ci: drop the one-insn-per-tb workaround, the qemu panic is an 11.1.0-only regression
    a972c8d logger tests: join every writer future before its database closes, fix the gate-open race
    78d688a ci: run the aarch64 suite with one qemu insn per tb, tcg optimization falsely trips the ubsan pointer check
    fe5bd96 admin: live ring capacity is injectable, eviction test no longer timing-bound
    1f215ac build: bump version to 0.0.9
    2f86cd7 changelog: 0.0.9 releases today
    51cda8b milestone 33: contract closure — samples, file-authority enumeration, dead code, bundle ceiling
    e5f23d5 querylog: return to the default checkpoint cadence
    24521ab milestone 32: task-shaped configuration, file mode as a rendering, config status api
    c99a37d milestone 31: concurrent upstream exchanges, dot session reuse, queue metrics
    623667e milestone 30: overview as a dashboard, explicit health contract, period aggregations
    0e83477 agents: move opus-coder to the global scope
    fa323c7 milestone 29: activity — history, live and policy simulation on one surface
    0fd6bbd milestone 28: query provenance — every logged query is exactly explainable
    7e6cb50 release cut: bump-kind justfile recipe and a compiled, tested cut tool
    8a17e9e admin: fix dashboard phantom scroll, drop last-failure column from upstream table
    0107df5 spec: measurement window is the deployment side's call
    
    Downloads
  • v0.0.12
    Release / guard (push) Successful in 35s
    Gates / frontend (push) Successful in 1m40s
    Gates / test (push) Successful in 2m23s
    Gates / test-aarch64 (push) Successful in 7m12s
    Gates / package (push) Successful in 4m18s
    Gates / container (push) Successful in 16s
    Release / gates (push) Successful in 14m12s
    Release / publish (push) Failing after 4m57s
    Stable

    mokhtar released this 2026-08-30 09:58:51 +00:00 | 38 commits to master since this release

    Overview stops re-reading the whole query log. One endpoint, one snapshot, pre-aggregated buckets — a 30-day view now costs the same on a month of history as on a day of it. Read the upgrade note first: it resets your query history.

    Changed

    • Upgrading resets your query history. The query-log schema gains the aggregate tables described below, and querylog.db is never migrated: the first start after the upgrade sets the old file aside (kept on disk next to the new one, named with the reason) and begins a fresh log. Settings, groups, blocklists and every other configuration are untouched.
    • The Overview is served by one endpoint, GET /api/overview. It replaces GET /api/stats, /api/stats/timeseries, /api/stats/types, /api/stats/routes and /api/stats/clients, which are gone. The five panels now come from a single database snapshot, so they can no longer disagree with each other, and the page shows one loading and one error state instead of five.
    • Query statistics are pre-aggregated as they are written. The query log now maintains 30-minute aggregate tables in the same transaction that stores the rows, and the 24-hour, 7-day and 30-day views read those instead of scanning every logged query. The cost of opening the Overview no longer grows with the size of the log: measured at three million rows, the 30-day view went from roughly eight-tenths of a second of scanning to under fifty milliseconds, at the price of about ten percent on each background write batch and ~1.5 MB of disk. The server also keeps the most recent response per period in memory and serves repeat polls from it while nothing has changed — until new queries land, retention prunes, or the period's time window rolls forward — so on a quiet network most of the steady 30-second refreshes do no database work at all.

    The Overview charts move to visx and grow up: one hover treatment across all four, honest labels, and maintained d3 math under the app's own rendering.

    Changed

    • The Overview charts are drawn with visx. The hand-written chart layout code is replaced by visx 4.0.0 primitives — maintained d3 math for the scales, ticks, stacking and arcs — while the rendering, colours and themes stay the app's own. The charts read as before, with four behaviour improvements: all four charts now share the same hover treatment (the per-client chart and both donuts gain the tooltip and dimming the query timeline already had, so pointing at a ring segment names it, its count and its share), an open tooltip follows a data refresh instead of showing stale counts, and it retires cleanly when the time window rolls. The admin bundle grows by about 68 KB and stays under its size budget.
    • The query timeline's third series is called "Allowed". What the chart called "Other" is every query that was neither blocked nor served from cache — answered upstream, from a local record or a forward zone — so it is now named for what it is rather than for the subtraction that produces it. It stays on the chart even in a window where nothing was allowed, alongside Blocked and Cached: all three name a kind of answer a query can get, and a period where every query was blocked or cached is worth seeing.
    • The client chart drops "Other" in a window where it counted nothing. That series aggregates the clients outside the top eight, so when it counts nothing there is nothing being aggregated, and a legend entry, a tooltip row and a table column that exist only to say "zero" are noise. The named clients stay even at zero, because a client that went quiet is a fact about the window.

    Artifacts

    f31e83a49ca21f6d97cc6c0223c6d9ca05f0f1b2432653a4b2f9a93ca0e78d29  nxdns-0.0.12-x86_64-linux-musl.tar.gz
    45fe8337866989b9404f5e67945dfa989d72dc207bed3f95ea3c51dfa284b32e  nxdns-0.0.12-aarch64-linux-musl.tar.gz
    4cbfc00d7ca2db57a1b78587de185c2e77148eba70a2b0b4eefc97d52e53ab39  IMAGE-DIGEST.txt
    
    git.mial.net/mokhtar/nxdns:0.0.12@sha256:a9a60ba0e64701d0252d433a794dc5a035e3122f07e9fab5b1e2e6a77924f33c
    

    Compare v0.0.8...v0.0.12

    Commits since v0.0.8
    6170571 build: bump version to 0.0.12
    c847072 overview: one endpoint, live projections and a response cache (m36)
    7cdb74e build: bump version to 0.0.11
    4367003 changelog: 0.0.11
    728b8d6 admin: draw the overview charts with visx
    091962a build: bump version to 0.0.10
    4fcbd0a changelog: 0.0.10
    d961b15 db-mode config changes apply live in-process
    17e6e93 admin: only the content region scrolls on wide screens
    8a44d90 cut: schema gate — refuse to release an undisclosed querylog schema change
    03149b5 ci: drop the one-insn-per-tb workaround, the qemu panic is an 11.1.0-only regression
    a972c8d logger tests: join every writer future before its database closes, fix the gate-open race
    78d688a ci: run the aarch64 suite with one qemu insn per tb, tcg optimization falsely trips the ubsan pointer check
    fe5bd96 admin: live ring capacity is injectable, eviction test no longer timing-bound
    1f215ac build: bump version to 0.0.9
    2f86cd7 changelog: 0.0.9 releases today
    51cda8b milestone 33: contract closure — samples, file-authority enumeration, dead code, bundle ceiling
    e5f23d5 querylog: return to the default checkpoint cadence
    24521ab milestone 32: task-shaped configuration, file mode as a rendering, config status api
    c99a37d milestone 31: concurrent upstream exchanges, dot session reuse, queue metrics
    623667e milestone 30: overview as a dashboard, explicit health contract, period aggregations
    0e83477 agents: move opus-coder to the global scope
    fa323c7 milestone 29: activity — history, live and policy simulation on one surface
    0fd6bbd milestone 28: query provenance — every logged query is exactly explainable
    7e6cb50 release cut: bump-kind justfile recipe and a compiled, tested cut tool
    8a17e9e admin: fix dashboard phantom scroll, drop last-failure column from upstream table
    0107df5 spec: measurement window is the deployment side's call
    
    Downloads