Files
nxdns/AGENTS.md
T
mokhtar c50c6d285a
CI / test (push) Successful in 1m22s
CI / test-aarch64 (push) Successful in 4m55s
CI / frontend (push) Successful in 39s
CI / cross (push) Successful in 7m57s
CI / docker (push) Failing after 1h10m42s
milestone 17: real deadlines, validator holes, upstream editor, trusted proxies, contract samples, badvers
2026-08-07 17:55:59 +02:00

4.0 KiB

AGENTS.md

Aim

nxdns: a self-hosted DNS sinkhole for a household LAN, written in Zig 0.16.0. Portfolio-grade public repo. PLAN.md is the source of truth for scope and design; specs/ holds per-milestone contracts; specs/research/ holds verified stdlib facts.

Values

We intentionally architect this code to be robust, maintainable, pragmatic — good craftsmanship and good engineering. We explicitly avoid tech debt, code smells, bad architecture decisions, and brittle implementations.

What that means in practice:

  • This is a greenfield project. Breaking changes are allowed. Never keep a bad interface for compatibility; fix it at the root.
  • No versioning of scope. A feature is in scope (build it completely) or out of scope (do not build it). No "v2 later", no stubs left behind.
  • Fix root causes, not symptoms. Do not iterate on workarounds.
  • Scope is small on purpose: household scale, two targets, few dependencies. Do not add generality nobody asked for.
  • Dependencies are liabilities: stdlib first; vendored + pinned C deps (sqlite3, mbedTLS) only where the stdlib has nothing.
  • Verify stdlib claims against ../zig at tag 0.16.0 — pre-0.16 knowledge is stale (std.Io migration). See specs/research/zig-0.16-api-notes.md.
  • Pure core: dns/, filter/, local/, cache/ take bytes and return bytes — no Io, no sockets, no clocks hidden inside.
  • Every failure mode must be visible: no silent drops, no unbounded logs, no swallowed errors. Counters + health surfaces over log spam.
  • Tests are runnable acceptance criteria, not decoration. Required CI stays deterministic — no network-dependent tests in blocking jobs.
  • Comments state constraints the code cannot show. No narration, no commented-out code.
  • Git: GPG-signed commits (git commit -S), simple lowercase messages, no generated-by footers.

Reading zig build test output

A fully passing zig build test still prints a line like failed command: .../test --cache-dir=... --seed=... --listen=-, and still exits 0. That line is a known upstream zig 0.16.0 labelling defect. It does not mean a test failed, and no test binary crashed.

The build runner sets a step's result_failed_command on every spawn (std/Build/Step/Run.zig:1540) and never clears it on success. It then prints a step's diagnostics whenever the step wrote anything to stderr, explicitly "no matter the result" (compiler/build_runner.zig:1381), and that printer emits the failed command: label unconditionally when the field is set (compiler/build_runner.zig:1515). Our suite writes to stderr on every run, because the tests that cover the warning paths log through the real sink. A minimal reproducer with no mbedTLS and no C — one passing test whose body is a std.debug.print — prints the same label and reports "3/3 steps succeeded; 1/1 tests passed"; deleting the print removes the label. No upstream issue matched a search, so the reference is the 0.16.0 source lines above.

Any other failure text is real. Trust the summary line: zig build test exiting non-zero, a N failed count, or a panic backtrace all mean a genuine failure. Do not filter, wrap, or suppress the runner's output to hide the label — that would hide real failures with it.

One trap: running a cached test binary by hand with --listen=- aborts with internal test runner failure: EndOfStream. That is not a teardown bug; the IPC runner is talking to a closed stdin because no build runner is on the other end. Run the binary with no arguments to get the plain stdio report.

Regenerating the contract samples

web/src/lib/contractSamples.gen.ts is a committed golden of canonicalized API responses, byte-compared against the live server by a -Dintegration test and type-checked by tsc. After a deliberate API contract change, regenerate it with:

zig build test -Dintegration -Dcontract-samples-out="$PWD/web/src/lib/contractSamples.gen.ts"

then update web/src/lib/types.ts to match and commit both. Never edit the generated file by hand.