No description
  • Scheme 77.5%
  • Shell 13.6%
  • Rust 5.9%
  • Makefile 1.8%
  • Python 0.8%
  • Other 0.4%
Find a file
2026-07-30 22:19:17 -06:00
.builds ci: set umask 022 in package/verify tasks too (jerbuild creates group-writable cache dirs during pkg operations) 2026-07-22 13:10:55 -06:00
.claude chore: add AGENTS.md, .claude lock, and 30-min sandbox soak benchmark results 2026-05-18 11:48:33 -06:00
.jerboa Security hardening and release readiness 2026-06-23 10:49:23 -06:00
bench Security hardening and release readiness 2026-06-23 10:49:23 -06:00
bin Security hardening and release readiness 2026-06-23 10:49:23 -06:00
docs Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
fuzz Security hardening and release readiness 2026-06-23 10:49:23 -06:00
lib/jerboa-dns Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
scripts Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
static Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
support Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
tests Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
wasm Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
zones Support relative TXT owners in sexp zones 2026-05-20 19:30:21 -06:00
.gitignore jpkg: complete binary package setup 2026-07-30 15:07:56 -06:00
.gitsafe.json Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
.jerbuild Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
.jerbuild-jdns-data Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
AGENTS.md docs: remove jerboa-emacs restriction 2026-07-30 15:46:20 -06:00
build.ss Security hardening and release readiness 2026-06-23 10:49:23 -06:00
jpkg.lock jpkg: complete binary package setup 2026-07-30 15:07:56 -06:00
jpkg.policy.sexp jpkg: complete binary package setup 2026-07-30 15:07:56 -06:00
jpkg.sexp jpkg: complete binary package setup 2026-07-30 15:07:56 -06:00
LICENSE Switch to MIT license 2026-07-21 13:41:55 -06:00
Makefile Enable FreeBSD fiber TCP builds 2026-07-30 22:19:17 -06:00
README.md Embed WASM parsers and document deployment 2026-07-30 20:59:01 -06:00
SECURITY.md Harden DNS cache, privileges, and native loading 2026-07-11 17:22:04 -06:00
vs-djbdns.md Use fibers for TCP DNS handling 2026-05-20 20:01:16 -06:00
vs.md Fix FreeBSD build and add chroot + Capsicum security hardening 2026-03-28 15:22:15 -04:00

jerboa-dns

Authoritative-only DNS server and zone compiler in Jerboa/Chez Scheme. jdns serves both UDP and TCP DNS on the configured port.

Build and Test

make build
make test

Release evidence:

make release-evidence

The release bundle records DNS soak/load status under dist/release-evidence/soak/. By default this is a blocked, record-only status; set JDNS_RUN_RELEASE_SOAK=1 with production-duration JDNS_SOAK_SECONDS to capture current UDP dnsperf evidence plus bounded DNS-over-TCP smoke. Bounded local coverage-guided fuzz evidence is recorded with:

JDNS_RUN_COVERAGE_FUZZ=1 JDNS_FUZZ_RUNS=2048 make fuzz-evidence

When JDNS_RUN_COVERAGE_FUZZ=1 is set for make release-evidence, the release bundle also records coverage_fuzz_status=local-smoke-recorded under dist/release-evidence/fuzz-evidence/ after dns_query, cdb_record, and cdb_file complete. Authoritative-only, amplification, and edge rate-limit policy is documented in docs/rate-limit-amplification.md and copied into dist/release-evidence/abuse-policy.txt.

Static release targets live behind the static-* Make targets, for example:

make static-freebsd

Runtime Security Defaults

jdns binds the UDP and TCP sockets first, then chroots to ROOT (default .), drops supplementary groups with setgroups(0, NULL), applies setgid/setuid, verifies the resulting identities, and then applies platform filesystem restrictions. A root launch requires both a non-zero UID and non-zero GID. Root retention is denied unless the development-only JDNS_RETAIN_PRIVILEGES=1 / --retain-privileges override is explicit; that override is audited in the log and is not a production configuration. See docs/runtime-hardening.md.

TCP handling uses Jerboa fibers over nonblocking sockets when the fiber runtime is available before chroot. Embedded static builds can fall back to bounded nonblocking OS-thread tasks instead of failing at startup. TCP clients are capped at 128 concurrent sessions, and idle TCP reads/writes time out after 15 seconds.

The daemon is authoritative-only and does not recurse or forward queries. Amplification-prone ANY queries are minimized with a small REFUSED response. Production deployments must still provide edge rate limits until native response-rate limiting exists.

Filesystem sandbox setup fails closed by default. For local development only, you can allow a weaker fallback with either:

JDNS_ALLOW_SANDBOX_FALLBACK=1 jdns ...
jdns --allow-sandbox-fallback ...

The WASM DNS parser and WASM CDB reader are embedded into the binary at build time (lib/jerboa-dns/wasm-embedded.ss, regenerated by wasm/build.sh), so the sandbox is active with no files on disk, including inside the chroot. If the wasmi runtime itself is not linked in, startup/query handling fails closed by default. The only in-process parser fallback is the explicit development opt-out JDNS_ALLOW_IN_PROCESS_PARSER=1 or JDNS_ALLOW_IN_PROCESS_CDB=1.

JDNS_PARSER_WASM / JDNS_CDB_WASM point at on-disk modules as an explicit development override (useful for testing a rebuilt parser without relinking); they are not needed in production.

Zone Updates

jdns-data writes a temporary CDB and atomically renames it into place. The server validates one immutable in-memory snapshot at startup. A low-frequency watcher detects device/inode/time/size changes, builds and validates a candidate off the request path, and atomically swaps it; in-flight queries finish against their old snapshot. Queries never reopen the CDB. The default one-second poll can be changed with JDNS_CDB_RELOAD_MS (10060000 ms), and malformed updates leave the last valid snapshot serving.

Zone Formats

jdns-data accepts the original tinydns data format and a grouped s-expression format. The s-expression form starts with (zone ...), so the compiler auto-detects it:

(zone
  (domain "example.com"
    (soa "ns1.example.com" "1.2.3.4" 259200)
    (ns "ns2.example.com" "1.2.3.5" 259200)
    (a "1.2.3.4" 86400)
    (mx "mail.example.com" 10 "1.2.3.6" 86400)
    (txt "v=spf1 include:example.net mx -all" 86400)
    (txt "_dmarc" "v=DMARC1; p=none" 86400))
  (domain "www.example.com"
    (a+ptr "1.2.3.4" 86400))
  (domain "static.example.com"
    (cname "www.example.com" 3600))
  (domain "ipv6.example.com"
    (aaaa "2001:db8::1" 86400)))

TXT records can optionally start with a relative owner name. For example, (txt "_dmarc" "v=DMARC1; p=none") inside example.com compiles as a TXT record for _dmarc.example.com.

Compile either format the same way:

make jdns-data ARGS="zones/example.sexp data.cdb"