OpenRung
A free, privacy-first relay network that helps you reach the open internet. Open source and nonprofit-run.
A DNS resolver that runs on hardware you own. It blocks known-bad names, flags suspicious new ones on-device, and raises an alert when something on your network looks where nothing legitimate ever loo
SecurityGo · ShellGitHub @ujjaval-vermaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Sovereign DNS defence: blocklists, on-device behavioral detection, decoys.
Translation pending
Loading the full README.
Read the full README ↗Beta — expect breakage. Config keys, CLI flags and log lines may change between releases without notice. The behavioral detector flags and never blocks. Please report what breaks.
On 31 March 2026, someone used a hijacked maintainer account to publish [email protected] and [email protected]. Both pulled in a new dependency whose postinstall script installed a remote-access trojan; on macOS it landed in /Library/Caches/. Every check upstream of npm install passed. The trojan still had to reach its server, and stopping that is the one job Protocol Ward does. The command-and-control domain was registered the day before, so no list knew it at the time; the timing and per-device-history signals aimed at that pattern are coming soon, and today you stop it by adding the domain to a list.
Ward is a DNS resolver that sits at the edge of your network, on hardware you control, and decides which names your devices may resolve. It is not a supply-chain scanner. It does not audit tarballs or sandbox postinstall scripts. It is the layer that is still there after every upstream check has been bypassed. The longer argument is in docs/product/pitch.md.
Per query, decoys are matched first (a decoy is never forwarded), then allowlists, then blocklists, then the query is forwarded upstream. The detector runs only after a forward, and only flags.
Names on your lists are answered with your configured block response, with the list and rule in the log. Ward reads hosts-format lists and AdGuard ||host^ rules, so the hosts-format or AdGuard-format exports of public lists such as OISD, Hagezi and StevenBlack work without conversion. Bare-domain lines (a name with no address) are skipped with a warning, so a mixed file still loads, but a file with no usable entries fails to load. AdGuard exports' [Adblock Plus] and ! header lines also produce warnings but the file loads. Allowlists take precedence.
Example — TanStack, May 2026 (CVE-2026-45321). 84 malicious versions of 42
@tanstack/*packages sent stolen credentials out throughfilev2.getsession.organdseed1–seed3.getsession.org. The advisory names domain blocking as the only network mitigation. Putgetsession.orgon a list and that route is closed.
The detector is opt-in: set model: { builtin: lexical } in your config. When enabled, Ward analyses each hostname that misses your lists on-device, skipping local names and OS connectivity checks; timing and per-device history are coming soon. The built-in detector scores the name itself (randomness, rare letter sequences, digits, consonant runs, length) and raises a flag on names that look machine-generated, such as DGA domains. In the beta it never blocks. Flags show on the dashboard with their reasons, and you add a name to a blocklist to enforce. → Try it in your browser: Ward's real Go code compiled to WebAssembly, with nothing sent off-device for analysis.
You configure names that nothing legitimate on your network ever resolves, such as nas-backup.home.arpa. A lookup is a high-confidence sign that something is enumerating your network, and Ward raises an alert with attribution. No model runs. Decoys never appear in ward config export (invariant 6). → Try the decoys demo.
The AI classifies. The deterministic policy engine decides. Classifiers emit a typed verdict; the policy engine is a stateless Go function with an exhaustive case for every verdict. In the beta, built-in detector verdicts only raise a flag, and an external classifier's verdicts are only logged. A model never has direct effect on whether a query is answered, and the DoD harness checks the pkg/model import boundary on every make dod run (invariant 1).
go install protocolward.ai/ward/cmd/ward@latest # Go 1.26+
ward version
go install builds print ward dev; the archives on the releases page print the release version (for example ward 0.2.0-beta.2).
Ward runs on Linux and macOS. Windows support is planned for the future.
Docs, quickstart and configuration reference: https://protocolward.ai.
From source:
git clone https://github.com/ujjaval-verma/protocolward.ai.git
cd protocolward.ai
make bootstrap # pinned tools + git hooks (needs pre-commit installed)
make ci # lint + vet + race tests + repo gates + build + wasm
./bin/ward version
listen is required in ward.yaml; there is no default bind address. The examples use 127.0.0.1:5354 because on macOS port 5353 belongs to mDNSResponder.
The repo ships a 42-domain demo blocklist at testdata/blocklists/trackers-demo.txt.
Needs dig (from dnsutils / bind-tools, preinstalled on macOS) and a built binary (make build; the full make ci above is not required).
cat > /tmp/ward.yaml <<'EOF'
listen: "127.0.0.1:5354"
upstreams:
- address: "1.1.1.1:853"
server_name: "cloudflare-dns.com"
timeouts: {dial: "2s", query: "2s", shutdown: "2s"}
blocklists:
- id: trackers
path: ./testdata/blocklists/trackers-demo.txt
model:
builtin: lexical
EOF
./bin/ward serve --config /tmp/ward.yaml &
sleep 1
for h in doubleclick.net google-analytics.com criteo.com wikipedia.org github.com qwkjxzpvtr.net; do
printf "%-28s → %s\n" "$h" "$(dig @127.0.0.1 -p 5354 $h +short | head -1)"
done
echo "Open http://127.0.0.1:18987 and look under flags for qwkjxzpvtr.net"
read -r -p "Press Enter to stop ward serve " _
kill %1
This is the beginning of the README. Read the rest at the original. Read the full README ↗