# Jarl *A Jarl rules a Hold; this one rules your zones.* Jarl manages a BIND 9 (named) server: zones, records of every type, views, ACLs, TSIG keys and any named.conf option. It has a web UI, a REST API and a command line client. All three go through the same validated, audited, all-or-nothing change pipeline, and nothing reaches named before BIND's own checkers have accepted it. ``` $ jarl record add adbraun.lol nas A 192.168.1.20 --ptr add nas.adbraun.lol. IN A 192.168.1.20 add 20.1.168.192.in-addr.arpa. IN PTR nas.adbraun.lol. zone 1.168.192.in-addr.arpa serial 2026100700 -> 2026100701 zone adbraun.lol serial 2026100701 -> 2026100702 applied (changeset 12) $ jarl record add adbraun.lol www A 192.168.1.99 error: www.adbraun.lol has a CNAME, so it can't have any other records (found A); a CNAME must be the only record at its name (RFC 1034) [zone adbraun.lol] ``` ## Features * **Everything in one place**: primary, secondary, forward, stub, mirror, hint, redirect and in-view zones, reverse zones from a CIDR, split DNS with views, ACLs, TSIG keys, and every BIND 9.20 option at server, view, zone and top level, with the ARM's description and grammar built in. * **Safe changes.** Every change is validated as a whole (CNAME rules, glue, delegations, RRset TTLs, references, option syntax, open-resolver and open-transfer lints), checked with `named-checkconf -z`, installed atomically, reloaded, and verified with `rndc zonestatus`. If named doesn't load it, the previous files are put back. * **Changesets**: apply edits one at a time, or bundle them, review the diff, and apply them together. Every applied changeset can be reverted. Serials are bumped automatically. * **Users and API tokens with granular permissions**, e.g. a token that may only write `_acme-challenge` TXT records in one zone. Passwords use argon2id, sessions have CSRF protection, logins are rate limited. * **Audit log** of every login, change and token, with user, token, IP and outcome. * **Let's Encrypt DNS-01** built in: `jarl acme` plus certbot and acme.sh hooks, including CNAME delegation to a dedicated ACME zone. * **Adopts an existing server**: imports named.conf and zone files (comments included), then switches named over with an automatic fallback. * One static Go binary for the daemon (web UI embedded, SQLite built in) and one for the CLI, run by a sandboxed systemd service as an unprivileged user. ## Quick start On a Debian/Ubuntu BIND 9.20 server ([install.md](docs/install.md) has the details): ```sh make && sudo make install sudo systemd-sysusers jarl.conf && sudo systemd-tmpfiles --create jarl.conf sudo chgrp jarl /etc/jarl/jarl.yaml sudo -u jarl jarld init --token # first admin + a CLI token sudo -u jarl jarld import # read the current named.conf and zones sudo jarld adopt # switch named over (keeps a backup, rolls back on failure) sudo systemctl enable --now jarl jarl config set server http://127.0.0.1:8053 jarl config set token # paste the token from 'jarld init' jarl status ``` Then open the web UI at `http://127.0.0.1:8053/`, or set `listen` and `tls` in `/etc/jarl/jarl.yaml` to reach it from your management network. ## Documentation | | | |-|-| | [docs/concepts.md](docs/concepts.md) | How Jarl works: source of truth, changesets, the apply pipeline, authentication. Read this first | | [docs/install.md](docs/install.md) | Installing, importing and adopting an existing server, TLS, upgrading | | [docs/configuration.md](docs/configuration.md) | `/etc/jarl/jarl.yaml` reference | | [docs/cli.md](docs/cli.md) | The `jarl` command line client | | [docs/api.md](docs/api.md) | REST API reference | | [docs/permissions.md](docs/permissions.md) | Users, tokens, grants and roles | | [docs/acme.md](docs/acme.md) | Let's Encrypt DNS-01 with certbot and acme.sh | | [docs/validation.md](docs/validation.md) | Every check Jarl makes | | [docs/operations.md](docs/operations.md) | Backups, drift, failures, recovery, monitoring | | [docs/development.md](docs/development.md) | Code layout, tests against real BIND, conventions | | [CHANGELOG.md](CHANGELOG.md) | Changes per release | ## The web UI The UI is built for getting things done rather than for looks. It has these pages: * **Dashboard**: named status, adoption, drift, recent changes, open problems. * **Zones**: list, create (fields adapt to the zone type), per-zone records with inline editing and type-specific hints, settings (SOA, TTL, serial policy, view), options, the generated zone file, and pasting a zone file to merge or replace records. * **Views, ACLs, TSIG keys, Server options**: the options editor knows every BIND 9.20 statement for the scope and shows its description, grammar and a value field that fits it. * **Changes**: pending edits with the diff, named-checkconf output and problems, applied as one changeset. History lists applied changesets with their diffs and a revert button. * **Audit, Users, Account** (password, API tokens) and **Search**. By default, edits are collected as pending changes. Pending rows are shown inline (green = new, yellow = changed, struck out = deleted) until you review and apply them. Switch on "Apply edits immediately" to skip the review step. ## Requirements * BIND 9.20, with `named-checkconf`, `named-checkzone` and `rndc`. The option catalog follows 9.20. 9.18 will probably work, because named-checkconf still checks every change, but it is untested. * Linux with systemd for the packaged service. Defaults match Debian/Ubuntu, and other layouts are configurable. * Go 1.27+ to build. ## Status Version 0.1.0, the first release. It is feature-complete for one server's configuration. DNSSEC signing and managing several servers are not handled yet ([concepts.md](docs/concepts.md#what-jarl-deliberately-doesnt-do-yet)).