20 Commits
Author SHA1 Message Date
adamandClaude Opus 5.5 01ec37b9b2 Explore the statistics: a query log to drill down by client, name, view and more; storage on the dashboard
jarld keeps every answer for a while (stats.retention.queries, default 7 days) in the statistics database: time, client, view, name, type, result and what blocked it. Every client, name, view, record type, result and blocklist on the Statistics page leads to an explore page for it: answers over time, the top values of every other dimension (each a link that narrows further) and the latest answers. GET /stats/explore.

The dashboard shows the size of Jarl's databases (and the query log's rows) and, per disk Jarl uses, how full it is and the databases' share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:15:36 +01:00
adamandClaude Opus 5.5 5fcf4082c9 Web UI: Server and account menus keep the top bar on one line
Server options, ACLs, TSIG keys, Exposure, Certificates, Encrypted DNS and the audit log are in a Server menu, Account, Users and Log out under the user's name; the right-hand group stays together when it wraps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:06:18 +01:00
adamandClaude Opus 5.5 66dcf53684 DNS over TLS and HTTPS set up in one step
jarl encrypted-dns setup (and the Encrypted DNS page) finds or obtains a
certificate for the host name, checks that nothing else listens on the
DoT and DoH ports at the chosen addresses (offering a free DoH port), and
stages named's tls statement and listeners as one change, keeping port 53
where it was when there was no listen-on, plus the host name's records in
the private views (local to the view in a linked zone). Status shows what
to give Android, Linux, Firefox, Chrome and Windows clients. A tls
statement with missing files is refused (tls_file_missing), and a
certificate named uses can't be deleted. docs/encrypted-dns.md, e2e with
real DoT and DoH queries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:09:14 +01:00
adamandClaude Opus 5.5 88f63ffb0a TLS certificates: Jarl obtains and renews them over ACME
Jarl is now an ACME client (lego's core, no DNS providers). It answers
DNS-01 challenges in a zone it serves, or through Porkbun's DNS API while
Porkbun serves the domain; "auto" decides from the domain's public name
servers at every renewal, and a CNAME on _acme-challenge is followed.
Certificates are used for jarld's HTTPS (served without a restart; each
port speaks HTTPS and plain HTTP, plain HTTP from loopback and trusted
proxies still served) and/or written for named's DNS over TLS/HTTPS, with
rndc reconfig on renewal. Renewal follows the CA's ARI window or two
thirds of the lifetime, with back-off. Certificates you renew yourself
can be used too. Certificates page and dashboard card, jarl cert,
/api/v1/certificates, Prometheus gauges, docs/certificates.md, and an
e2e section against Pebble.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:35:53 +01:00
adamandClaude Opus 5.5 bdd7acfd3b Internet exposure checks and response rate limiting
Jarl works out which views are reachable from the internet (match-clients
ending in a catch-all, not private-looking addresses: an internal view of a
global IPv6 prefix is still yours; a server without views: open allow-query
and a non-private listen address), with a per-view override (schema 9), and
checks public ones: rate limiting, open resolver, minimal-any, cache
snooping, open NOTIFY and transfers, version and host name, management
interfaces. Fixes are staged like any change, including rate limiting from
ISC's example in log-only mode with the secondaries exempt, then enforcing.
named logs what rate limiting limits (or would, in log-only mode, where its
counters don't move) as query-errors; Jarl routes those lines to its log
and counts them (rrl breakdown, ratelimited top lists, Prometheus
jarl_ratelimit_logged_total), shown on the new Exposure page.

A change that newly opens a public view up must be confirmed (accept_risk,
--accept-risk, a checkbox on the Changes page; 409 confirm_risk
otherwise). Findings can be acknowledged with a reason and stay visible as
info. GET /exposure, POST /exposure/fix|ack|unack, jarl exposure,
docs/exposure.md.

Also fixes query statistics skipping TSIG-signed queries ("IP#port/key
NAME" in named's log).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:38:56 +01:00
adamandClaude Opus 5.5 c696d02513 Give each view its own page, and add a CNAME from an A record
Views open a page like a zone's, with Settings, Options and Zones tabs
(and New zone in that view); the Views page lists and orders them. A
and AAAA records get a + CNAME button that fills in the add form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:12:56 +01:00
adamandClaude Opus 5.5 02c393a6bb Apply pending changes from the banner
An Apply now button on the pending-changes banner applies the draft and
stays on the page; if the changes are refused, a dialog says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:56:10 +00:00
adamandClaude Opus 5.5 9fc23310bd Add blocking: blocklists and block/allow rules with BIND response policy zones
jarld downloads blocklists (Adblock, hosts or plain domains), checks them
daily and reloads named only when a list changed; your own block and
allow rules take precedence, and the most specific rule wins. Lists and
rules apply per view (default: every view that offers recursion). Web UI
Blocking page with name checks, jarl blocklist and jarl block, the
/blocking API, blocked counts in the statistics. Database schema 6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:24:19 +00:00
abrown38andClaude Opus 5.5 fb1ee09250 Report development builds as such, not as the release version
Only a release build (the commit tagged v<VERSION>, unchanged) gives a
version number. Others print 'dev <commit> (3 commits after v0.2.0-rc.1)',
from git describe (the Makefile's DESCRIBE), in jarld version,
jarl --version, jarl status, jarl whoami and the startup log; a build
without git says '(source build)'. The CLI's User-Agent and the footer's
tooltip follow suit, and /status's build gains since and ahead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:28:13 +00:00
abrown38andClaude Opus 5.5 ca0f148b78 Merge branch 'view-ui'
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:17:49 +00:00
abrown38andClaude Opus 5.5 4b79abf296 Replace breadcrumbs with back links; keep the zone header on tab switches
The zone page's breadcrumb (Zones › view) linked the view to a filtered
zone list while the view link below it opened the view. It is now a
single "← Zones" link back to the list with its filters, and a changeset
has "← History". View links carry where they came from, so the views page
offers "← example.org (external)" or "← Zones" (only the UI's own #/
addresses are accepted).

Switching a zone's tabs no longer blanks the page: the header and tabs
show at once and the previous tab stays, faded, until the next has
loaded. The router shows "Loading…" only for pages that take a while.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:07:55 +00:00
abrown38andClaude Opus 5.5 db9ebb14dd Show the build in a footer, linked to its release or commit
The version moves from the header to a "Powered by Jarl" footer. A build
of a release tag (HEAD tagged v<VERSION>, no tracked changes; the release
workflow sets RELEASE_TAG) shows and links the release; any other build
shows and links its commit, marked -modified with uncommitted changes.
jarld version, jarl --version, /status and /auth/whoami report the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:48:24 +00:00
abrown38andClaude Opus 5.5 b05711b13e Link views from the zone list, add breadcrumbs and view colours
The view shown in the zone list and on the zone page now links to that
view. The zone page and changeset page get breadcrumbs, and the zone list
keeps its filters in the address so going back restores them. Views can
be given a colour (Views > Edit, `jarl view color`, PUT /views/{id}/color)
so zones of the same view are easy to spot; the colour is UI-only, kept
outside changesets, and needs no deploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:43:57 +00:00
abrown38andClaude Opus 5.5 8f9cdd0a17 Add two-factor login (TOTP) with recovery codes
Users can set up an authenticator app from their account page; login
then asks for a code after the password, with one-time recovery codes
as a fallback. Sensitive actions (tokens, user changes, 2FA resets)
need a code from the last 5 minutes. totp.require (none/admins/all)
forces setup, and admins can reset a user's 2FA from the UI, the jarl
CLI or jarld on the server. The dashboard and jarl status flag users
missing 2FA and a shared "admin" account still in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:12:24 +00:00
abrown38andClaude Opus 5.5 1afed741bf Use the JARL wordmark in the header and as the README banner
The header now shows the login page's wordmark letters (without the rule)
instead of "Jarl" set in Cinzel, so the bundled Cinzel font, its preload
and its licence stanza in the .deb's copyright file are removed: the web
UI needs no web font.

The README opens with the login page's banner, the logo above the
wordmark (artwork/banner.svg). scripts/banner.py builds it from the web
UI's logo.js and brand.js, with light and dark colors, and logo.py runs
it after tracing the logo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:09:41 +00:00
abrown38andClaude Opus 5.5 0f04dbb4b1 Fix the logo's pop-in with a traced vector logo
The logo was an 880 KB SVG wrapping a 1254px PNG, downloaded again on
every page load. Keep that PNG as the master (artwork/logo.png) and have
scripts/logo.py trace it with potrace into a 6 KB logo.svg (favicon,
README) and logo.js, which brand.js draws inline in the header and on the
login page so the logo arrives with the page. Remove the unused
logo-white.svg.

Static files now carry content ETags, so reloads get 304s instead of
downloading the UI again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 18:01:22 +00:00
abrown38andClaude Opus 5.5 785b0006f9 Multi-server CLI, more reverse zones, and a fix for new view directories
- CLI: name several servers and groups in the config file; select them
  with -s/--server NAME|GROUP|all|URL or JARL_SERVER. A command aimed at
  several servers is dry-run on all of them first and only runs if every
  one accepts it. Named servers always use their own token.
- Reverse zones: /32 and /128 get single-address zones; networks between
  octet or nibble boundaries (e.g. a /22) are covered by several zones
  created together, from the CLI and the web UI.
- Fix: under jarl.service's RestrictSUIDSGID, creating a view directory
  failed on chmod with the setgid bit; fall back to the permission bits.
- Web UI: the review/live switch is a small header button, and the
  pending-changes bar only shows when there are pending changes.
- TSIG key wording; the keys page points to restricted API tokens.
- Docs: several servers (cli.md), a secondary server (operations.md),
  tokens vs TSIG keys (permissions.md); CHANGELOG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:12:03 +00:00
abrown38andClaude Opus 5.5 a7d389be27 Web UI: Jarl logo and wordmark
Add the logo, a Viking jarl drawn like a woodcut whose spear-pointed crown
holds a DNS tree. It is generated by scripts/logo.py, which mirrors the
left half so the figure is exactly symmetrical, as logo.svg (white on a
Nordic-blue disc) and logo-white.svg. It links home in the header, sits on
the login page and is the favicon.

The login page shows an angular "JARL" wordmark drawn as SVG paths over a
rule with a "DNS" node, coloured from the theme. The header's "Jarl" uses
a self-hosted Cinzel Bold subset (SIL OFL); jarld serves .woff2 as
font/woff2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 06:19:51 +00:00
abrown38andClaude Opus 5.5 3b7bee67f8 Add optional query statistics
Off by default (stats in jarl.yaml). When enabled, Jarl adds a loopback
statistics channel and a response log to named's configuration (merged
into existing logging/statistics-channels statements) and switches
response logging with rndc responselog, because the responselog option
only counts when named starts. jarld polls named's counters every minute
and tails the response log (resuming after restarts and across
rotation). It stores aggregates, never individual queries, in a separate
SQLite database: per-minute, hourly and daily counters (queries, result
codes, record types, views, zones, cache hits, upstream latency, DNSSEC)
and hourly/daily top lists (names, clients, NXDOMAIN and SERVFAIL names,
refused clients), each with its own retention. Clients can be anonymized
or excluded.

- Web UI: Statistics page with KPI tiles, queries and failed answers over
  time, a weekday x hour heatmap, top lists and breakdowns; SVG charts
  with hover/keyboard readouts, a table view and light/dark palettes
  validated for colour-vision deficiency.
- CLI: jarl stats status|summary|top|timeline|heatmap|breakdown.
- API: /api/v1/stats/*, behind a new "stats" permission resource.
- jarld brings named's configuration up to date with jarl.yaml at
  startup (engine.SyncConfig), never overwriting hand-edited files.
- resync now refuses to overwrite drifted files without force, as
  documented.
- tmpfiles.d creates /var/log/named (allowed by named's AppArmor profile
  on Debian 13 and Ubuntu).
- The e2e harness runs named with -f like the Debian unit.
- docs/stats.md, plus API, CLI, configuration, permissions and README
  updates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:21:34 +00:00
abrown38andClaude Opus 5.5 bd6b8778e4 Jarl 0.1.0: manage BIND through a web UI, REST API and CLI
Jarl keeps a BIND 9.20 server's whole configuration (views, ACLs, TSIG
keys, zones of every type, records, named.conf options) in SQLite and
renders named.conf, keys.conf and zone files from it.

Every change goes through one changeset pipeline: whole-configuration
validation, named-checkconf -z and named-checkzone in a staging
directory, atomic install, rndc reconfig/reload, serial verification
with rndc zonestatus, and rollback if named does not load the change.
Changes apply one at a time by default or are bundled and applied
together. Applied changesets can be reverted.

- jarld: the daemon with the REST API and an embedded web UI, plus import,
  adopt, check, resync, backup and local user recovery commands
- jarl: the CLI, including ACME DNS-01 hooks for certbot and acme.sh
- users with argon2id passwords and CSRF-protected sessions; hashed API
  tokens with granular grants and roles; an audit log
- systemd unit, sysusers.d/tmpfiles.d files, Makefile
- unit tests, integration tests against a real named, scripts/e2e.sh
- docs/ guides, README and CHANGELOG

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:03:13 +00:00