Quickstart
Ward is a single Go binary. You need Go 1.25 or newer and dig (preinstalled on macOS; dnsutils or bind-utils on Linux).
1. Install
Section titled “1. Install”go install protocolward.ai/ward/cmd/ward@latestward versionward version prints ward dev for source installs. If your shell cannot find ward, add $(go env GOPATH)/bin to your PATH.
2. Write a minimal config
Section titled “2. Write a minimal config”Make a working directory with one blocklist and one decoy:
mkdir -p ~/ward && cd ~/wardprintf '0.0.0.0 doubleclick.net\n0.0.0.0 google-analytics.com\n' > blocklist.txtprintf 'nas-backup.home.arpa\n' > decoys.txtSave this as ~/ward/ward.yaml:
listen: "127.0.0.1:5354"log_level: "info"
upstreams: - address: "9.9.9.9:853" server_name: "dns.quad9.net"
blocklists: - id: "starter" path: "./blocklist.txt"
decoys: - id: "home-decoys" path: "./decoys.txt"
model: builtin: lexicallistenhas no default; you always choose the bind address. Port 5354 avoids 5353, which macOS uses for mDNS.- Upstreams are DNS-over-TLS.
server_nameis the name on the upstream’s TLS certificate. - List paths are relative to the directory you run
wardfrom. model: builtin: lexicalturns on the on-device behavioral detector.
Every key is documented in the configuration reference.
3. Check, then run
Section titled “3. Check, then run”ward doctor --config ./ward.yamlward serve --config ./ward.yamlward doctor validates the file and checks that the bind address is free and each upstream accepts a TCP connection (the TLS handshake is not checked yet). Every error it prints names the fix.
4. Ask it some questions
Section titled “4. Ask it some questions”In a second terminal:
dig @127.0.0.1 -p 5354 github.com +shortdig @127.0.0.1 -p 5354 doubleclick.net +shortdig @127.0.0.1 -p 5354 nas-backup.home.arpa +shortdig @127.0.0.1 -p 5354 xjw7qk2vzr9tplm4.com +short| Query | What happens |
|---|---|
github.com |
Not on any list. Forwarded upstream and answered with real addresses. |
doubleclick.net |
Blocklist hit. Answered locally with 0.0.0.0; the log names list starter. |
nas-backup.home.arpa |
Decoy hit. Answered locally and never forwarded. Ward logs an alert naming the client that asked. |
xjw7qk2vzr9tplm4.com |
Not on any list, so it is forwarded as normal. The detector scores the name in the background and records a flag with its reasons. It is not blocked. |
Open the local dashboard at http://127.0.0.1:18987 to see recent decisions and flags. It only binds to loopback.
5. Use it for real
Section titled “5. Use it for real”- Pull in a maintained list. Ward reads hosts-file lines that start with
0.0.0.0,127.0.0.1or AdAway’s short0, and AdGuard||example.com^rules, so most public blocklists work unchanged. Plain one-name-per-line files are not accepted for blocklists or allowlists yet; Ward skips those lines and reports how many it skipped when it loads the list. - Add an allowlist for anything a list gets wrong. Allowlist hits beat blocklist hits.
- Bind
:53on a machine your network can reach, and point your router’s DHCP DNS setting at it. Binding port 53 needs elevated privileges (for exampleCAP_NET_BIND_SERVICEon Linux). - Read how the detector works before acting on flags.