Dual-dialect store (SQLite via modernc.org, MySQL via go-sql-driver,
both pure Go) with order-tolerant start/complete upserts, filtered and
paginated listing, and aggregate queries (per-day, top domains, query
types, statuses, duration percentiles, top clients). Sender gains
optional EXPLOREDNS_WEBHOOK_TOKEN bearer auth; a round-trip test pins
receiver structs byte-compatible with the sender payloads.
Note: go directive moves to 1.25.0, required by modernc.org/sqlite.
CI reads the version from go.mod so GOTOOLCHAIN=local stays satisfied.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dns.squish.net-style traversal detail tree (refid-parented via longest
prefix, collapsible .0 resolve subtrees, completed-earlier markers, raw
log fallback), a servers card with Leaflet/OSM map lazy-loaded from CDN
and client-side geojs.io geolocation with graceful degradation, and a
delegation-tree favicon (ico + svg).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- per-client-IP token bucket on POST /api/traverse (EXPLOREDNS_RATE_LIMIT,
default 30/1h; direct localhost exempt, proxied clients are not)
- optional usage webhooks (EXPLOREDNS_WEBHOOK_URL): start/complete JSON
events, fire-and-forget with 5s timeout + one retry so a dead receiver
never delays a job
- post-traversal version.bind fingerprinting exposed at
GET /api/traverse/{id}/servers (pending until ready) and announced via
an SSE "servers" event; never delays job completion
- /api/health reports the serving Fly region (FLY_REGION) for observing
anycast routing from a roaming client
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
--version/-V on the CLI, version in /api/health via Server.SetVersion,
Makefile/Dockerfile ldflags stamping from git describe, and a release
workflow on v* tags: both binaries for linux/darwin (amd64+arm64) and
windows/amd64 with SHA256SUMS attached to the Gitea release
(idempotent — reuses an existing hand-written release and skips
already-uploaded assets), plus version-tagged Docker images.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- hard per-traversal deadline (EXPLOREDNS_JOB_TIMEOUT, default 5m) so
every job reaches a terminal state; timed-out jobs report error with
any partial results instead of masquerading as complete
- cap concurrent traversals (EXPLOREDNS_MAX_JOBS, default 8) returning
429 when saturated
- CORS off by default (the embedded SPA is same-origin); opt in via
EXPLOREDNS_CORS_ORIGIN
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port the traversal engine to the Ruby dnstraverse model so behaviour and
output match dns.squish.net:
- dns: single RD=0 query path (RD=1 only for upstream root discovery),
per-run packet cache, EDNS0 512-fallback with warnings, UDP->TCP on
truncation; fix --retries 0 and --root-server IP-literal handling;
drop all hardcoded 127.0.0.1:53 resolvers
- traverse: hierarchical per-branch InfoCache, 7-step response
classification with the full 10-status vocabulary, bailiwick
partitioning, strictly-deeper lame-referral rule, refid grammar with
.0 resolve subtrees and childset digits, per-IP branching at 1/n
weight, cache-based glue resolution with noglue/loop dead ends, CNAME
restarts from the deepest cached zone, fast-mode memoization,
probability aggregation with Ruby-identical stats keys (sums to 1.0)
- output: byte-for-byte reference text format pinned by a golden test,
reference CLI defaults, working --quiet/--show-X=false, TTY-aware
colour, deduplicated deterministic JSON
- web: adapt API/SPA to the new engine, SSE events carry refid/status,
fix subscribe/snapshot duplicate-event race and a statusCls TDZ bug,
align SPA type list with the backend
- delete the old engine and dead code (net -4,350 lines)
Verified against live runs of the reference Ruby engine across five
domains (answers, NXDOMAIN, null MX, CNAME restart, glueless resolve)
with no divergences beyond the documented typo fixes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>