From 2b017197d0ed3b38e0bbeb07448744033e7b9bf0 Mon Sep 17 00:00:00 2001 From: nak0x Date: Sun, 6 Sep 2026 23:19:34 +0200 Subject: [PATCH] Add a usage guide and an install script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GUIDE.md covers the parts a README does not: what happens when you click a link, making the proxy stick, the handful of commands worth knowing, and what to do when a page comes out wrong — which it will, since extraction is heuristic. install.sh builds the workspace, installs the four binaries, writes starter configs, fetches the blocklists, sets up a systemd user service for the proxy, and offers to register furst as the default browser. Every system-changing step can be skipped, and the default-browser change is prompted rather than assumed; a non-interactive run declines it. Re-running never overwrites a config that already exists, so it is safe after editing rules. --uninstall removes binaries, service and desktop entry while keeping configs, and --purge takes those too. The script passes --workspace to cargo, which is the mistake to avoid here: the root package is only furst, so a bare build silently skips the other three binaries. --- GUIDE.md | 225 ++++++++++++++++++++++++++++++++++++++++++++ install.sh | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 497 insertions(+) create mode 100644 GUIDE.md create mode 100755 install.sh diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..9f6a1dc --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,225 @@ +# Using furst + +A practical walkthrough. For what each piece *is*, see [README.md](README.md) +and the per-tool READMEs. + +--- + +## Install + +```sh +./install.sh +``` + +That builds the workspace, installs four binaries to `~/.local/bin`, writes +starter configs, fetches the blocklists, and offers to register furst as your +default browser. It is safe to re-run; it never overwrites a config you have +edited. + +``` +./install.sh --help # all options +./install.sh --prefix /usr/local/bin +./install.sh --no-default-browser --no-service +./install.sh --uninstall +``` + +Then install the handlers the rules refer to: + +```sh +sudo pacman -S mpv yt-dlp zathura zathura-pdf-mupdf nsxiv surf +``` + +Nothing breaks if you skip some — a rule whose command is missing falls through +to the next one. `furst --list` shows which are absent. + +--- + +## What happens when you click a link + +Nothing to launch. The router is your default browser, and **the reader starts +itself on demand.** Only the proxy runs as a service. + +``` +click a link, anywhere + │ + ▼ + furst ─── youtube, vimeo, twitch ──► mpv, H.264 forced, no browser at all + │ *.pdf ──► zathura + │ *.png *.jpg *.gif ──► nsxiv + │ meet, figma, netflix ──► firefox + │ mailto: magnet: ──► xdg-email, transmission + │ + └────── everything else ────────► furst-serve ──► surf + (reader, minimal HTML) +``` + +Every reader page has a bar: **refresh · original · browser**. Links inside an +article stay in the reader. When a page genuinely needs the real web, click +**browser** and it opens in Firefox. + +Two URLs worth bookmarking: + +| | | +|---|---| +| http://127.0.0.1:7714/ | reader home — your links, feeds, search box | +| http://furst.proxy/ | proxy status — rules, requests, share blocked | + +--- + +## Making the proxy stick + +The proxy is the one piece that must already be running. `install.sh` sets up a +systemd user service for it: + +```sh +systemctl --user status furst-proxy +``` + +Programs find it through the environment, so add this to `~/.zshrc`: + +```sh +eval "$(furst-proxy --env)" +``` + +`surf`, `curl`, `wget` and most CLI tools read those variables. **Firefox does +not** — set its proxy manually in *Settings → Network Settings → Manual*, to +`127.0.0.1` port `8228`, and tick *Also use this proxy for HTTPS*. + +Check it is working: `curl -s -x http://127.0.0.1:8228 http://furst.proxy/` +should return the status page. + +--- + +## Commands worth knowing + +```sh +furst --explain # why did that open there? ← the one you'll use most +furst --list # every rule, and what isn't installed + +furst-read --text # article as plain text; pipe it to less +furst-read --html # article as minimal HTML + +furst-serve --status # is the reader running? +furst-serve --clear # drop cached pages + +furst-proxy --test # would this host be blocked? +furst-proxy --update # refresh blocklists (weekly is automatic) +``` + +`furst --explain` is the habit that makes everything else legible: + +``` +$ 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 + [reader] furst-serve --open https://youtu.be/abc123 + [default] surf https://youtu.be/abc123 +``` + +The arrow marks what will actually run. Anything above it that is missing from +`$PATH` is skipped, and marked as such. + +--- + +## Fixing things + +Extraction is heuristic, so some pages will come out wrong. In order of effort: + +**An article looks truncated or empty.** Click **original** in the bar, or add +`&fresh=1` to the URL to bypass the cache. + +**A site consistently extracts badly.** Look at what it kept: + +```sh +curl -s https://site.example/article | furst-read --stdin --text https://site.example/article +``` + +The knobs are `POSITIVE` and `NEGATIVE` in `furst-read/src/extract.rs` — lists +of substrings matched against `class` and `id`. Adding the site's content +wrapper class to `POSITIVE` usually fixes it. Rebuild and reinstall with +`./install.sh`. + +**A site should never go to the reader.** Add its host to the `heavy` rule in +`~/.config/furst/rules.toml`. + +**Something legitimate is blocked.** Confirm it, then allow it: + +```sh +furst-proxy --test cdn.example.com +# add to ~/.config/furst/proxy.toml: +# allow = ["cdn.example.com"] +systemctl --user restart furst-proxy +``` + +Allow rules win over everything, at any subdomain depth. + +**Search says the engine refused.** DuckDuckGo rate-limits repeat visitors. +Point `[search] url` in `~/.config/furst/home.toml` at a SearXNG instance: + +```toml +[search] +url = "https://searx.be/search?q={q}" +``` + +**A page renders blank in the reader.** It is probably client-rendered. There is +no JavaScript here by design — use **browser**. + +--- + +## Tuning the rules + +`~/.config/furst/rules.toml` is matched top to bottom, first match wins, and a +rule whose command is missing falls through to the next. + +```toml +[[rule]] +name = "my-forum" +hosts = ["forum.example.com"] +run = ["furst-serve", "--open", "{url}"] + +[[rule]] +name = "hn-in-terminal" +hosts = ["news.ycombinator.com"] +terminal = true # wraps in $TERMINAL -e +run = ["my-tui-client", "{url}"] +``` + +| host pattern | matches | +|---|---| +| `example.com` | the apex **and** every subdomain | +| `*.example.com` | the same thing | +| `=example.com` | that exact host only | +| `*` | anything | + +Placeholders: `{url}` `{url_enc}` `{host}` `{path}` `{scheme}`. If no argument +mentions `{url}` or `{url_enc}`, the URL is appended last. + +Order matters — put specific rules above the catch-all `reader` rule at the +bottom. + +--- + +## Where things live + +| | | +|---|---| +| `~/.config/furst/rules.toml` | routing rules | +| `~/.config/furst/home.toml` | reader home links, feeds, search engine | +| `~/.config/furst/proxy.toml` | proxy port, blocklists, block/allow | +| `~/.cache/furst/pages` | rendered pages — `furst-serve --clear` | +| `~/.cache/furst/lists` | blocklists, refreshed weekly | +| `~/.local/share/applications/furst.desktop` | the default-browser registration | + +--- + +## Ports + +| | | +|---|---| +| 7714 | reader (`FURST_PORT`) | +| 8228 | proxy (`port` in `proxy.toml`) | + +Both bind `127.0.0.1` only. Neither is a network service; do not expose them. diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..10c0e5e --- /dev/null +++ b/install.sh @@ -0,0 +1,272 @@ +#!/usr/bin/env bash +# +# Build and install the furst toolset. Safe to re-run: it never overwrites a +# config you have edited, and every system-changing step can be skipped. +# +# ./install.sh --help + +set -euo pipefail + +PREFIX="${HOME}/.local/bin" +CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/furst" +CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/furst" +UNIT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user" +DESKTOP="${XDG_DATA_HOME:-$HOME/.local/share}/applications/furst.desktop" + +BINARIES=(furst furst-read furst-serve furst-proxy) +HANDLERS=(mpv yt-dlp zathura nsxiv surf firefox) + +MODE=install +DO_BUILD=1 +DO_LISTS=1 +DO_SERVICE=1 +DO_BROWSER=ask +PURGE=0 + +if [ -t 1 ]; then + B=$'\033[1m'; DIM=$'\033[2m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'; RED=$'\033[31m'; R=$'\033[0m' +else + B=; DIM=; GREEN=; YELLOW=; RED=; R= +fi + +step() { printf '\n%s==>%s %s%s%s\n' "$GREEN" "$R" "$B" "$1" "$R"; } +info() { printf ' %s\n' "$1"; } +skip() { printf ' %s%s%s\n' "$DIM" "$1" "$R"; } +warn() { printf ' %s!%s %s\n' "$YELLOW" "$R" "$1"; } +die() { printf '\n%serror:%s %s\n' "$RED" "$R" "$1" >&2; exit 1; } + +usage() { + cat <<'EOF' +install.sh — build and install the furst toolset + +USAGE + ./install.sh [OPTIONS] + +OPTIONS + --prefix where to install binaries (default ~/.local/bin) + --no-build install from an existing target/release build + --no-lists skip downloading the proxy blocklists + --no-service skip the systemd user service for the proxy + --no-default-browser do not register furst as the default browser + -y, --yes answer every prompt with yes + --uninstall remove binaries, service and desktop entry + --purge with --uninstall, also remove configs and caches + -h, --help this + +The install never overwrites an existing config file, so re-running it after +editing your rules is safe. +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --prefix) PREFIX="${2:?--prefix needs a directory}"; shift 2 ;; + --no-build) DO_BUILD=0; shift ;; + --no-lists) DO_LISTS=0; shift ;; + --no-service) DO_SERVICE=0; shift ;; + --no-default-browser) DO_BROWSER=no; shift ;; + -y|--yes) DO_BROWSER=yes; shift ;; + --uninstall) MODE=uninstall; shift ;; + --purge) PURGE=1; shift ;; + -h|--help) usage; exit 0 ;; + *) die "unknown option $1 (--help for usage)" ;; + esac +done + +# ---------------------------------------------------------------- uninstall --- + +if [ "$MODE" = uninstall ]; then + step "Removing the service" + if command -v systemctl >/dev/null 2>&1; then + systemctl --user disable --now furst-proxy.service 2>/dev/null || true + rm -f "$UNIT_DIR/furst-proxy.service" "$UNIT_DIR/furst-serve.service" + systemctl --user daemon-reload 2>/dev/null || true + info "stopped and removed" + else + skip "no systemctl" + fi + + step "Removing binaries" + for b in "${BINARIES[@]}"; do + if [ -e "$PREFIX/$b" ]; then rm -f "$PREFIX/$b"; info "removed $PREFIX/$b"; fi + done + + step "Removing the desktop entry" + rm -f "$DESKTOP" + info "removed $DESKTOP" + warn "your default browser is now unset — pick one in your desktop settings," + warn "or run: xdg-settings set default-web-browser firefox.desktop" + + if [ "$PURGE" = 1 ]; then + step "Purging configs and caches" + rm -rf "$CONFIG_DIR" "$CACHE_DIR" "${XDG_CACHE_HOME:-$HOME/.cache}/furst-read" + info "removed $CONFIG_DIR and caches" + else + step "Kept" + info "$CONFIG_DIR and caches — remove with --uninstall --purge" + fi + printf '\n%sUninstalled.%s\n\n' "$B" "$R" + exit 0 +fi + +# ------------------------------------------------------------------- build --- + +cd "$(dirname "$0")" + +if [ "$DO_BUILD" = 1 ]; then + step "Building" + command -v cargo >/dev/null 2>&1 || die "cargo not found — install rust first" + info "$(cargo --version)" + # --workspace matters: the root package is only furst, so a bare build would + # silently skip the other three. + cargo build --release --workspace + info "built 4 binaries" +else + step "Skipping build" +fi + +for b in "${BINARIES[@]}"; do + [ -x "target/release/$b" ] || die "target/release/$b missing — run without --no-build" +done + +# ----------------------------------------------------------------- install --- + +step "Installing to $PREFIX" +mkdir -p "$PREFIX" +for b in "${BINARIES[@]}"; do + install -m755 "target/release/$b" "$PREFIX/$b" + info "$b $(du -h "$PREFIX/$b" | cut -f1)" +done + +case ":$PATH:" in + *":$PREFIX:"*) ;; + *) warn "$PREFIX is not on your \$PATH — add this to ~/.zshrc:" + warn " export PATH=\"$PREFIX:\$PATH\"" ;; +esac + +export PATH="$PREFIX:$PATH" + +# ----------------------------------------------------------------- configs --- + +step "Writing starter configs" +mkdir -p "$CONFIG_DIR" +init_config() { + local file="$1" cmd="$2" + if [ -e "$CONFIG_DIR/$file" ]; then + skip "$file already exists, left alone" + else + "$PREFIX/$cmd" --init >/dev/null && info "$file" + fi +} +init_config rules.toml furst +init_config home.toml furst-serve +init_config proxy.toml furst-proxy + +# ---------------------------------------------------------------- handlers --- + +step "Checking handlers" +missing=() +for h in "${HANDLERS[@]}"; do + command -v "$h" >/dev/null 2>&1 || missing+=("$h") +done +if [ ${#missing[@]} -eq 0 ]; then + info "all present" +else + warn "not installed: ${missing[*]}" + warn "rules naming them fall through to the next handler; install with:" + warn " sudo pacman -S mpv yt-dlp zathura zathura-pdf-mupdf nsxiv surf" +fi + +# --------------------------------------------------------------- blocklists --- + +if [ "$DO_LISTS" = 1 ]; then + step "Fetching blocklists" + if "$PREFIX/furst-proxy" --update 2>&1 | sed 's/^/ /'; then :; else + warn "could not fetch — the proxy will run with only its built-in rules" + fi +else + step "Skipping blocklists" + skip "run 'furst-proxy --update' later" +fi + +# ------------------------------------------------------------------ service --- + +if [ "$DO_SERVICE" = 1 ] && command -v systemctl >/dev/null 2>&1; then + step "Installing the proxy service" + mkdir -p "$UNIT_DIR" + cat > "$UNIT_DIR/furst-proxy.service" < "$UNIT_DIR/furst-serve.service" </dev/null || true + if systemctl --user enable --now furst-proxy.service 2>/dev/null; then + info "furst-proxy.service enabled and started" + else + warn "could not start the service — run it yourself with: furst-proxy &" + fi + skip "furst-serve.service written but not enabled (the reader autostarts)" +else + step "Skipping the service" + skip "start the proxy yourself: furst-proxy &" +fi + +# ---------------------------------------------------------- default browser --- + +step "Default browser" +if [ "$DO_BROWSER" = ask ]; then + if [ -t 0 ]; then + printf ' Register furst as your default browser? [y/N] ' + read -r reply + case "$reply" in [yY]*) DO_BROWSER=yes ;; *) DO_BROWSER=no ;; esac + else + DO_BROWSER=no + fi +fi + +if [ "$DO_BROWSER" = yes ]; then + "$PREFIX/furst" --install 2>&1 | sed 's/^/ /' +else + skip "not changed — run 'furst --install' when you want it" +fi + +# -------------------------------------------------------------------- done --- + +cat < Network Settings > Manual, + 127.0.0.1 port 8228, and tick "Also use this proxy for HTTPS". + + Reader home http://127.0.0.1:7714/ + Proxy status http://furst.proxy/ + + Read ${B}GUIDE.md${R} next. The command to remember is: + furst --explain + +EOF