(0.4.3 released) FastAdHunter — High-performance DNS/HTTP filtering on RouterOS containers written in RUST

FastAdHunter - GitHub - bobdenaut/FastAdHunter: The fastest network-wide ad blocker with the smallest possible memory footprint · GitHub

Network-wide filtering !

FastAdHunter is an API-first network filter for homes, labs, and other networks. One ARM64 container protects the LAN's phones, browsers, TVs, thermostats, game consoles, and other devices.

It combines DNS filtering, HTTP URL filtering, and HTTPS host filtering at the SNI. HTTPS is relayed as an encrypted connection: FastAdHunter does not decrypt browsing traffic or require a client CA in the production path.

Memory safe proven and fast, really fast! Rust job :slight_smile:

Dashboard:

Live feed:

Mikrotik terminal:

Production status: FastAdHunter 0.4.1 has been running on the reference MikroTik RB5009 since 2026-09-21 in dns+http+https mode. HTTPS interception is disabled by design.

Why FastAdHunter

Browser extensions are effective but device-specific. DNS filters protect more devices, but a domain-only decision cannot distinguish one unwanted path from the rest of a useful host. FastAdHunter puts both decisions at the network boundary:

  • Network-wide: one router deployment covers every client on the LAN.
  • Precise: DNS rules act on domains; URL rules can use host, path, method, resource type, and client context.
  • Privacy-conscious: HTTPS policy uses the TLS SNI and leaves the encrypted connection intact.
  • Predictable: caches, queues, and tracking are bounded; request bodies are streamed rather than buffered.
  • Operable: REST, WebSocket events, a web dashboard, and a terminal monitor expose the engine without a privileged UI integration.

What it filters

Layer What happens
DNS Domains are checked before cache or upstream resolution. Upstream answers are cached; verdicts are not.
HTTP Requests are checked against URL rules before the origin is contacted. Bodies are streamed.
HTTPS The TLS SNI is read, host-level policy is applied, and the encrypted connection is relayed byte for byte.
DoT / DoH Encrypted DNS listeners serve clients that support Private DNS.

One compiled Rule Engine serves DNS and HTTP. A path rule therefore remains a path rule; it does not become a whole-domain DNS block.

HTTPS without interception

FastAdHunter's production HTTPS path is deliberately limited and explicit:

  1. The listener reads the TLS ClientHello and extracts the SNI.
  2. Host-level policy decides whether the connection is blocked or forwarded.
  3. An allowed connection is relayed without decrypting or rewriting it.

The production path does not install a FastAdHunter CA on client devices. The interception code exists in the build but is disabled because the Interception Document has no clients selected. This keeps ordinary HTTPS browsing opaque to the proxy while still allowing host-level filtering and HTTPS/HTTP3 control at the router boundary.

Production snapshot

These are measurements from the reference RB5009 deployment or its deployed path. They are not universal promises for every device or workload.

Metric Observed result Measurement context Project target
Rules compiled 766,499 from 1,198,086 source entries 0.4.1, 16 public lists corpus-dependent
Ruleset resident heap 24.48 MiB 0.4.1, deployed ruleset <= 40 MB
Ruleset compile time 2.92 s 0.4.1, 1.20 M parsed rules < 3 s
Sustained DNS throughput 20k+ QPS 0.2.x deployed path, synthetic profile >= 10k QPS
HTTP proxy added latency +344 us p50 0.2.x, opaque pass-through workload < 1 ms budget; statistic differs
Opaque HTTP throughput 208-271 MiB/s 0.2.x, pass-through workload >= 100 MiB/s
Container image 14.07 MiB 0.3.0, ARM64 image with dashboard <= 30 MB

The 0.2.x and 0.3.0 rows are historical measurements and are labelled as such; they are not 0.4.1 release measurements. The refresh transient remains a known, bounded cost: rebuilding the ruleset temporarily holds old and new data together and can exceed the steady-state memory budget. See PERFORMANCE.md and the linked measurement records for methodology and limits.

Built for routers

FastAdHunter is a statically linked, non-root ARM64 container. Configuration, certificates, cached lists, and history live on persistent mounts outside the image:

/config    TOML configuration, API key, TLS certificate, authentication state
/data      cached rule lists, session state, and history

The reference target is a MikroTik RB5009 with four ARMv8 cores and 1 GB of memory shared with RouterOS. The runtime keeps the normal path small and bounded:

  • the DNS cache is bounded by entry count and bytes;
  • stale answers can be served while one bounded worker refreshes them;
  • HTTP and HTTPS bodies are streamed rather than buffered;
  • configuration and ruleset changes use atomic swaps;
  • HTTP connections can run in dedicated single-thread allocation domains.

Dashboard and API

The dashboard is served by fah-api from the same image and TLS listener. It uses the public API rather than a privileged path into the engine.

The API provides health, authentication, telemetry, statistics, clients, policies, lists, rules, cache, history, configuration, certificates, memory diagnostics, and a live event stream. The terminal monitor uses the same REST and WebSocket interfaces:

cargo run --release -p fah-tui-monitor

See API.md for endpoint shapes.

Security model

The administrative API uses HTTPS by default and supports two authentication paths:

  • Bearer API key for automation. It is generated on first boot and stored in /config.
  • Argon2id password authentication for the dashboard, issuing a signed session cookie with HttpOnly, Secure, and SameSite=Strict properties.

Administrative routes require authentication, except health and login. The deployment has no WAN administration model; expose the API only on a trusted management network. HTTPS interception is disabled in the current production path and does not require a client CA.

Read the complete security model in SECURITY.md.

Quick start

The production workflow is:

  1. Build an ARM64 image with Docker Buildx.
  2. Put /config and /data on persistent storage.
  3. Start the container with DNS and the configured HTTPS API port.
  4. Point the LAN resolver at the container and verify DNS filtering.
  5. Enable dns+http or dns+http+https after the DNS path is working.
docker buildx build --platform linux/arm64 \
  -t fastadhunter:0.4.1 \
  -o type=docker,dest=fastadhunter-arm64.tar .

The complete RouterOS procedure includes image format requirements, network placement, verification, and rollback: docs/deploy-rb5009.md.

Configuration precedence is built-in defaults, TOML, environment variables, then validated API changes. See CONFIGURATION.md.

Current scope

FastAdHunter is not:

  • a browser extension;
  • antivirus software;
  • an intrusion detection system;
  • a general-purpose firewall;
  • a cosmetic DOM filter.

Cosmetic HTML filtering is not part of today's production path. It is planned as a built-dormant, opt-in capability and is not currently built. FastAdHunter is proprietary and currently has no public license.

Documentation

Architecture diagrams

Explore the system visually in docs/diagrams:

FastAdHunter is built with Rust, Tokio, Hyper/Axum, Hickory, rustls, rcgen, Argon2id, and Preact. Its design prioritizes measurable latency, bounded memory, streaming, and a small operational surface over feature count.