-
Jarl v0.1.0 Stable
released this
2026-10-09 09:40:56 +01:00 | 127 commits to main since this releaseThe first version.
Added
-
Daily database backups: the
jarl-backup.timersystemd timer (turned on
by the Debian package) runsjarld backup, which writes
jarl-<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 new
backup:section of jarl.yaml; the schedule is the timer's. The dashboard
andjarl statusshow the newest backup, and warn when it is older than
backup.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 as
jarl.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
thejarlCLI on stderr whenever it uses such a token (responses carry a
Jarl-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 NAMEor
JARL_SERVER, set a default withjarl config use, and group them
(jarl config group set dns ns1 ns2). A command aimed at a group (or
all) 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 via
socket. 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. Installing
jarlcreates 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 and
POST /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.--save
writes 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=https
header. 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'sexec
DNS 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 isartwork/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 existingloggingand
statistics-channelsstatements) and switches response logging with
rndc 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), in
jarl 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 separatekeys.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 -z
andnamed-checkzonein a staging directory, atomic install,rndc reconfig
and 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, legacy
master/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.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-