# furst A toolset for browsing the modern web on hardware that cannot run a modern browser. Built for a Core2 Duo with 4GB of RAM and a minimal Arch install, where Firefox is the bottleneck. ## The idea A news page is 2–5MB across 80+ requests with 1–3MB of JavaScript to parse and JIT. The same article, extracted, is ~20KB. Choosing a lighter browser engine buys 2–3×. **Not loading the payload at all buys considerably more.** So the goal is not a faster browser. It is to keep a browser engine out of the loop wherever possible, and to make the cases where you still need one rare and deliberate. ``` link click │ ┌────▼─────┐ video ──────────────► mpv (H.264 forced) │ furst │ pdf / image ────────► zathura / nsxiv │ router │ mailto / magnet ────► xdg-email / transmission └────┬─────┘ a short "heavy" list ► firefox ──┐ │ │ ▼ everything else │ ┌──────────────┐ ┌──────────────┐ │ │ furst-serve │───────►│ furst-read │ │ │ local reader │ │ library │ │ └──────┬───────┘ └──────────────┘ │ │ minimal HTML, no JS/CSS/fonts │ ▼ ▼ a light browser (surf, luakit, …) ──────► furst-proxy ad and tracker hosts refused before the TLS handshake ``` ## The pieces | | | |---|---| | [`furst`](src/) | the router. Sits where the default browser used to and dispatches each URL to the cheapest tool that can handle it. Two dependencies, 541K binary. | | [`furst-read`](furst-read/README.md) | fetch, extract the article, render it as a self-contained document. Library and CLI. | | [`furst-serve`](furst-serve/README.md) | the local reader. Rewrites in-page links so browsing stays in reader mode, caches pages, and handles what extraction cannot: search, feeds, listings, comment threads. | | [`furst-proxy`](furst-proxy/README.md) | filtering proxy for the times you do use a browser. Refuses ad and tracker hosts at `CONNECT`, before any TLS handshake, so nothing is decrypted. 80k rules in 9MB. | ## Install ```sh cargo build --release --workspace install -Dm755 target/release/furst target/release/furst-read \ target/release/furst-serve -t ~/.local/bin/ furst --init # rules tuned for low-end hardware furst --install # become the system default browser furst-serve --init # home page links and feeds furst-proxy --init && furst-proxy --update # 80k blocklist rules sudo pacman -S mpv yt-dlp zathura zathura-pdf-mupdf nsxiv surf ``` To filter what a browser still loads directly, run `furst-proxy` and `eval "$(furst-proxy --env)"`. This is what gives `surf` ad blocking, since WebKitGTK has no extension mechanism. Then every link click in every application goes through the router, and most of them never reach a browser engine. ## Measured | page | raw HTML | scripts | stylesheets | through furst | |---|---|---|---|---| | wiki.archlinux.org/title/Zram | 64K | 4 | 2 | 16K | | en.wikipedia.org/wiki/Core_2 | 207K | 5 | 2 | 47K | | theverge.com | 770K | 55 | 71 | 2.4K | | lwn.net headlines (feed) | — | — | — | 11K, 15 entries | The HTML column understates it. The script and stylesheet columns are the real story: those become zero, along with every webfont and tracker, which is where an old CPU actually spends its time. A cached revisit is ~0.4ms. ## Two details that matter on this hardware **Video is forced to H.264.** A Core2 handles 720p `avc1` in software but stalls on VP9 and AV1, which is what YouTube serves by default. The format string in the `video` rule is doing more work than the resolution cap, and playing it in mpv skips the browser entirely. **Blocking needs no certificate authority.** A proxy sees `CONNECT doubleclick.net:443` before any TLS handshake, so it can refuse the connection without decrypting anything. Ads and trackers are third-party hosts, so that captures nearly all of the weight while leaving your traffic sealed. **The reader is the catch-all, not a special case.** Rules fall through when their command is missing, so the last rule can be "send it to the reader" while `default` stays a plain browser. Firefox is left for a short list of sites that genuinely need it — and every reader page has a **browser** link for the rest. ## Limits - Anything that renders client-side arrives empty. There is no JavaScript here; that is the point, not an oversight. - Free search endpoints rate-limit repeat visitors. The reader detects a challenge page and tells you which engine refused, rather than showing an empty list. See [furst-serve](furst-serve/README.md#on-search). - Paywalls and DRM are not defeated, only rendered plainly or handed off. ## Development ```sh cargo test --workspace # note: --workspace, the root package is just furst cargo clippy --workspace --all-targets ``` ## License MIT