servo-reader (sr)
Read any website in your terminal as clean, rendered markdown — using the real Servo browser engine.
Lynx-grade weight, modern-engine fidelity. Unlike lynx/w3m, JS-rendered sites
work (a real layout + JS engine loads them); unlike a full browser, the output is
calm wrapped markdown in your terminal — no pixels, no Chromium.
Tiered fetch keeps it fast: most pages are read over a plain HTTP GET in under a second with no engine at all — Servo is only spun up when a page comes back thin / JS-gated. So the giant backend is the fallback, not the floor.
sr --links https://servo.org/about/
sr https://news.ycombinator.com
sr en.wikipedia.org/wiki/Servo_(software)
sr --raw servo.org/about/ | glow - # pipe raw markdown elsewhere
sr --width 100 --no-pager some.blog/post
How it works
URL ─▶ Tier 1: HTTP GET ──── rich content? ──yes──┐
│ │
└─ thin / JS-gated / failed? │
└▶ Tier 2: Servo (headless servoshell + WebDriver)
navigate, settle, post-JS DOM ─┤
▼
servo_agent.distill (trafilatura / markdownify) ─▶ markdown
└─ servo_reader.render (zero-dep ANSI + OSC-8 links) ─▶ terminal
--http forces the cheap path (instant, no engine); --servo forces the engine;
default auto does Tier 1 → Tier 2. The footer shows which tier won (via http /
via servo).
It stands on servo-agent
for the engine plumbing and the distiller, and adds a tiny terminal markdown
renderer (no third-party deps — that is the whole point of a light reader).
Install / run
This is a uv project. It is not published on PyPI.
servo-agent is pulled straight from GitHub, so a fresh clone builds standalone:
git clone https://github.com/parker-brown-family/servo-reader
cd servo-reader
uv sync
uv run sr --links https://servo.org/about/
To put sr on your PATH instead, install it as a uv tool:
uv tool install git+https://github.com/parker-brown-family/servo-reader
Co-developing against a local
servo-agentcheckout? Override the source inpyproject.tomlwithservo-agent = { path = "../servo-agent", editable = true }.
The Servo engine
Pages that come back thin over HTTP are rendered with Servo, which needs a built
servoshell binary (Servo's shell). servo-reader looks for one in this order:
$SERVOSHELL, the path to a binaryservoshellonPATH- a built
servo/checkout beside servo-reader (servo/target/debug/servoshellorservo/target/release/servoshell)
export SERVOSHELL=/path/to/servo/target/debug/servoshell # a local build, or
export SERVO_WEBDRIVER=host:port # an already-running engine
Using $SERVO_WEBDRIVER against a warm engine skips the per-call browser launch
and is much faster than cold-starting a debug servoshell each time. See
Servo's build docs to
produce the binary.
Most reads never touch the engine. Without one, a page that comes back thin over
HTTP still prints its HTTP text, with a note on stderr that Servo rendering was
skipped, and --servo exits with an error. Pass --http to stay on the cheap
path always.
Options
| Flag | Meaning |
|---|---|
-L, --links |
append a numbered index of the page's links |
-l N, --follow N |
follow link N from the last page read |
-b, --back / -f, --forward |
move through visit history |
--history |
list recent pages and exit |
--images auto|kitty|sixel|off |
inline images (default auto-detect; needs [images] extra) |
--http |
force the cheap HTTP fetch (instant, no engine) |
--servo |
force the Servo engine (renders JS) |
--engine auto|http|servo |
fetch strategy (default auto: HTTP → Servo fallback) |
--raw |
emit raw markdown, no ANSI (great for piping) |
--no-color |
layout without color |
--width N |
wrap width (default: terminal, capped at 100) |
--max-chars N |
hard cap on extracted text (default 100k) |
--no-settle |
skip the post-load quiet wait (faster, static pages) |
--no-pager |
never page through less |
--timeout S |
navigation timeout |
Browsing — follow links
Every read remembers the page's links. List them with --links, then jump:
sr --links en.wikipedia.org/wiki/Servo_(software) # numbered link index
sr -l 4 # follow link 4
sr -l 12 # …and keep going (the new
# page's links are saved too)
Reads are also recorded in a back/forward history with browser semantics:
sr --history # list visited pages (▶ marks where you are)
sr --back # previous page
sr --forward # forward again (a new read from a back-position re-branches)
State lives in $XDG_STATE_HOME/servo-reader/ (last.json links, history.json
stack) — small files, no daemon required for navigation.
Images (kitty / sixel)
Optionally render a page's figures inline:
uv sync --extra images # in a clone; adds Pillow, the only extra dependency
# or, as a uv tool:
uv tool install 'servo-reader[images] @ git+https://github.com/parker-brown-family/servo-reader'
sr --images auto en.wikipedia.org/wiki/Servo_(software)
auto detects the terminal's graphics protocol and stays off when it can't be
sure — so it never garbles a terminal that can't decode graphics. Supported:
kitty / WezTerm / Ghostty (kitty protocol) and foot / xterm-with-sixel /
Black Box (sixel). Force one with --images kitty|sixel.
Images are fetched only from http/https hosts with public addresses, so an
image served from localhost or a private network shows the placeholder instead
(see SECURITY.md).
Note: terminals on the alacritty backend — including terminal-delight — support neither protocol, so images won't display there (you'll see a
🖼 alt-textplaceholder instead). Use a kitty/sixel terminal for the image lane.
Warm engine (skip the cold start)
Engine-tier reads cold-start a servoshell (~2s). Run one persistent engine and
every read attaches to it automatically:
sr-engine start # launch a headless servoshell, register its WebDriver port
sr-engine status # running? where?
sr ... # engine reads now attach to the warm one (no env export needed)
sr-engine stop
sr discovers the daemon via the same state dir; you can still override with
$SERVO_WEBDRIVER to point at any engine. (Measured: an engine read drops from
~2.4s cold to ~1.3s warm.)
Status
The package version is 0.4.0, which is what sr --version prints. Nothing has
been tagged or released, so install from a clone or from the git URL above. It
covers the tiered fetch, link following with back/forward history, the
warm-engine daemon, and inline images. Pillow, for the image lane, is the only
optional dependency.
Metadata
Release files for servo-reader 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| servo_reader-0.4.0.tar.gz | 31.4 kB | With provenance | Details for servo_reader-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| servo_reader-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for servo_reader-0.4.0-py3-none-any.whl |
Total release size: 58.2 kB
Release files / servo_reader-0.4.0.tar.gz
| Download URL | servo_reader-0.4.0.tar.gz |
|---|---|
| Size | 31.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
94627e967fa75a7b9d30010a39b01a0335749b96726b67fd92582eb48a42a6b6
|
|
BLAKE2b-256 checksum How to use checksums |
8dfc38df417e05576d8b4cb9d550dee373575baf15c3226fb7ee792dbc6420f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logRelease files / servo_reader-0.4.0-py3-none-any.whl
| Download URL | servo_reader-0.4.0-py3-none-any.whl |
|---|---|
| Size | 26.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
231957b8efa67a87284a422d34d9b788f0a034d0beb28ba9188e9324b0a0c236
|
|
BLAKE2b-256 checksum How to use checksums |
958a68419c74183431403368bbf35a774e6e2e5105c966ed106e60715b6a13e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logSecurity for servo-reader 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for servo_reader-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.