Files
nxdns/website/demo

Screenshot demo

A synthetic household that produces the admin screenshots in ../src/assets/screenshots/. Six devices on a docker LAN send real DNS traffic through the published nxdns image, real public blocklists decide what is blocked, and the bulk of the traffic is then spread over the previous 24 hours so the Overview shows a believable day. Nothing here is real household data, and no device carries a person's name.

Device Address Group
nxdns 192.168.50.2 —
Living room TV 192.168.50.41 default
iPhone 192.168.50.52 default
Pixel 192.168.50.53 default
Kids' phone 192.168.50.54 kids (safe search, gambling and NSFW lists)
Desktop PC 192.168.50.20 default
MacBook Air 192.168.50.21 default

The LAN is 192.168.50.0/24 because the machine that first ran this uses 192.168.1.0/24 for its real network. The admin UI is on http://127.0.0.1:18080, password nxdns-demo. Upstreams are Cloudflare DoH with Quad9 DoT as failover; Quad9's DoH endpoint requires HTTP/2, which nxdns does not speak.

Run every command from this directory. It needs docker, Node.js and Python 3.

Generate the household

NXDNS_IMAGE picks the image; it defaults to the version the committed screenshots show. Set it to the release the new screenshots are for.

NXDNS_IMAGE=git.mial.net/mokhtar/nxdns:<version> ./start.sh

Wait until /api/blocklists reports all four lists loaded. Then shrink the cache for the compressed bulk run, so the cache-hit share stays near what a real day produces, and send the bulk traffic:

curl -s -c .cookies -H 'content-type: application/json' -d '{"password":"nxdns-demo"}' http://127.0.0.1:18080/api/auth/login
curl -s -b .cookies -X PUT -H 'content-type: application/json' -d '{"cache":{"size":48}}' http://127.0.0.1:18080/api/settings
./devices.sh bulk

The bulk run sends about 18,000 queries. Wait until all six device containers have exited, then restore the cache and rewrite the history while nxdns is stopped. check_coherence.py runs the repository's projection oracle and must print no FAIL:

curl -s -b .cookies -X PUT -H 'content-type: application/json' -d '{"cache":{"size":10000}}' http://127.0.0.1:18080/api/settings
docker stop nxdemo-nxdns
./db.sh /gen/rewrite_history.py /data
./db.sh /gen/check_coherence.py /data
docker start nxdemo-nxdns
./devices.sh live

gen/check_profiles.py prints each device profile's expected blocked share through /api/lookup. Use it when you change gen/profiles.py.

Produce a Diagnostics episode

Cut the primary DoH upstream off inside nxdns's network namespace, wait about two minutes, and restore it. nxdns opens an "Upstream failing" episode and closes it on its own after the routes come back. Check that the addresses are still the ones cloudflare-dns.com resolves to.

docker run --rm --net container:nxdemo-nxdns --cap-add NET_ADMIN python:3-alpine ip route add blackhole 104.16.248.249/32
docker run --rm --net container:nxdemo-nxdns --cap-add NET_ADMIN python:3-alpine ip route add blackhole 104.16.249.249/32
docker run --rm --net container:nxdemo-nxdns --cap-add NET_ADMIN python:3-alpine ip route del blackhole 104.16.248.249/32
docker run --rm --net container:nxdemo-nxdns --cap-add NET_ADMIN python:3-alpine ip route del blackhole 104.16.249.249/32

Take the screenshots

The first time, install the pinned Playwright and its browser:

npm ci
npx playwright install chromium

Then shoot all five pages in light and dark at 1440×900, 2× scale, and build a contact sheet to review them:

node gen/shoot.mjs shots "$(./db.sh /gen/blocked_query_id.py /data)"
python3 -I gen/contact_sheet.py shots screenshots.html

Review the sheet before anything replaces the files in ../src/assets/screenshots/.

Tear down

./teardown.sh