Flagship system
ShieldDNS v2.0
Security · Networking · Systems
A self-hosted, privacy-first ecosystem that blocks ads, trackers and malware at three layers — an Android VPN filter, a Windows DNS server and a browser extension — all filtering locally with no accounts and no telemetry.
- 12
- Technologies
- 2
- Domains
- 8
- Capabilities
- 1
- Public links
Who it's for
Privacy-conscious individuals, families and small teams who want ads, trackers and malware blocked on every device — without trusting a third-party resolver with their browsing history.
Where it fits
Personal Android phones (no root), Windows desktops and any Chromium/Firefox browser; equally at home protecting a household or a small office network.
The hard part
The problem
Ad and tracker blocking is usually solved per-browser or behind a paid cloud resolver that sees every domain you visit. Sridhar wanted coverage across the whole device — every app, every browser, and the desktop — without routing traffic through a third party or trusting anyone else's server.
Key constraints
Everything had to run locally: no accounts, no telemetry, no data leaving the device. On Android it had to work without root. And it had to stay fast while matching against millions of blocklist entries on every single DNS lookup.
Architecture overview
Three independent applications share one principle — an ad cannot load if its domain never resolves. The Android app runs a loopback VpnService that intercepts only DNS queries; the Windows app runs a local resolver on 127.0.0.1:53 and redirects the system DNS; the browser extension blocks at the network and DOM layers. Each resolves a domain through an ordered, fastest-first pipeline before forwarding clean queries over encrypted upstream DNS.
Live request · tracker.ads.example
- DNS request
Incoming domain lookup
- Session whitelist
Known-good — allow immediately
- LFU hot cache
Recently seen — answer without a full match
- Session blacklist
Known-bad — block immediately
- Bloom-filter prefilter
Definitely-not-blocked exits here
- Patricia trie
Authoritative match against millions of entries
request exits here - Block
No early match — descends to the authoritative Patricia trie, matches a blocklist entry, blocked.
How it works
What's actually in it.
- 01Five-layer resolution pipeline: session whitelist → LFU hot cache → session blacklist → Bloom-filter prefilter → authoritative Patricia trie match.
- 02CNAME uncloaking inspects every hop of the chain rather than only the queried hostname.
- 03Behavioural reputation scoring (0–100) from subdomain entropy, risky TLDs, homoglyph look-alike detection and visit frequency, with time-decay so one spike never permanently blocks a site.
- 04Redirect-chain reconstruction from a rolling DNS buffer, scored by hop count, /24 IP-block diversity and inter-hop timing.
- 05Android VpnService loopback tunnel intercepts DNS only — user traffic is never routed off-device.
- 06Windows .NET 10 WPF app runs a resolver on 127.0.0.1:53, redirecting system DNS on start and restoring it on stop.
- 07Eight community blocklists (EasyList, EasyPrivacy, uBlock, OISD, StevenBlack, Hagezi, URLhaus, AdGuard) refreshed in the background via WorkManager.
- 08Release engineering: R8 shrinking, certificate pinning, separate debug/release signing, JUnit + MockK tests and LeakCanary.
Engineering decisions
A Bloom filter in front of the Patricia trie
The authoritative match lives in a Patricia trie holding millions of blocklist entries. But most domains a user visits are not on any blocklist — so paying for a full trie descent on every lookup is wasted work. A Bloom filter answers 'is this definitely not blocked?' in constant time and lets the overwhelming common case skip the trie entirely, with the whitelist and an LFU hot cache short-circuiting even earlier.
The trade-off
A Bloom filter can report a false positive, so a 'maybe blocked' answer still costs a trie lookup to confirm. That cost is accepted deliberately: false positives are rare, the trie is authoritative, and the pipeline is tuned so the expensive path runs only when it has to.
The obvious approach
Ship a browser extension that blocks against a filter list — the standard, well-trodden approach.
Why I didn't choose it
A browser extension only protects that browser. Native apps, games, telemetry and other browsers leak straight past it, and CNAME-cloaked trackers hiding behind first-party hostnames are invisible to a domain-only list.
My approach
Block at the DNS layer across the whole device and reconstruct the CNAME chain hop by hop, so a tracker disguised as a first-party subdomain still gets caught. Three apps cover browser, desktop and mobile from one shared principle.
Trade-offs
Three codebases (Kotlin, C#, JavaScript) and a real DNS pipeline to maintain — far more surface than one extension — in exchange for coverage nothing browser-only can match.
Technical highlights
- Root-free, system-wide DNS filtering on Android via a loopback VpnService.
- Per-app firewall across Wi-Fi, mobile data and roaming, enforced at the DNS layer.
- Encrypted upstream resolution over DoH/DoT to Cloudflare, Quad9, AdGuard, Mullvad or Google, with bootstrap IPs compiled in.
- Tiered shield levels (Normal / Aggressive / Nuclear) trading false-positive rate against aggressiveness.
Claims & evidence
Designed for microsecond-scale lookups against millions of blocklist entries by making the common case nearly free.
Design target · ShieldDNS architecture — Bloom + cache pipeline
Filters at three layers: browser extension, Windows applications system-wide, and Android system-wide without root.
Qualitative · ShieldDNS scope
Every statement above carries its evidence class — nothing is invented.
Tech stack
- Kotlin 2.0
- Jetpack Compose
- Android VpnService
- Hilt
- Room
- WorkManager
- C# / .NET 10
- WPF
- Manifest V3
- declarativeNetRequest
- DNS-over-HTTPS
- DNS-over-TLS
Want to go deeper on ShieldDNS v2.0?
I'm happy to walk through the architecture and the decisions behind it.