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.
103 lines
3.2 KiB
Markdown
103 lines
3.2 KiB
Markdown
# 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 2–5MB across 80+ requests with 1–3MB of JavaScript to parse and
|
||
JIT. The same article extracted is ~20KB. Choosing a lighter *engine* buys
|
||
2–3×; not loading the payload at all buys 10–100×. `furst` is the dispatcher
|
||
that decides which of those you get, per URL.
|
||
|
||
## Install
|
||
|
||
```sh
|
||
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
|
||
|
||
```sh
|
||
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.
|
||
|
||
```toml
|
||
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` `exec`s the handler rather than forking, so it leaves no process
|
||
behind.
|
||
|
||
## License
|
||
|
||
MIT
|