# furst-proxy A filtering HTTP proxy. Point any browser at it and ad and tracker hosts stop resolving — including in `surf` and other WebKitGTK browsers, which have no extension mechanism and therefore no uBlock Origin. 80,000 rules, ~9MB resident, 82ms to start. ## It does not decrypt your traffic A proxy receives `CONNECT doubleclick.net:443` **before** any TLS handshake happens. That is enough to refuse the connection, so blocking needs no certificate authority in your trust store and no visibility into the contents of anything — your bank session is a sealed tunnel this program relays without being able to read. Ads and trackers are third-party hosts, so refusing hosts is most of the win. What it cannot do, by construction: - hide elements cosmetically (uBlock's `##` rules) - block ads served from the site's own domain - filter inside an HTTPS response Those need TLS interception. That is a deliberate omission, not a missing feature — see [the note below](#what-tls-interception-would-add). ## Use ```sh furst-proxy --init # write ~/.config/furst/proxy.toml furst-proxy --update # fetch the blocklists furst-proxy # run in the foreground furst-proxy --test doubleclick.net # would this be blocked? furst-proxy --env # shell exports furst-proxy --pac ~/.furst.pac # proxy auto-config file ``` Point a browser at `127.0.0.1:8228` for both HTTP and HTTPS: ```sh eval "$(furst-proxy --env)" # anything honouring http_proxy surf https://example.com/ # WebKitGTK reads the environment ``` For Firefox, set the manual proxy in *Settings → Network Settings*, or load the PAC file. Visit **http://furst.proxy/** through the proxy for a status page: rules loaded, requests, share blocked, bytes relayed. ## What happens to a request | | | |---|---| | `CONNECT` to a blocked host | `403`, refused before the handshake | | `CONNECT` to anything else | tunnelled bytes, untouched | | plain HTTP to a blocked host | `204 No Content` | | plain HTTP to anything else | forwarded, relayed | Blocked plain-HTTP requests get `204` rather than an error, so a blocked script or beacon looks like an empty answer instead of a failure the page retries. ## Configuration `~/.config/furst/proxy.toml`: ```toml port = 8228 lists = ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"] # Webfont and analytics CDNs: a webfont is bytes, a layout pass and a repaint # for no information, which is a bad trade on an old CPU. block_heavy = true block = ["extra.tracker.example"] allow = ["fonts.gstatic.com"] # wins over everything ``` Lists are cached under `$XDG_CACHE_HOME/furst/lists` for a week; `--update` refreshes them. If a refresh fails, the stale copy is used — degraded filtering beats none. Hosts files, bare domain lists, and the `||domain^` subset of Adblock syntax are all accepted; rules that need response inspection (`$third-party`, paths, wildcards) are skipped rather than half-applied. Matching is by domain suffix, so `doubleclick.net` also covers `stats.g.doubleclick.net`. Lookups walk the labels of the requested host, so cost is the number of dots in it, not the size of the list. ## What TLS interception would add A `--mitm` mode would generate a local CA, mint a certificate per host, and decrypt each connection so responses could be filtered. It would buy cosmetic filtering and first-party ad blocking. It was left out because the trade is poor here: - every HTTPS page would be decrypted by this program, so a bug in it becomes a bug in the confidentiality of everything you browse - a CA private key on disk is a credential worth stealing - certificate minting and validation is where proxies get subtly wrong, and getting it wrong silently downgrades security rather than breaking loudly - `furst-serve` already renders most pages without a browser engine, so the remaining first-party ads are seen rarely If you want it anyway, the seam is `handle()` in `proxy.rs`: after accepting `CONNECT` for an unblocked host, terminate TLS with a minted certificate instead of tunnelling. ## Limits - Only the loopback interface is bound. This is a personal proxy, not a network service; do not expose it. - Plain HTTP is relayed with `Connection: close`, so no keep-alive on that path. HTTPS tunnels are unaffected. - At most 96 concurrent connections, so a runaway page cannot exhaust a 4GB machine. Beyond that the proxy answers `503`.