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>
837 lines
50 KiB
Markdown
837 lines
50 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to Jarl are documented here. The format follows
|
||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
||
[Semantic Versioning](https://semver.org/).
|
||
|
||
## [Unreleased]
|
||
|
||
### Upgrading from 0.4
|
||
|
||
- New exposure warnings (docs/exposure.md) appear for views reachable from
|
||
the internet, e.g. "no rate-limit" or "version visible" on an external
|
||
view: fix them from the Exposure page, or acknowledge them. A change
|
||
that newly opens a public view up (an open resolver, say) now needs
|
||
`--accept-risk`, or the checkbox on the Changes page.
|
||
|
||
- Once you use address ranges, the database can't go back to 0.4: its
|
||
parser doesn't know them. Remove the ranges first if you need to
|
||
downgrade.
|
||
|
||
### Fixed
|
||
|
||
- Option values in the web UI looked different from the editor: a long
|
||
line wrapped back to the left edge, losing its indentation, and the
|
||
column changed width when Edit opened. Values now keep their layout
|
||
(a long line scrolls), with the same width and padding as the editor.
|
||
The "{ } around the list are optional" hint only shows for options that
|
||
end in a list (not rate-limit, which only has one inside).
|
||
- Query statistics skipped every query signed with a TSIG key (named
|
||
writes "/key NAME" after the client's port), such as queries with a
|
||
view's key: they are counted now.
|
||
- A changeset with a staged record delete could not be applied after that
|
||
record was deleted elsewhere (e.g. with the CLI), and it was hard to see
|
||
which operation to remove: the error only said "record 832 does not
|
||
exist". A staged delete of a record that is already gone now counts as
|
||
done (with a warning), and the other operations that no longer apply
|
||
(an edit or disable of a deleted record, a change to an object changed
|
||
since) are named with their number and summary. The Changes page marks
|
||
that operation and offers to remove it right in the error.
|
||
|
||
### Added
|
||
|
||
- DNS over TLS and HTTPS ([docs/encrypted-dns.md](docs/encrypted-dns.md)):
|
||
`jarl encrypted-dns setup --hostname NAME --address ADDR` (and the
|
||
Encrypted DNS page) obtains a certificate for the host name if needed,
|
||
checks that nothing else listens on port 853 and the DoH port (443 by
|
||
default) at those addresses, offering a free port, and stages named's
|
||
`tls` statement and listeners as one change, keeping port 53 where it
|
||
was, plus the host name's records in your private views (local to the
|
||
view in a linked zone). The page and `jarl encrypted-dns` say what to
|
||
give Android, Linux, Firefox, Chrome and Windows. A `tls` statement whose
|
||
files are missing is refused (`tls_file_missing`), and a certificate
|
||
named uses can't be deleted.
|
||
- Linked zones ([docs/views.md](docs/views.md#linked-zones)): zones of the
|
||
same name in different views can be linked, and Jarl keeps their shared
|
||
records in step: a change in one view reaches the others in the same
|
||
changeset (diff, permissions, history and undo cover every view). Each
|
||
view can still have records of its own (`--local`, "Only here"), which
|
||
override the shared records of that name and type there, and keeps its
|
||
own zone options. Linking existing zones changes no answer: what is the
|
||
same everywhere becomes shared, the rest stays local, and a view's
|
||
version can be shared later (`--shared`, "Share"). `jarl zone link` and
|
||
`unlink`, `zone create --link-with VIEW`, `record add|set|update
|
||
--local|--shared`, a LINK column in `record list`, "Keep them linked"
|
||
when copying a zone in the web UI, `POST /zones/{zone}/link` and
|
||
`/unlink`. ACME challenges are written once per link, and `--view all`
|
||
changes a link once.
|
||
- TLS certificates ([docs/certificates.md](docs/certificates.md)): Jarl
|
||
obtains certificates from Let's Encrypt or any ACME CA (account set up
|
||
in jarl.yaml under `acme.account`) and renews them itself, from the CA's
|
||
renewal information (ARI) or two thirds into their lifetime, with
|
||
back-off after failures. It answers the 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,
|
||
so moving the domain to your own name servers needs no change. A CNAME
|
||
on `_acme-challenge` into a zone Jarl serves is followed, for domains
|
||
hosted elsewhere. A certificate is used for jarld's HTTPS ("web") and/or
|
||
written for named (`<bind.config_dir>/tls/NAME/`, "dns"; named rereads
|
||
it on renewal), and certificates you renew yourself can be used too
|
||
(reread when their files change). Certificates page and dashboard card
|
||
in the web UI, `jarl cert` (check, create, issue, update, delete,
|
||
account), `/api/v1/certificates`, Prometheus
|
||
`jarl_certificate_expiry_timestamp_seconds`, `jarl_certificate_failing`.
|
||
- jarld serves HTTPS and plain HTTP on the same ports once it has a
|
||
certificate (`tls.cert_file`, or one with the web use, which takes
|
||
effect without a restart): plain HTTP from the server itself and from
|
||
trusted proxies is served as before, browsers elsewhere are redirected
|
||
to HTTPS and API requests get `https_required`. Before, with
|
||
`tls.cert_file`, plain HTTP got an error even from localhost.
|
||
- Internet exposure ([docs/exposure.md](docs/exposure.md)): Jarl works out
|
||
which views are reachable from the internet (match-clients ending in a
|
||
catch-all; for a server without views, open allow-query and a
|
||
non-private listen address), with a per-view override, and checks them:
|
||
response rate limiting, open resolvers, ANY answers (minimal-any), cache
|
||
snooping, open NOTIFY and zone transfers, BIND version and host name,
|
||
management interfaces. Fixes are staged like any change, including
|
||
response rate limiting from ISC's example in log-only mode, with your
|
||
secondaries exempt, and a later "start enforcing". With statistics on,
|
||
Jarl reads named's rate limiting log lines and shows what was (or would
|
||
have been) limited: counts, client networks and names (Exposure page,
|
||
`jarl stats breakdown rrl`, top lists `ratelimited`, Prometheus
|
||
`jarl_ratelimit_logged_total`). A change that newly opens a public view
|
||
up must be confirmed (checkbox on the Changes page, `--accept-risk`, API
|
||
`accept_risk`; 409 `confirm_risk` otherwise). Findings can be
|
||
acknowledged with a reason: they stay visible as info, with who, when
|
||
and why. Exposure page and dashboard card in the web UI, `jarl
|
||
exposure` (fix, ack, unack), `GET /exposure`.
|
||
- `jarl changeset show`, `diff`, `apply`, `discard` and `remove` take the
|
||
changeset from `-C` or `JARL_CHANGESET` when no ID is given
|
||
(`jarl changeset apply`). A changeset from `JARL_CHANGESET` is now
|
||
checked before it is used, for staging too: it must exist, be open and
|
||
be yours, so a stale export (an applied changeset, or an ID that later
|
||
turned into someone else's web UI draft) can't silently take your
|
||
changes; the error says what to do. Commands say on stderr which
|
||
changeset from `JARL_CHANGESET` they use, and `apply`/`discard` remind
|
||
you to `unset JARL_CHANGESET`. `-C` and ID arguments are used as given.
|
||
- Address ranges such as `10.0.10.101-10.0.10.199` or
|
||
`2001:db8::100-2001:db8::1ff` (also `!` negated, and with spaces around
|
||
the dash) wherever an address match list is used: ACLs, views'
|
||
match-clients and match-destinations, options such as allow-query, and
|
||
blocking's client groups ([docs/concepts.md](docs/concepts.md)). Jarl
|
||
keeps the range as written and writes the fewest prefixes that cover
|
||
exactly that range to named.conf, with the range as a comment. The web
|
||
UI shows what a range becomes while you type it (`GET
|
||
/catalog/ranges`). Mistakes are refused when entered: an end that isn't
|
||
a full address, the start after the end, IPv4 mixed with IPv6, a range
|
||
in an option that takes no address match list (forwarders). A range
|
||
covered by an earlier element is reported as never matching, a range of
|
||
every address counts as "any" for the open resolver and open transfer
|
||
checks, and an IPv6 range in the IPv4-mapped space gets a warning. ACL
|
||
names can no longer look like a range.
|
||
- Prometheus metrics at `/metrics` ([docs/metrics.md](docs/metrics.md)),
|
||
set up in the new `metrics:` section of jarl.yaml (off by default).
|
||
`metrics.allow` lists the addresses that may scrape (loopback by
|
||
default), `metrics.token` lets anyone who sends it as a bearer token
|
||
scrape, and `metrics.listen` adds addresses that serve only `/metrics`
|
||
(for a Prometheus server elsewhere). Root and jarld's own user can also
|
||
scrape on the local socket.
|
||
- Health, always: named up, recursive and TCP clients against their
|
||
limits, adopted, open changesets, last apply, validation problems by
|
||
severity, drifted files, object counts, the last backup (and failure,
|
||
and whether it is stale), blocking paused, each blocklist's entries,
|
||
last check and change and whether it fails, the DNSSEC phase of every
|
||
signed zone, and when API tokens expire.
|
||
- With query statistics on: named's counters (queries, result codes,
|
||
record types, UDP/TCP, authoritative/recursive, rate limiting, upstream
|
||
round-trip times, DNSSEC validation, cache hits) and Jarl's (logged
|
||
responses per view and zone, blocked queries per blocklist), as
|
||
counters since jarld started, plus named's memory, CPU, threads, cache
|
||
and sockets, and the collector's health.
|
||
- Example queries and alert rules, checked with promtool.
|
||
|
||
## [0.4.0] - 2026-10-11
|
||
|
||
### Upgrading from 0.3
|
||
|
||
- Back up first (`sudo jarld backup`).
|
||
- When jarld first starts, it rewrites named's configuration: options
|
||
move into their new fixed order, and every view gets its view key in
|
||
`match-clients` and `keys.conf`. It is checked with named-checkconf
|
||
before named reloads, like any apply. Files you changed by hand are left
|
||
alone; the log then says to run `jarl resync`.
|
||
- The systemd unit changed (it allows `AF_NETLINK`). The package replaces
|
||
it; an override that adds `AF_NETLINK` (`systemctl edit jarl`) does no
|
||
harm and can be removed.
|
||
- The database moves to schema 8 (disabled zones, client groups) as
|
||
jarld starts.
|
||
- If statistics showed a spike after views were created or changed, see
|
||
"Statistics" under Fixed: the bad minutes can be removed from
|
||
`/var/lib/jarl/stats.db` (minute rows have `res = 'm'`; the hours and
|
||
days that include them are `'h'` and `'d'`).
|
||
|
||
### Added
|
||
|
||
- Different blocking for different devices (docs/blocking.md), like
|
||
Pi-hole's groups, in one view and without loading any list twice.
|
||
Client groups put devices (addresses, networks or ACLs of them) at a
|
||
blocking step: `off` (only your rules), `normal` (the default), or steps
|
||
you add above it, each including the lists of the steps below. Lists
|
||
start at a step. Jarl puts a small "gate" policy zone in front of each
|
||
step's lists that lets the devices below that step through. Web UI: a
|
||
Devices panel on the Blocking page and a step on each list. CLI: `jarl
|
||
block step`, `jarl block group`, `jarl blocklist update --step`. API:
|
||
`/blocking/steps`, `/blocking/groups`. Database schema 8.
|
||
- View keys: every view gets a TSIG key, `jarl-view-NAME`, at the front of
|
||
its match-clients, so a query signed with it lands in that view from any
|
||
address. Jarl signs its delegation check's queries to this server's own
|
||
addresses with the key of the zone's view: before, they got whichever
|
||
view matched the server's own address (usually internal), so the check
|
||
could report the wrong serial for the external view. `jarl view key
|
||
VIEW` shows a key, for `dig -k` (docs/views.md). The first apply after
|
||
upgrading adds the keys to named.conf and keys.conf.
|
||
- Server statistics: named's memory (resident, and its own count) and
|
||
CPU use, its cache size, sockets, and recursive and TCP clients against
|
||
their limits; the host's memory and load; free space where zone files,
|
||
logs and Jarl's data are. Memory and CPU are kept over time like the
|
||
other statistics. Statistics page → Server, `jarl stats server`, API
|
||
`/stats/server`. Read from named's statistics channel, `rndc status` and
|
||
/proc, with no extra privileges.
|
||
- CLI: the record commands that change records take several views,
|
||
`--view internal,external`, or `--view all`, and make the change in each,
|
||
as one changeset (all views or none).
|
||
- Blocking can be paused until resumed, like Pi-hole's "indefinitely":
|
||
`jarl block pause indefinitely`, **Until I resume** on the Blocking page,
|
||
`{"indefinitely": true}` in the API.
|
||
- 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 blocklist` and `jarl 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 `blocking` permission; database schema 6.
|
||
|
||
### Changed
|
||
|
||
- Web UI: each view has its own page, like a zone: Settings (match lists,
|
||
comment, colour), Options and Zones tabs (with **New zone in VIEW**),
|
||
and Delete. The Views page lists them in order, with the arrows that
|
||
reorder them; view links everywhere open the view's page.
|
||
- Web UI: A and AAAA records have a **+ CNAME** button that fills in the
|
||
add form with a CNAME pointing to the record's name, leaving only the
|
||
alias's name to type. It shows when the row is hovered or focused
|
||
(always on touch screens).
|
||
- Blocking page: how many queries each blocklist blocked in the last 24
|
||
hours, and your rules together, with statistics on (linking to the
|
||
Statistics page).
|
||
- 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.
|
||
- Options that end in a list (`allow-transfer`, `forwarders`,
|
||
`listen-on`, ...) can be given without their braces, in the web UI, CLI
|
||
and API: `10.0.10.53; key xfer` becomes `{ 10.0.10.53; key xfer; }`.
|
||
Before, only some server options (those edited one element per line)
|
||
added them.
|
||
- The dashboard's configuration checks include named-checkconf's own
|
||
warnings about the running configuration (such as `both "recursion no;"
|
||
and "allow-recursion" active`), which before only showed while applying.
|
||
The CLI now prints them after an apply too.
|
||
- Statistics page: cards and tiles fill their rows instead of leaving one
|
||
alone beside an empty space, and the queries and failed answers charts
|
||
have the same margins, so their time axes line up.
|
||
- Dashboard: options have their own tile; "0 TSIG keys · 22 options" read
|
||
as if the options belonged to the keys.
|
||
- Web UI times use the 24-hour clock.
|
||
- 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
|
||
|
||
- Reverting a changeset added needless "restore" operations for
|
||
blocklists, rules and views that hadn't changed: copying the state
|
||
turned their empty view lists into missing ones.
|
||
- Under the shipped systemd unit, jarld couldn't list the host's
|
||
addresses (it needs a netlink socket, which `RestrictAddressFamilies`
|
||
didn't allow), so the name server checks didn't recognize this server's
|
||
own addresses. The Name servers tab then queried them like another
|
||
server's, and the delegation check didn't sign those queries with the
|
||
view's key: with views, both got the view matching the server's own
|
||
address (usually internal) and reported a wrong serial. The unit now
|
||
allows `AF_NETLINK`, and without it jarld reads the addresses from
|
||
/proc.
|
||
- 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_conflict` warning 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, `--ptr` for the PTR record, `--missing-ok`), `record list
|
||
--disabled`, and `disable:`/`enable:` entries for `jarl apply -f`. API:
|
||
`POST /records/{id}/disable|enable`, `POST
|
||
/zones/{zone}/records/disable|enable`, a `disabled` field on records,
|
||
`?disabled=` on record lists and `record.disable|enable` operations.
|
||
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 status` prints
|
||
the command too, and turning signing off gives the matching `jarl record
|
||
delete`.
|
||
|
||
- CLI output for scripts: `-o id` prints just the IDs of what a command
|
||
shows or lists (`jarl zone show adbraun.lol -o id`), `-o
|
||
field=view,soa.serial` just those fields, tab separated. `-q` on `show`
|
||
and `list` prints 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|refused` and `jarl 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.service` now gives jarld the `CAP_NET_BIND_SERVICE` capability
|
||
(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; `--wide` shows 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|wipe` and the zone page's DNSSEC tab, or
|
||
the zone's new `dnssec_policy` field in the API. named signs the zone
|
||
with BIND's `default` policy (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 with
|
||
`rndc dnssec -checkds`). It refuses steps that would break a signed
|
||
domain: switching straight to unsigned (it goes through BIND's
|
||
`insecure` policy instead, and finishes only once named is done),
|
||
deleting a signed zone or moving it to another view, and TTLs above the
|
||
policy's `max-zone-ttl`. `wipe` starts 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 delegation` now 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-ds` and 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-run` shows the checks and `--force` skips
|
||
them. Administrators only.
|
||
- Backups include the DNSSEC keys (private keys too), stored inside the
|
||
backup file, and `jarld restore` puts them back for named
|
||
(`bind.named_user`, default `bind`). `jarl-backup.service` has
|
||
`CAP_DAC_READ_SEARCH` to read them; `--without-keys` skips 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.require` in 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.issuer` names the server in authenticator apps
|
||
(default `Jarl <hostname>`).
|
||
- Resetting a user's two-factor login (lost phone): Users → Reset 2FA,
|
||
`jarl user totp reset NAME`, or on the server
|
||
`jarld user totp reset NAME` (also with jarld stopped).
|
||
- The dashboard and `jarl status` list users who must use two-factor login
|
||
but haven't set it up, and suggest disabling the first `admin` account
|
||
when it is still active next to personal administrator accounts.
|
||
`jarl user list`/`show` and 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.com` or `JARL_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`, or
|
||
`PUT /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 `-modified`
|
||
with uncommitted changes. Before, every build of a commit after a
|
||
release still showed the release's version. `GET /status` and
|
||
`/auth/whoami` report the build as `build` (docs/development.md).
|
||
- `jarld version`, `jarl --version`, `jarl status` and `jarl whoami` give
|
||
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 deb` gives 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 more `apt install --reinstall`. It also removes older
|
||
packages from `dist/` (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=1` streams 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=1`
|
||
streams 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 serials` compares the serial named serves, which for a signed
|
||
zone is the signed zone's (ahead of Jarl's).
|
||
- `jarld import` turns `dnssec-policy` in an existing configuration into
|
||
the zones' DNSSEC setting, and warns that their keys must be moved into
|
||
Jarl's key directory before adopting.
|
||
- `dnssec-policy` is no longer accepted as a zone, view or server option
|
||
(zones have a DNSSEC setting instead), nor `key-directory` as 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 `.deb`s to a
|
||
separate `testing` distribution of the package registry instead of
|
||
`stable`. Servers that follow `stable` can't pick up an RC with
|
||
`apt 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.sh`
|
||
rejected its `.deb`s, whose names carry the Debian version
|
||
(`0.2.0~rc.1`) instead of `0.2.0-rc.1`.
|
||
- `jarld init -u` and the install guide recommend naming the first
|
||
administrator after yourself (one account per person).
|
||
- `jarld import` no longer warns that a zone with `allow-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.timer` systemd timer (turned on
|
||
by the Debian package) runs `jarld backup`, which writes
|
||
`jarl-<UTC time>.db` into `backup.dir` (`/var/backups/jarl`), checks it
|
||
with SQLite's integrity check before giving it that name, and deletes
|
||
backups older than `backup.retention` (30d) while always keeping the
|
||
newest `backup.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
|
||
and `jarl status` show the newest backup, and warn when it is older than
|
||
`backup.warn_after` (2d) or the last attempt failed. `backup.stats` also
|
||
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
|
||
serve` now 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 `jarl` CLI on stderr whenever it uses such a token (responses carry a
|
||
`Jarl-Token-Expires` header). `jarl token list --expiring [WITHIN]` lists
|
||
them.
|
||
- `jarl zone serials ZONE` and 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 ZONE` and 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 NAME` or
|
||
`JARL_SERVER`, set a default with `jarl 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` (`socket` in jarl.yaml).
|
||
Requests on it from root or jarld's own user need no token and act as
|
||
an administrator (the kernel's `SO_PEERCRED` says who is connecting);
|
||
anyone else must send a token. The `jarl` CLI uses it when no server is
|
||
configured, or with `-s local`, so `sudo jarl status` works 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's `copyright` file.
|
||
- Debian packages: `make deb` builds, with only `dpkg-deb` (no root) and
|
||
for the host or any other architecture (`DEB_ARCH=arm64`), `jarl` (the
|
||
server: jarld, its units and docs) and `jarl-cli` (the `jarl` CLI, its
|
||
completion and the ACME hooks), which `jarl` depends on. `jarl-cli` has no
|
||
scripts, so it can go on any machine you manage Jarl from. Installing
|
||
`jarl` creates the `jarl` user 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 `.deb`s and `SHA256SUMS`), and uploads the `.deb`s
|
||
for amd64 and arm64 to the Gitea Debian registry, so servers install and
|
||
upgrade Jarl with apt. `make cli-dist` builds the standalone CLI files;
|
||
`scripts/gitea-release.sh` publishes. docs/development.md has the setup
|
||
(a runner and a `RELEASE_TOKEN` secret), docs/install.md the apt source.
|
||
- Bash completion for `jarl` and `jarld`, installed by `make install` and
|
||
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 dist` tarballs include the completion scripts (`completion/`).
|
||
- Token rotation: `jarl token rotate`, the Rotate button in the web UI and
|
||
`POST /api/v1/tokens/{id}/rotate` give 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 FILE` to any file, and `--current` picks the
|
||
token the CLI runs with, so a restricted token can renew itself.
|
||
`jarl token list` shows when a token was last rotated.
|
||
- An endpoint for lego's `httpreq` DNS provider
|
||
(`/api/v1/acme/httpreq/present` and `/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 with `X-Forwarded-Proto` or `Forwarded`.
|
||
Plain HTTP can be allowed from loopback only (`allow_loopback_http`) or
|
||
everywhere (`require_https: false`). `GET /api/v1/acme/httpreq` shows how
|
||
jarld sees a request, to check a proxy setup. `jarl acme` and 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.sh` is a program for lego's `exec`
|
||
DNS provider that publishes DNS-01 challenges through `jarl 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.py` traces it into vector paths (`assets/logo.svg`, 6 KB,
|
||
and `assets/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 by `scripts/banner.py` from 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; `stats` in jarl.yaml, docs/stats.md).
|
||
When enabled, Jarl adds a loopback statistics channel and a response log to
|
||
named's configuration (merged into existing `logging` and
|
||
`statistics-channels` statements) 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 new `stats` permission. 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, unprivileged `jarl` user in group `bind`).
|
||
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 -z`
|
||
and `named-checkzone` in a staging directory, atomic install, `rndc reconfig`
|
||
and per-zone reload, verification with `rndc 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 import` reads an existing named.conf tree
|
||
and zone files (comments, `$INCLUDE`, `$GENERATE`, legacy
|
||
`master`/`slave` spellings); `jarld adopt` switches 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` / `--resolver` for 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-key` a 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 resync` refuse to overwrite files that were changed
|
||
outside Jarl unless `--force` is 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 --save` stored 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_changesets` only 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 check` show zone names without the trailing dot.
|
||
- `jarl` now shows the warnings a change introduces when it is applied or
|
||
staged, not only on dry runs.
|
||
- `jarl check` and `jarl resync` no longer print named-checkconf's routine
|
||
"loaded serial" lines, only its errors and warnings.
|
||
- `jarl zone list` showed only primary zones: `zone create`'s default
|
||
`--type primary` leaked 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 check` and the web UI.
|
||
- The `zone_outside_view` error says how to fix it (`--adopt-zones`).
|
||
- The end-to-end test starts named with `-f` like the Debian unit; with `-g`,
|
||
named sends all logging to stderr.
|