Skip to content
Enes Cavus
All work
Firmware12 / 20

PURE — Network-Wide Ad Blocker

A plug-in box that blocks ads and trackers for every device on a home network — a Luckfox Pico Max running a hardened Buildroot Linux image.

1Features

  • Rockchip RV1106G3 (Cortex-A7, 1.2 GHz) with 181 MB usable RAM and 256 MB SPI NAND
  • DNS-level filtering of ~600,000 rules for every device, DoH upstreams queried in parallel
  • Runs the network's DHCP, so every client picks it up as its resolver automatically
  • Reachable as pure.local whatever the local subnet, through a standard-library mDNS responder
  • Moves itself into an unknown subnet on first boot, and rolls back if the gateway goes quiet
  • Watchdog on real HTTP health checks: restart the service, reboot only if DNS stays down
  • Flash-wear aware: raw measurements stay in RAM, only hourly summaries reach the NAND
  • Factory image hardened — telnet, Samba and adb closed, SSH key-only

2Specifications

Board
Luckfox Pico Max
SoC
RV1106G3 · Cortex-A7
RAM
181 MB usable
Storage
256 MB SPI NAND · UBIFS
OS
Buildroot 2023.02 · Linux 5.10
Filter rules
~600,000
Services
5, started at boot
Power
USB-C

3Figure 1 — On the router

PURE — Network-Wide Ad Blocker

4Device panel

Network status: 30-day outage map, download speed and availability
Control panel: queries, blocked domains and per-device rules
Network check verifying DNS, blocking, DHCP and stray resolvers
Setup wizard with router-specific instructions and a live installation check

5Description

PURE sits next to the home router and answers every DNS query on the network: requests for ad and tracker domains go nowhere, everything else resolves normally. It is a Luckfox Pico Max — a Rockchip RV1106G3 Cortex-A7 with 181 MB of usable RAM and 256 MB of SPI NAND — running Buildroot Linux, with the resolver cross-compiled for armv7 as a single static Go binary so the whole system fits on the NAND without a microSD card.

Most of the work was making a development image behave like a product. The stock image shipped with an empty CA store, so the encrypted upstreams failed silently while the service looked healthy; a launch script overwrote the static address with a DHCP lease; a boot race left the board without a default route; and the ownership of /root made sshd reject keys. Each is fixed in the image rather than by hand on every unit, and the init chain was extended so the box can find its own place on a network it has never seen.

Everything is sized for the hardware. Five services share the RAM, and the extra features — outage log, speed tests, new-device alerts, Telegram notifications — run in one Python process on one timer loop instead of five. The board runs 24/7 on flash with a finite write life, so raw data stays in RAM. Features the kernel cannot support — NAT, VPN, packet filtering — were measured and ruled out instead of promised.

Production is written down as a per-unit process around a golden NAND image: flash, verify, strip the development tooling, reset to the setup wizard so the customer sets their own password, and sign off an exit checklist per serial number.

6Boot sequence

Boot sequence · /etc/init.d
  1. S35pureagfirst boot only: take a DHCP lease so the unit lands in the customer's subnet
  2. S40networkbring up eth0 with the saved static address
  3. S45pureaggateway gone + DHCP offer from another subnet → move there and save
  4. S46pureilkayarafter the setup wizard: apply filters, DoH upstreams and DHCP
  5. S99adguardhomestart the resolver; wait for LOWER_UP before adding the default route
  6. S99puremdnsannounce pure.local — A record, cache-flush bit, 120 s TTL
  7. S99puremonitoroutage log, hourly speed test, new-device alerts in one process
  8. S99purepanelweb panel behind the resolver's login
  9. S99purewatchdogHTTP health checks: restart first, reboot only for DNS

7Resources

Memory budget181 MB
Resolver
87 MB
Web panel
12.4 MB
Monitor
10.8 MB
mDNS
5.8 MB
Watchdog
<1 MB
Kernel, cache & free
64 MB

Resident memory with all five services up. The resolver grows to ~117 MB after a heavy scan; about 45 MB stays free under load.

8Design tools

BuildrootBusyBoxGoPythonShellSolidWorks