Options of a block (server options, a view, a zone) are listed and
written grouped by topic, alphabetical within a group, so the same
option is in the same place everywhere. Top-level statements keep the
order they were added in.
named.conf diffs add the enclosing blocks to each hunk header, like
git's function context: @@ -50,6 +49,8 @@ view "internal" {.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 KiB
Changelog
All notable changes to Jarl are documented here. The format follows Keep a Changelog, and versions follow Semantic Versioning.
[Unreleased]
Added
- Disabled zones: a zone can be disabled instead of deleted. named stops
loading it (its zone statement is left out of named.conf), while Jarl
keeps it with its records, options and history, still editable;
enabling it serves it as it is then. Disabling says what clients get
instead (recursion, or REFUSED). Signed zones can't be disabled. Web
UI: Disable/Enable on the zone page, a badge in the zone list. CLI:
jarl zone disable|enable. API:PATCH /zones/{zone}with{"disabled": true}. Database schema 7. - Blocking (docs/blocking.md): named answers "doesn't exist" for ads,
trackers and malware, using BIND's response policy zones.
- Blocklists in Adblock, hosts or plain-domain format, downloaded by jarld
and checked daily (
blocking.refresh). A list is downloaded only when the server says it changed, checked with named-checkzone, and reloaded only when its content differs; a failed update keeps the previous version and shows on the dashboard. - Your own block and allow rules, for a domain and the names below it or the exact name. Rules take precedence over every list, and the most specific rule wins.
- Lists and rules apply to chosen views, or to every view that offers recursion. They are configuration: staged, reviewed and reverted like any change.
- Checking a name says whether it is blocked and by which rule or list entry, with an Allow button in the web UI.
- Web UI: a Blocking page, with well-known lists to pick and named's
memory per list. CLI:
jarl blocklistandjarl block. API:/blocking/lists,/blocking/rules,/blocking/check. - Pausing:
jarl block pause [DURATION]or the Blocking page lets every name through for a while (at most a day); blocking resumes by itself. The lists stay loaded, so it takes effect at once. - Statistics: blocked queries over time (a line on the queries chart)
and their share, blocked per list, the most blocked names and the
clients with the most blocked queries (
jarl stats top blocked,top blocked-clients). Blocked answers no longer count as names that don't exist. - The view choice in the web UI's forms is a compact dropdown, however many views there are.
- A
blockingpermission; database schema 6.
- Blocklists in Adblock, hosts or plain-domain format, downloaded by jarld
and checked daily (
Changed
- Options are kept in one fixed order, in named.conf, the web UI and the CLI: grouped by topic (files, listening, access control, recursion and forwarding, DNSSEC, transfers and notify, then the rest), alphabetical within a group. The same option is now in the same place in every view and zone. The first apply after upgrading reorders named.conf (a diff that only moves lines). Top-level statements keep their order.
- Diffs of named.conf say which block each change is in, after the
@@line numbers (@@ -50,6 +49,8 @@ view "internal" {), like git does with function names. Moving an option from the server's options into a view no longer looks like two unrelated edits. - Web UI options (server, top-level, view and zone): values with blocks, such as logging, are shown and edited laid out as in named.conf, over several lines with indentation. The editor grows with its content, Tab indents and Enter keeps the indentation (Esc, then Tab, moves on). With a draft open, the tables show the draft's values, so a staged edit stays visible after Save.
- The pending-changes banner has an Apply now button: it applies your draft without going to the Changes page, and stays on the page you're on. If the changes are refused, a dialog says why and links to Changes.
- Blocking page: blocklists and rules have an Edit button (name, URL,
views, comment; a rule's action and scope). The list button that
downloads now is called Refresh, like
jarl blocklist refresh. - Records table: disabled records are hatched and have a "disabled" badge next to their name, instead of only a muted colour and a badge in the comment column.
Fixed
- Statistics: after a reconfig that added, removed or recreated views (creating the first view, for instance), a minute could count everything since named started: millions of queries in an hour. A per-view counter (cache, upstream times, validation) going down was taken for a restart of named. Now only a new boot time means a restart, and a counter that goes down counts 0 for that minute.
- Blocking page, checking a name: when your own block rule is for that very name, it offers to remove the rule instead of an allow rule, which was refused as a second rule for the same name. It also names the rule that matched rather than the policy zone entry.
- docs/validation.md lacked the
disabled_conflictwarning added in 0.3.0.
[0.3.0] - 2026-10-09
Added
-
Disabled records: a record can be disabled instead of deleted. It stays in Jarl with its ID, TTL and comment but is left out of the zone (the zone file lists it as a comment), and enabling it puts it back exactly as it was. Web UI: a Disable/Enable button on each record, disabled records greyed out with a badge, a filter for them, and a count in the zone list. CLI:
jarl record disable|enable(by ID or by name, type and data,--ptrfor the PTR record,--missing-ok),record list --disabled, anddisable:/enable:entries forjarl apply -f. API:POST /records/{id}/disable|enable,POST /zones/{zone}/records/disable|enable, adisabledfield on records,?disabled=on record lists andrecord.disable|enableoperations. Validation checks the zone as it is served: the last NS record can't be disabled, enabling a record is checked like adding it, and a disabled record that a CNAME now blocks gets a warning. Database schema 5. -
DNSSEC for a zone whose parent zone is in Jarl: the DNSSEC tab gives the
jarl record add PARENT NAME DS ...command to copy and an "Add to PARENT" button (staged like any change), links to the parent zone, and says when the parent has the DS record.jarl zone dnssec statusprints the command too, and turning signing off gives the matchingjarl record delete. -
CLI output for scripts:
-o idprints just the IDs of what a command shows or lists (jarl zone show adbraun.lol -o id),-o field=view,soa.serialjust those fields, tab separated.-qonshowandlistprints the IDs too. With a change (jarl record add ... -o id) they apply to the changed object. -
Statistics: client lists (busiest and refused clients, in the web UI,
jarl stats top clients|refusedandjarl stats summary) show the host name Jarl's records have for each address: the PTR record's target, or else the name of an A/AAAA record. Looked up in Jarl's own data, so no DNS queries; an address without a record shows as before. -
Zones list: a padlock after each signed zone's name, linking to its DNSSEC tab (open and amber while signing is being turned off).
Changed
- The web UI and API can listen on ports below 1024, such as 443:
jarl.servicenow gives jarld theCAP_NET_BIND_SERVICEcapability (and only that). With a unit that doesn't, jarld's error says what's missing. See docs/configuration.md, "HTTPS on port 443". - Records table: names are no longer greyed out when they repeat the row above (it made those records look inactive). Records of the same name are separated by a faint line instead, and a full line marks where the next name starts.
Fixed
jarl record list: one long value (a DKIM key) padded every row to its length, so on a terminal each row wrapped onto a second, blank-looking line. Long data is now shortened to fit the terminal;--wideshows it in full, as does output to a pipe or file.
[0.2.0] - 2026-10-09
Added
- DNSSEC signing per zone (docs/dnssec.md):
jarl zone dnssec enable|status|check-ds|disable|wipeand the zone page's DNSSEC tab, or the zone's newdnssec_policyfield in the API. named signs the zone with BIND'sdefaultpolicy (one ECDSA P-256 key that is never replaced) or a custom one, using inline signing, with keys in a per-view directory under/var/lib/bind/jarl/dnssec-keys. Jarl shows the keys and the DS record to give the registrar, says when it's safe to publish it, and checks the parent's DS records (check-ds, which also tells named withrndc dnssec -checkds). It refuses steps that would break a signed domain: switching straight to unsigned (it goes through BIND'sinsecurepolicy instead, and finishes only once named is done), deleting a signed zone or moving it to another view, and TTLs above the policy'smax-zone-ttl.wipestarts over with a clean slate once the parent has no DS. Each step in the web UI opens a dialog that says what it needs first.jarl zone delegationnow also compares the parent's DS records with the zone's keys. - Porkbun support (optional, docs/dnssec.md#porkbun): with an API key in
the new
porkbun:section of jarl.yaml (and nowhere else),jarl zone registrar status|sync-glue|set-ns|publish-ds|remove-dsand the zone page's Registrar tab show and change the domain's name servers, glue records (one per name server inside the domain, with all its IPv4 and IPv6 addresses) and DS records at Porkbun. Every change first checks that it won't take the domain offline (the name servers answer for the zone, glue matches, a DS goes out only once the domain is delegated to servers that serve the signed zone, DS records match the zone's keys before the name servers change);--dry-runshows the checks and--forceskips them. Administrators only. - Backups include the DNSSEC keys (private keys too), stored inside the
backup file, and
jarld restoreputs them back for named (bind.named_user, defaultbind).jarl-backup.servicehasCAP_DAC_READ_SEARCHto read them;--without-keysskips them. - Jarl raises a zone's serial above the one named has loaded whenever it writes a zone file whose serial isn't higher (after restoring an older database, say), so named loads it (it refuses otherwise for a signed zone) and secondaries pick it up.
- Two-factor login to the web UI with an authenticator app (TOTP: SHA-1,
6 digits, 30 s, as every app supports). Users turn it on under Account
(password, QR code, a code to confirm) and get 10 one-time recovery
codes. Logging in then asks for a code (or a recovery code) after the
password; each code works once. In a session with two-factor login,
creating or rotating API tokens, changing users and resetting someone's
two-factor login need a code from the last 5 minutes: the web UI asks
for one, API clients send
X-Jarl-TOTP. API tokens and the local socket are not affected. totp.requirein jarl.yaml (none,admins,all) makes two-factor login compulsory; such a user who hasn't set it up can only set it up after logging in.totp.issuernames the server in authenticator apps (defaultJarl <hostname>).- Resetting a user's two-factor login (lost phone): Users → Reset 2FA,
jarl user totp reset NAME, or on the serverjarld user totp reset NAME(also with jarld stopped). - The dashboard and
jarl statuslist users who must use two-factor login but haven't set it up, and suggest disabling the firstadminaccount when it is still active next to personal administrator accounts.jarl user list/showand the Users page show who uses it. - API:
POST /auth/login/totp,/me/totp(setup,enable,disable,recovery-codes),DELETE /users/{id}/totp; database migration 4. scripts/testenv.sh: turns a fresh Debian/Ubuntu machine into a test bed. It installs BIND and the build tools (Go from go.dev), writes a hand-made style configuration with internal and external views, a split-horizon zone (example.comorJARL_TEST_ZONE) and a reverse zone, starts named and prints the steps to install Jarl and adopt the configuration (docs/development.md).- View colours. Give a view a colour (Views → Edit,
jarl view color, orPUT /views/{id}/color) and the zone list, the zone page and the views list show its name in that colour. The colours are muted, and pastel in dark mode. The default is the link colour. A colour isn't configuration, so setting one needs no changeset or deploy. - A "← Zones" link above a zone, back to the zone list, and "← History" above a changeset. The zone list keeps its filter, view and type in the address, so going back to it (with that link or the browser's back button) restores them. A view opened from a zone or the zone list has a "← example.com (external)" or "← Zones" link back.
Changed
- The version moved from the header to a footer: "Powered by Jarl v0.1.0",
linking to the release on Gitea. A build that isn't of a release tag
shows its commit instead, linked to the commit and marked
-modifiedwith uncommitted changes. Before, every build of a commit after a release still showed the release's version.GET /statusand/auth/whoamireport the build asbuild(docs/development.md). jarld version,jarl --version,jarl statusandjarl whoamigive a version number only for release builds. Other builds say they are development builds and how far past the last tag they are:dev ca0f148b78b6 (3 commits after v0.2.0-rc.1). Before, they printed the version and the commit (0.2.0-rc.1 (ca0f148b78b6)), which read like a release.make debgives development builds their own package version after the last tag (0.2.0~rc.1+git3.gca0f148, plus the build time with uncommitted changes). Installing one over the RC or an older build is an upgrade: no moreapt install --reinstall. It also removes older packages fromdist/(docs/development.md).- The view in the zone list, and on the zone page, links to that view, and the views page highlights it. It used to be plain text, and the zone page linked to the views page as a whole.
- The zone's Name servers tab shows at once and follows the check as it
happens. Each query is listed as a dig command line (
dig @192.0.2.1 example.org. SOA +norec) with a "waiting 1.8 s" counter until it is answered. The finished result then replaces the progress, and the queries stay under "Queries sent". Results, the delegation check's included, are kept for 10 minutes, so returning to the tab shows them at once; "Check again" asks anew. A check keeps running if you leave the tab. Behind it,GET /zones/{zone}/serials?stream=1streams the queries as newline-delimited JSON (docs/api.md). - The delegation check streams its queries the same way, every one of
them: finding the parent zone, its servers' addresses, the NS and DS
queries to each parent server and the SOA from each delegated server.
Each shows a short answer ("referral to katelyn.ns.cloudflare.com., …",
"DS 2371", "NXDOMAIN"). The serials check now lists its address lookups
as separate A and AAAA queries.
GET /zones/{zone}/delegation?stream=1streams them. - Switching a zone's tabs (Records, Settings, …) no longer blanks the page while the tab loads: the page changes once the tab is ready, and "Loading…" appears only if a page takes a while.
jarl zone serialscompares the serial named serves, which for a signed zone is the signed zone's (ahead of Jarl's).jarld importturnsdnssec-policyin an existing configuration into the zones' DNSSEC setting, and warns that their keys must be moved into Jarl's key directory before adopting.dnssec-policyis no longer accepted as a zone, view or server option (zones have a DNSSEC setting instead), norkey-directoryas a zone option.- Deleting a zone also removes the journal and signed files named kept next to it.
- Release candidates (tags like
v0.2.0-rc.1) publish their.debs to a separatetestingdistribution of the package registry instead ofstable. Servers that followstablecan't pick up an RC withapt upgrade. An RC may also take its release notes from the## [0.2.0]or## [Unreleased]section of the CHANGELOG instead of needing its own (docs/development.md, "Release candidates").
Fixed
- Publishing a release candidate failed:
scripts/gitea-release.shrejected its.debs, whose names carry the Debian version (0.2.0~rc.1) instead of0.2.0-rc.1. jarld init -uand the install guide recommend naming the first administrator after yourself (one account per person).jarld importno longer warns that a zone withallow-update { none; }uses dynamic updates (validation already allowed it).- docs/cli.md gives the CLI configuration file's location on macOS
(
~/Library/Application Support/jarl/config.yaml) and Windows (%AppData%\jarl\config.yaml). It was documented only as~/.config/jarl/config.yaml.
[0.1.0] - 2026-10-09
The first version.
Added
-
Daily database backups: the
jarl-backup.timersystemd timer (turned on by the Debian package) runsjarld backup, which writesjarl-<UTC time>.dbintobackup.dir(/var/backups/jarl), checks it with SQLite's integrity check before giving it that name, and deletes backups older thanbackup.retention(30d) while always keeping the newestbackup.keep_at_least(7). Ages come from the names, and only files named like backups are ever deleted. Settings are in the newbackup:section of jarl.yaml; the schedule is the timer's. The dashboard andjarl statusshow the newest backup, and warn when it is older thanbackup.warn_after(2d) or the last attempt failed.backup.statsalso backs up the statistics database. -
jarld restore [FILE | latest]: lists backups, or checks one, shows what it holds next to the current database, and after confirmation replaces the database with it, keeping the current one asjarl.db.before-restore-<time>. It refuses while jarld runs:jarld servenow holds a lock (jarl.db.lock), which also stops two jarlds from using the same database. docs/operations.md explains restoring. -
Warnings before API tokens expire, from 14 days ahead: on the dashboard, in token lists ("soon", "expired"), once a day in jarld's log, and from the
jarlCLI on stderr whenever it uses such a token (responses carry aJarl-Token-Expiresheader).jarl token list --expiring [WITHIN]lists them. -
jarl zone serials ZONEand the zone page's Name servers tab: ask the zone's other name servers which serial they serve (a primary zone's NS records and also-notify targets, a secondary zone's primaries, with their ports and TSIG keys) and compare it with this server's. Exits with 2 when one is behind or doesn't answer. -
jarl zone delegation ZONEand a button on the Name servers tab: for zones on the internet, checks that the parent zone delegates the zone to the name servers in its NS records, with matching glue, and that each of them serves it. An undelegated (internal-only) zone gets one calm note. Neither check runs unless asked. -
jarld sends
Server: Jarl/<version>. -
CLI exit code 2: a check ran and found problems.
-
CLI: several servers in one configuration file. Name them (
jarl config server add ns1 URL), pick one with-s/--server NAMEorJARL_SERVER, set a default withjarl config use, and group them (jarl config group set dns ns1 ns2). A command aimed at a group (orall) is first checked on every server as a dry run and only runs if it succeeds everywhere, so a change one server rejects changes nothing anywhere. Named servers always use their own token. -
Reverse zones for any network: a /32 (or /128) gets a zone that answers for just that address, and a network between octet (or nibble) boundaries, such as a /22, is covered by several zones (four /24s) that are created together, from the CLI (
jarl zone create 10.0.8.0/22) and the web UI. Networks smaller than a /24 explain that they belong in the enclosing /24's zone. -
docs/operations.md: setting up a secondary server with its own jarld.
-
A local unix socket,
/run/jarl/jarl.sock(socketin jarl.yaml). Requests on it from root or jarld's own user need no token and act as an administrator (the kernel'sSO_PEERCREDsays who is connecting); anyone else must send a token. ThejarlCLI uses it when no server is configured, or with-s local, sosudo jarl statusworks on the server with nothing set up, and losing every token or password never locks root out. Changes are audited under the system user's name viasocket. The systemd unit creates/run/jarl(0750). -
Jarl is released under the MIT License (
LICENSE), which is installed with the docs and is the Debian package'scopyrightfile. -
Debian packages:
make debbuilds, with onlydpkg-deb(no root) and for the host or any other architecture (DEB_ARCH=arm64),jarl(the server: jarld, its units and docs) andjarl-cli(thejarlCLI, its completion and the ACME hooks), whichjarldepends on.jarl-clihas no scripts, so it can go on any machine you manage Jarl from. Installingjarlcreates thejarluser and directories and an example/etc/jarl/jarl.yaml; upgrades restart a running jarld and never touch/etc/jarl/jarl.yaml; removing it stops jarld, and even a purge keeps Jarl's database and the files named uses. -
Releases from Gitea Actions: pushing a
v<version>tag runs.gitea/workflows/release.yml, which tests, builds and publishes a Gitea release (the CHANGELOG section as notes; the tarballs, the CLI for Linux, macOS and Windows, the.debs andSHA256SUMS), and uploads the.debs for amd64 and arm64 to the Gitea Debian registry, so servers install and upgrade Jarl with apt.make cli-distbuilds the standalone CLI files;scripts/gitea-release.shpublishes. docs/development.md has the setup (a runner and aRELEASE_TOKENsecret), docs/install.md the apt source. -
Bash completion for
jarlandjarld, installed bymake installand the Debian package. Besides commands and flags, it completes server and group names from the CLI configuration, zone, view, ACL, TSIG key and user names from the server, and record types. -
make disttarballs include the completion scripts (completion/). -
Token rotation:
jarl token rotate, the Rotate button in the web UI andPOST /api/v1/tokens/{id}/rotategive a token a new secret while it keeps its ID, name, user and grants. The old secret stops at once or after a grace period of up to 7 days (--grace), during which jarld logs who still uses it. A token that expires gets its lifetime again.--savewrites the new secret where the CLI got the old one (its configuration or--token @FILE),--write FILEto any file, and--currentpicks the token the CLI runs with, so a restricted token can renew itself.jarl token listshows when a token was last rotated. -
An endpoint for lego's
httpreqDNS provider (/api/v1/acme/httpreq/presentand/cleanup), so lego and Traefik can publish DNS-01 challenges with only environment variables, without the jarl CLI. lego's username and API token arrive as HTTP Basic auth, which is accepted on these routes only. Off by default (acme.httpreq.enabled), and HTTPS-only by default: jarld's own TLS, or a trusted proxy that says so withX-Forwarded-ProtoorForwarded. Plain HTTP can be allowed from loopback only (allow_loopback_http) or everywhere (require_https: false).GET /api/v1/acme/httpreqshows how jarld sees a request, to check a proxy setup.jarl acmeand the endpoint share their code (internal/acme). -
Trusted proxies may also send the standard
Forwarded: proto=httpsheader. When a proxy header lists several values, the last one (added by the nearest proxy) counts. -
lego support:
contrib/lego/jarl-lego.shis a program for lego'sexecDNS provider that publishes DNS-01 challenges throughjarl acme, including challenge names reached through a CNAME (docs/acme.md). -
docs/concepts.md: what each zone type does, what to enter as a new zone's name servers (and when glue addresses are needed), how reverse zones map to networks, and why dynamic updates give way to API tokens.
-
A logo: a bearded Viking jarl in blue and white, whose crown holds a DNS tree (a root node and its branches). The master is
artwork/logo.png;scripts/logo.pytraces it into vector paths (assets/logo.svg, 6 KB, andassets/logo.js). It is shown on the login page, links home in the top left of the web UI, and is the favicon. The UI draws it inline, so it appears together with the page. -
The login page shows an angular, Norse-style "JARL" wordmark drawn as SVG paths (
assets/brand.js) above a rule with a "DNS" node; it follows the light and dark themes. The header beside the logo shows the same letters, without the rule, so the UI needs no web font. -
The README opens with the login page's banner, the logo above the wordmark (
artwork/banner.svg, built byscripts/banner.pyfrom the web UI's own logo and wordmark, with light and dark colors). -
Web UI: wherever an address match list is entered (server, view and zone options such as allow-query or allow-transfer, view match lists, ACL elements) the existing ACLs, TSIG keys and built-in names are offered as one-click suggestions, and names that don't exist are flagged as you type.
-
Query statistics (off by default;
statsin jarl.yaml, docs/stats.md). When enabled, Jarl adds a loopback statistics channel and a response log to named's configuration (merged into existingloggingandstatistics-channelsstatements) and switches response logging withrndc responselog. jarld aggregates named's counters and the log into a separate SQLite database: per-minute, hourly and daily counters (queries, result codes, record types, views, zones, cache hits, upstream latency, DNSSEC) and hourly and daily top lists (names, clients, NXDOMAIN and SERVFAIL names, refused clients), each with its own retention. Clients can be anonymized or excluded. It is shown on a new Statistics page in the web UI (KPI tiles, queries and failed answers over time, a weekday x hour heatmap, top lists and breakdowns, each chart with a table view), injarl stats status|summary|top|timeline|heatmap|breakdown, and in the API under/api/v1/stats/, behind the newstatspermission. Works with the bind9 packages of Debian 13 and Ubuntu 26.04. -
At startup jarld brings named's configuration up to date with jarl.yaml settings that affect it (such as statistics), through the usual checked pipeline. Files changed by hand are never overwritten.
-
jarld daemon: REST API (
/api/v1) and embedded web UI, with SQLite as the source of truth for the whole BIND configuration. Runs under systemd (Type=notify, sandboxed, unprivilegedjarluser in groupbind). SIGHUP and certificate file changes reload TLS. -
Configuration model: views (ordered, match-clients/destinations, recursive-only), ACLs (nested, negation, keys, reference tracking with rename and delete protection), TSIG keys (kept in a separate
keys.conf, never in diffs), zones of every type (primary, secondary, stub, static-stub, forward, mirror, hint, redirect, in-view), reverse zones from a CIDR, records of every type, options at server, view, zone and top-level scope, and defaults for new zones. -
BIND 9.20 option catalog: all 353 statements with grammar, allowed scopes, value kinds, deprecation status and ARM descriptions, used for validation and by the UI's option editor.
-
Changesets: immediate one-operation changes by default; staging into named changesets (
?changeset=,X-Jarl-Changeset,--changeset), batches (POST /apply,jarl apply -f), dry runs, previews with diffs, and revert of applied changesets. Optimistic locking with object versions. -
Apply pipeline: whole-configuration validation, rendering,
named-checkconf -zandnamed-checkzonein a staging directory, atomic install,rndc reconfigand per-zone reload, verification withrndc zonestatus, and automatic rollback when named doesn't load the change. Automatic serials (date, increment or unixtime policy, RFC 1982), bumped only when DNS data changes. Drift detection for files changed outside Jarl. -
Validation: CNAME/DNAME rules, NS/MX/SRV targets, glue and delegations, RRset TTL consistency, BIND 9.20 RRset/type limits, SPF/DMARC sanity, ACL/key references and cycles, option syntax and scope, and security lints (open resolver, open zone transfers, dynamic updates on managed zones).
-
Records: TXT entered as plain text (automatic quoting and 255-byte splitting), relative and absolute names with detection of doubled origins, PTR records kept in step with A/AAAA (
ptr), RRset replace, delete by match, zone file import (merge or replace). -
Import and adoption:
jarld importreads an existing named.conf tree and zone files (comments,$INCLUDE,$GENERATE, legacymaster/slavespellings);jarld adoptswitches named to Jarl's configuration with a backup and automatic fallback. -
Authentication: web logins with argon2id passwords, sessions (HttpOnly, SameSite=Strict) and CSRF tokens, rate limiting per user and IP; hashed API tokens (
jarl_...) with optional expiry. -
Permissions: granular grants (resource × read/write, filtered by zone, view, record name pattern and type), roles (
readonly,dns-editor,operator,acme), tokens restricted to a subset of their user's rights, administrators. -
Audit log of logins, changes, users, tokens and secret reveals, with user, token, IP and outcome, searchable and written to the journal.
-
Web UI: dashboard, zones and records with inline editing and pending markers, views, ACLs, TSIG keys, the option editor, review and apply, history with revert, audit log, users, account and tokens, search. Review mode by default, with an "apply immediately" switch.
-
jarl CLI: zones, records, views, ACLs, TSIG keys, options, settings, changesets, batches, history, users, tokens, audit, search, status, check and resync. Table, JSON and YAML output, and documented exit codes.
-
ACME DNS-01:
jarl acme set|unset, certbot hooks (jarl acme certbot-auth|certbot-cleanup), an acme.sh DNS API hook (contrib/acme.sh/dns_jarl.sh), automatic zone and view discovery, idempotent retries, and--follow-cname/--resolverfor a dedicated ACME zone. -
Operations:
jarld backup(online),jarld resync,jarld check,jarld render, local user recovery commands, audit retention. -
Packaging: Makefile (build, test, e2e, install, dist), systemd unit, sysusers.d and tmpfiles.d files, example configuration (
jarld config --example). -
Tests: unit tests for every package, integration tests against the real named 9.20 (checkers, reload and rollback, import and adopt), and an end-to-end script (
scripts/e2e.sh) that drives every CLI command against a running named. -
Documentation: concepts, install and adoption, configuration, CLI, API, permissions, ACME, validation, operations and development guides, and a split-DNS guide for adding views to a server that has none.
Changed
-
Web UI: the review/live switch is a small "Review edits" / "Live edits" button in the header, and the bar below it only appears while there are pending changes.
-
TSIG keys: the name help no longer calls
xfer-keya domain name (any letters, digits, hyphens and dots will do; both servers must use the same name), and the keys page points to restricted API tokens for giving scripts or people limited access to zones and records. -
Web UI, new zone dialog: choose Domain or Reverse zone. For a reverse zone you give the network (10.10.0.0/24, 2001:db8:1::/48) and the zone name is generated. Either way the dialog shows the exact zone name it will create and explains problems before submitting: an address or URL instead of a name, a host-like name such as www.example.com, a zone that already exists in the view, a child or parent of an existing zone, and networks that don't follow the octet or nibble boundaries.
-
Web UI, record form: after adding a record every field is cleared except the type, and the cursor returns to the name. A "Will add:" line shows the record exactly as it will be stored, with warnings for unqualified names (e.g. a CNAME to readthedocs.io without the final dot). In reverse zones the form asks for the address, defaults to PTR, offers the host names of A/AAAA records with that address, warns about a second PTR for an address, and the record list shows each record's address.
-
Reverse zones: a record name can be given as an address (10.10.0.20) in the API and CLI too, and a PTR target without a final dot that contains a dot (nas.adbraun.lol) is read as the full host name (with a warning); a bare label is refused.
-
jarl resync/jarld resyncrefuse to overwrite files that were changed outside Jarl unless--forceis given, as documented. Before, they always overwrote them.
Fixed
-
The logo popped in after the page: it was an 880 KB SVG wrapping a 1254-pixel PNG, downloaded in full on every page load. It is now traced into 6 KB of vector paths and drawn inline. Static files also get an ETag, so reloads revalidate them with 304 answers instead of downloading them again.
-
jarl token create --savestored the token at the top level of the CLI configuration even when a named server was selected; it now goes into that server's entry. -
The web UI showed future times, such as token expiry dates, as "just now"; they now read "in 29 days".
-
The dashboard's "Pending changes (1 open)" counted empty changesets (and other users'), so the Changes page then showed none. It now counts your own changesets that contain changes, and the status API's
open_changesetsonly counts non-empty ones. In review mode, a draft created for an edit that the server rejects is discarded instead of being left open and empty. -
Under the shipped systemd unit (
RestrictSUIDSGID=yes), the first zone in a new view failed with "chmod /var/lib/bind/jarl/VIEW: operation not permitted": setting the setgid bit on the new directory is refused there. Jarl now falls back to the permission bits, so the directory is group-writable for named, and only changes the mode when it needs to. -
Web UI: the "Jarl 0.1.0" brand link no longer underlines the gap between the name and the version on hover.
-
Web UI: creating a zone from the dashboard's "New zone" link no longer opens the dialog a second time.
-
Warnings noticed while parsing record data (a name read as absolute) were lost before reaching the result; they are shown again.
-
The negative-caching warning now uses the effective negative TTL, which is the smaller of the SOA minimum and the zone's default TTL (RFC 2308). It explains the real effect (new names can stay invisible to resolvers that already asked) and gives the command that fixes it. Before, it fired on the SOA minimum alone and wrongly blamed BIND's resolver cap.
-
Problems printed by
jarld checkshow zone names without the trailing dot. -
jarlnow shows the warnings a change introduces when it is applied or staged, not only on dry runs. -
jarl checkandjarl resyncno longer print named-checkconf's routine "loaded serial" lines, only its errors and warnings. -
jarl zone listshowed only primary zones:zone create's default--type primaryleaked into the list filter through a shared variable. Hint, secondary, forward and other zones are listed again. -
named-checkconf and named-checkzone output is classified by the tools' exit code. Their warnings (e.g.
both "recursion no;" and "allow-recursion" active) were shown as errors. They are now shown as warnings in the CLI,jarld checkand the web UI. -
The
zone_outside_viewerror says how to fix it (--adopt-zones). -
The end-to-end test starts named with
-flike the Debian unit; with-g, named sends all logging to stderr.