Skip to content

AlasdairDev/whxrl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

whxrl

A pocket-sized, network-wide ad & tracker blocker for the M5StickS3. by AlasdairDev

whxrl is a DNS sinkhole — a tiny Pi-hole-style ad blocker that runs entirely on an M5StickS3 (ESP32-S3). Point your router's DNS at it and every device on your Wi-Fi — phones, laptops, TVs — gets ads and trackers blocked, with nothing to install on those devices.

It sets itself up right on the Stick: scan for your Wi-Fi, join it, and type the password on the on-screen keypad. No PC or phone required. Once it's online there's also a clean web dashboard at http://whxrl.local.


How it works

When a device asks "what's the IP for ads.example.com?", whxrl checks the name against its blocklist. If it's an ad/tracker domain it answers 0.0.0.0 so the request never loads. Everything else is quietly forwarded to a real upstream resolver (Quad9 9.9.9.9 by default) and the answer passed back.

The blocklist lives in PSRAM as a hash set of 32-bit fingerprints (4 bytes per domain), so lookups are effectively instant even at the ~600,000 domains it holds. The same packed hashes are what's stored on flash too, so the on-device list is ~3.5× smaller than holding the domain text and boots in a single read. On first connect the device downloads and merges several well-maintained lists, de-duplicated — together they total roughly a million domains, trimmed to the ~600k that fit the hardware:

  • Hagezi DoH — encrypted-DNS bootstrap servers (see Anti-bypass below)
  • Hagezi Pro++ — ads + trackers + telemetry + native app/OS tracking (the bulk of the list)
  • Hagezi TIF — threat intelligence (malware / phishing / scam)
  • StevenBlack hosts — ads + malware

The lists refresh themselves automatically every few days, so new domains get picked up without a reflash. The download verifies each list server's TLS certificate against the built-in Mozilla CA bundle.

CNAME uncloaking: replies coming back from the upstream resolver are inspected too. If an innocent-looking first-party name (e.g. metrics.example.com) turns out to resolve via a CNAME to a known tracker, that answer is sinkholed as well — catching trackers that try to dodge a plain domain match.

Anti-bypass: many browsers, phones and TVs ship their own DNS-over-HTTPS and would quietly route around this box. whxrl blocks the bootstrap domains those DoH resolvers use, so those devices fall back to system DNS — i.e. back through the filter. (It still can't read DoH/DoT traffic itself; block those ports at your firewall if a device hard-codes its own resolver IP.)


Hardware

Board M5StickS3 (ESP32-S3, M5StickC Plus2-class)
Flash 8 MB
PSRAM 8 MB
Display 240 × 135 LCD

PSRAM is what makes the big blocklist possible. On a board without PSRAM the firmware falls back to a small in-RAM list (MAX_DOMAINS_NO_PSRAM, 8k domains).


Flash a prebuilt build (no toolchain)

Want it running without installing anything? The release/ folder has prebuilt binaries you can flash straight from a browser with the Espressif web flasher — use Chrome or Edge (Web Serial isn't supported in Firefox/Safari):

  1. Open https://espressif.github.io/esptool-js/.
  2. Set Baudrate to 921600, click Connect, and pick the Stick's serial port. (If Connect fails, hold the BOOT button while connecting, or drop to 115200.)
  3. Click Add File → choose release/whxrl-m5sticks3-merged.bin → set the offset to 0x0 → click Program.
  4. Press reset. The LCD drops into Wi-Fi setup; then open http://whxrl.local (default login admin / whxrl — change it right away).

See release/README.md for the component-file option and the exact offsets.


Building & flashing

Everything builds with arduino-cli; the included tools/build.ps1 fetches the CLI, the ESP32 core, and the libraries for you, then compiles firmware/whxrl.

# from the repo root, on Windows:
powershell -ExecutionPolicy Bypass -File .\tools\build.ps1            # build only
powershell -ExecutionPolicy Bypass -File .\tools\build.ps1 -Upload COM9   # build + flash

To build by hand with your own arduino-cli:

arduino-cli compile \
  --fqbn esp32:esp32:esp32s3:PSRAM=opi,FlashSize=8M,PartitionScheme=custom,FlashMode=qio,CDCOnBoot=cdc \
  firmware/whxrl

Important build notes

  • This is an 8 MB board. The bundled firmware/whxrl/partitions.csv gives a 3 MB app slice + ~4.9 MB FAT partition for the blocklist. The stock 16 MB schemes (app3M_fat9M_16MB) will not fit and FFat won't mount.
  • PartitionScheme=custom tells the core to use that partitions.csv.
  • Libraries: M5Unified, plus the ESP32Async forks of AsyncTCP and ESPAsyncWebServer (the older me-no-dev repos can fail to build on S3).

If your device doesn't show up as a COM port when plugged in: use a data (not charge-only) USB-C cable, and try holding the BOOT button while connecting.


Using it

On the Stick (buttons)

Button Action
Front "M5" button Select / OK / tap a key
Side button — single click Next / Down
Side button — double click Previous / Up
Side button — hold ~0.6 s Back / Cancel

First boot drops you straight into Wi-Fi setup: pick your network, type the password on the keypad, done. The home screen then shows RUNNING, the device's IP, and live Queries / Blocked / %-blocked counters. The About screen always shows the current dashboard login.

Point your network at it

To filter your whole network, set your router's DNS server (in its DHCP / LAN settings) to the device's IP, then toggle each device's Wi-Fi off and on so it picks up the change. To try it on just one device first, set that device's DNS to the whxrl IP.

Tip: reserve the device's IP in your router (tie it to the Stick's MAC) so it never changes. The dashboard's setup guide walks through all of this.

The dashboard

Once connected, open http://whxrl.local (or the device's IP) for a live dashboard: requests, ads blocked, block rate, most-blocked sites, a just-blocked feed, an On/Off toggle, and a "refresh blocklist" button.

The dashboard is password-protected (see Security below). The default login is:

username: admin
password: whxrl

The dashboard shows a reminder to change this immediately, and there's a Change login link to set your own username and password. The current login is always visible on the Stick's About screen if you forget it.


Security

whxrl is a network device, so it's built to be safe to leave running on your LAN:

  • DNS is LAN-only. The sinkhole answers queries from private addresses only (10.x, 192.168.x, 172.16–31.x, 127.x, 169.254.x), so it can't be abused as an open resolver from the internet.
  • Upstream replies are validated. Forwarded answers are only accepted from the resolver they were sent to, and the answer parser is bounds-checked, so a forged or malformed reply can't poison a lookup or corrupt memory.
  • The dashboard requires a login. All control endpoints (stats, on/off toggle, refresh, blocklist upload, change-login) sit behind HTTP Basic auth. Credentials are stored on the device's flash and changeable from the dashboard. Change the default admin / whxrl right away — it's a published default.
  • The setup hotspot is WPA2-protected. When you start on-device "Web Setup (AP)", the temporary whxrl hotspot uses a random per-device key shown on the screen, so a passer-by can't join during setup and snoop the Wi-Fi password you type.
  • Blocklist downloads use validated TLS. Each list is fetched over HTTPS with the server certificate checked against the built-in Mozilla CA bundle (with a graceful fallback so a captive portal or odd middlebox can't break updates — the dashboard shows whether the last refresh was TLS verified).

Everything runs over plain HTTP on your LAN, which is the norm for this class of device. If you want it reachable only from trusted machines, restrict it at your router/firewall.


Project layout

firmware/whxrl/      the firmware — whxrl.ino + partitions.csv (this is the Arduino sketch)
release/             prebuilt .bin files + web-flasher instructions
tools/build.ps1      one-shot build + flash script (Windows)
examples/            a small sample blocklist.txt
docs/                extra notes (e.g. generating flashable binaries)

arduino-cli/ and arduino-user/ are local build tooling installed by the build script; they're gitignored and not part of the repo.


Notes & limitations

  • Domains are matched by 32-bit hash, so there's a vanishingly small chance of a collision blocking a legitimate domain. Widen the table to 64-bit if that ever matters for your list.
  • It handles standard UDP DNS (port 53). It does not read DNS-over-HTTPS/TLS traffic — but it does block the bootstrap domains DoH clients use, which pushes most devices back onto filtered system DNS. For a device that hard-codes its own resolver IP, block that at your firewall.
  • It blocks ads served from separate ad domains, but not ones delivered from the same place as the content itself (e.g. YouTube's in-stream ads).
  • Counters reset when the device restarts (they're not saved to flash).

License

MIT.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors