Wegweiser is a single static binary that runs an authoritative DNS server. Start it, open the web interface, and have a working zone in five minutes without learning zonefile syntax first. It is a side project, written by one person who got tired of editing zonefiles by hand. Wegweiser is German for signpost.
Versions are semantic, and the changelog carries the compatibility note for each release. Migrations run at startup and only ever go forward, so a database written by a newer build is refused rather than downgraded and the way back is a backup.
Reverse zones manage themselves. Add an A record and the matching PTR appears in the
reverse zone responsible for it, IPv6 nibble zones and RFC 2317 classless delegation
included. Delete the record and the PTR goes with it. A conflict is shown, never silently
overwritten.
Every change is reversible. Each edit is a journal event, so zone history, a diff of any change, an audit trail and rollback to an earlier state all come out of one mechanism.
Two clients, one API. A dense, keyboard-driven web interface with a command palette and a live query stream, and a CLI that reaches everything the interface does. Neither of them touches the database: both are clients of the same REST API, and so is anything you write yourself.
Also here: authoritative UDP and TCP with EDNS0, DNS cookies, zonefile import and export, outbound zone transfer to the secondaries you name, signed with TSIG and announced with NOTIFY, SQLite persistence, token authentication, Prometheus metrics. One server, or a few of them as a cluster that keeps itself in step without a database beside it.
It does not resolve. No recursion, no forwarding, no cache. That one is settled rather than pending (D17), and a name outside its zones is answered with REFUSED. A network that wants both its own zones and the internet runs a resolver, hands that out over DHCP, and points a stub zone at Wegweiser. Both fit on one machine, on different ports.
Everything else it does not do yet is on the scope fence in docs/conventions.md, which says what is in, what is out, and what each pending item is waiting on. The fence moves with the code rather than with a tag, so it is the list to trust.
Every release carries a static binary for linux/amd64 and linux/arm64, the licence beside it,
and a checksums.txt over the lot. Take the archive for your architecture and that checksum
file from the latest release,
then:
$ sha256sum -c --ignore-missing checksums.txt
$ tar xzf weg_*_linux_amd64.tar.gz
$ ./weg versionOr as a container, which is scratch with the one binary in it:
$ podman run --rm --cap-add=NET_BIND_SERVICE -p 53:53/udp -p 53:53/tcp -p 8053:8053 \
-v weg:/var/lib/wegweiser ghcr.io/wegweiserzone/wegweiser:latest--cap-add=NET_BIND_SERVICE is what binding port 53 needs; the server never wants root.
Run it somewhere writable, on ports that need no capability at all:
$ ./weg serve --listen 127.0.0.1:5300 --api-listen 127.0.0.1:8053 --db ./weg.db
weg is answering on 127.0.0.1:5300 — 0 zones, 0 records from ./weg.db
the API is on http://127.0.0.1:8053
weg: this is the first start. The administrator token is shown once:
weg_...
Store it now; only its hash is kept.Open the API address in a browser and paste the token there, or hand it to the CLI:
$ export WEG_SERVER=http://127.0.0.1:8053 WEG_TOKEN=weg_...
$ weg zone create example.com
$ weg zone create 192.168.0.0/24 # becomes 0.168.192.in-addr.arpa
$ weg record add example.com www A 192.168.0.10
added www.example.com. 3600 IN A 192.168.0.10
generated 10.0.168.192.in-addr.arpa. 3600 IN PTR www.example.com.Nobody wrote that second line, and it is on the wire:
$ dig +short @127.0.0.1 -p 5300 www.example.com
192.168.0.10
$ dig +short @127.0.0.1 -p 5300 -x 192.168.0.10
www.example.com.The reverse zone had to exist first. Wegweiser fills the reverse zones it holds; it does not conjure them behind your back.
To see a fuller instance without installing one, make demo builds the binary, starts three
servers as a cluster on unprivileged ports and fills it with what a small network actually
looks like, reverse zones and all. WEG_DEMO_NODES=1 make demo runs a single server instead,
and make demo-stop takes either away again.
Bootstrap settings come from a config file, the environment or a flag, in that order of
precedence. docs/wegweiser.example.yaml documents every one of
them, the packaging looks for it at /etc/wegweiser/config.yaml, and weg config show
prints what a server would start with and where each value came from. Everything else lives
in the database and is reachable through the API.
The API listens on loopback by default, because it can change every zone this server answers for. Putting TLS and a reverse proxy in front of it is the intended way to expose it. A sandboxed systemd unit is in packaging/systemd.
A cluster is three or more servers holding the same zones, each answering queries from its own copy, with every change going through one log. Two servers make no honest cluster, and in a large one only three or five of them vote; D25 says why, and what to run instead.
Each member's configuration file needs a cluster section, with the same secret on every
one; the example configuration describes it. One server
starts the cluster, and every other one joins from its first start, with an empty database:
$ weg cluster init # on the first server, once
$ weg serve --join 192.0.2.1:8054 # on each of the others
$ weg cluster status # who the members are, and who leadsA write can be sent to any member, and reaches the one leading. weg cluster leave takes a
member out, and weg cluster remove takes out one that is off. A member that has left goes
on answering what it held; D46 says how it
comes back.
Two servers can have their third vote from a witness instead of a third copy of everything: wegwitness keeps the log, answers nothing, and hands leadership to a server whenever it wins an election (D39).
Needs the Go version in go.mod, or newer. No cgo, no C toolchain.
$ git clone https://github.com/wegweiserzone/wegweiser
$ cd wegweiser
$ make build
$ ./bin/weg version$ make check # the gate: format, tidy, vet, lint, tests, web interface
$ make test # tests with the race detector
$ make help # all targets| Document | What it covers |
|---|---|
| Conventions | Product thesis, architecture invariants, scope fence, the design bar |
| Decisions | Every settled question, one record each, and the reasoning |
| Configuration | Every bootstrap setting, and what it is for |
| Changelog | What each release moved |
Parts of this repository were written with an AI assistant, and I would rather say so than let you work it out. Not all of it. The direction is mine, so is every call recorded in docs/decisions/, and so is what goes in and what stays out. What I hand over is the writing: the documentation and the decision records are drafted from a paragraph I write first, and some of the code is too. I read what comes back, and the test suite is what keeps it honest: the race detector, fuzz targets on the wire parser, a browser suite over the web interface, and a linter that enforces the architecture mechanically. All of it runs on every push, and it is the same gate the code I typed myself has to pass.
Two reasons. I am one person doing this in my spare time, and it saves an enormous amount of it. And it gets me to a working answer on things I would otherwise have spent a fortnight fighting through, which is time I would rather put into the parts that are actually hard to get right.
That is settled, and not an invitation to argue about it. If it puts you off, fair enough, though the issue tracker is not the place to tell me. If the software is useful to you anyway, you are very welcome here.
See CONTRIBUTING.md. Contributions are certified with a Signed-off-by
line (DCO); there is no CLA.
Security issues: please follow SECURITY.md rather than opening a public issue.
GNU Affero General Public License v3.0 or any later version
(AGPL-3.0-or-later).
Chosen deliberately for a network-facing server: if you run a modified Wegweiser as a service, its users are entitled to the source. Note that this is more restrictive than PowerDNS (GPLv2), Knot DNS (GPLv3) and CoreDNS (Apache-2.0).