Furst/README.md
nak0x ec79c07b0f Add furst, a URL router for low-end hardware
Sits where the default browser used to and dispatches each URL to the
cheapest tool that can handle it: mpv for video, zathura for PDFs, a light
WebKit browser for reading, Firefox only when nothing else will do.

Rules live in ~/.config/furst/rules.toml and are matched in order. When a
rule's command is missing from $PATH the router falls through to the next
match, then to the default, so a config may name tools that do not exist
yet without breaking today.

- host patterns match apex plus subdomains, with = for exact and * for any
- paths glob case-insensitively, contains matches the raw URL
- {url} {url_enc} {host} {path} {scheme} placeholders, URL appended if unused
- terminal = true wraps a handler in $TERMINAL -e for TUI tools
- --explain shows the parse and every candidate in priority order
- --install registers a .desktop entry as the system default browser

Host parsing strips userinfo with rfind('@') so that
https://bank.example@evil.example/ routes on evil.example. Handlers are
exec'd rather than forked, leaving no process behind.
2026-09-06 19:18:13 +02:00

3.2 KiB
Raw Blame History

furst

A URL router. It sits where your default browser used to, and sends each URL to the cheapest tool that can actually handle it — mpv for video, a pager for PDFs, a light WebKit browser for reading, and Firefox only when nothing else will do.

Built for a Core2 Duo with 4GB of RAM, where the browser is the problem.

Why

A news page is 25MB across 80+ requests with 13MB of JavaScript to parse and JIT. The same article extracted is ~20KB. Choosing a lighter engine buys 23×; not loading the payload at all buys 10100×. furst is the dispatcher that decides which of those you get, per URL.

Install

cargo build --release
install -Dm755 target/release/furst ~/.local/bin/furst

furst --init      # writes ~/.config/furst/rules.toml, probes for a light browser
furst --install   # registers furst as the system default browser

--install writes ~/.local/share/applications/furst.desktop and points xdg-settings at it, so every link click in every application routes here.

Use

furst <url>              # match a rule and exec its command
furst --explain <url>    # show what would run, and why; run nothing
furst --list             # show the loaded rules

--explain is the one you want when a URL goes somewhere surprising:

$ furst --explain https://youtu.be/abc123
scheme  https
host    youtu.be
path    /abc123

-> [video] mpv --ytdl-format=bestvideo[vcodec^=avc1][height<=?720]+... https://youtu.be/abc123
   [default] surf https://youtu.be/abc123

Rules

~/.config/furst/rules.toml. First matching rule wins. If its command is missing from $PATH, furst falls through to the next matching rule, and finally to default — which is what lets you name tools you have not written yet and have the config stay working today.

default = ["surf", "{url}"]

[[rule]]
name = "video"
hosts = ["youtube.com", "youtu.be"]
run = ["mpv", "--ytdl-format=bestvideo[vcodec^=avc1][height<=?720]+bestaudio/best", "{url}"]

[[rule]]
name = "hn"
hosts = ["news.ycombinator.com"]
terminal = true                      # wrap in $TERMINAL -e
run = ["furst-hn", "{url}"]

A rule matches when every criterion it states is satisfied; a criterion is satisfied by any one of its patterns. A rule that states nothing matches everything.

Key Matches against
schemes https, mailto, magnet, …
hosts example.com = apex and every subdomain; *.example.com = same; =example.com = that host exactly; * = any
paths path component only, glob with *, case-insensitive
contains substring of the whole raw URL
Placeholder
{url} {url_enc} the URL, raw or percent-encoded
{host} {path} {scheme} parsed components

If no argument mentions {url} or {url_enc}, the URL is appended last.

Notes for old hardware

  • Force H.264 for video. A Core2 handles 720p avc1 in software but stalls on VP9/AV1, which is what YouTube serves by default. That format string is doing more work than the resolution cap.
  • Host matching strips userinfo with rfind('@'), so https://bank.example@evil.example/ routes on evil.example.
  • furst execs the handler rather than forking, so it leaves no process behind.

License

MIT