Read is a tailnet-only feed reader and read-later list. It polls RSS, Atom
and JSON Feed sources, saves links to read later, and shows each article as
its sanitized text. It runs behind
lodge, frostyard's tailnet-only web
shell, as app read, for the people on the tailnet.
Status: pre-release.
Documentation: docs/README.md
- Sources. RSS, Atom and JSON Feed, by URL or from a page's
<link rel="alternate">, imported and exported as OPML. Each source polls every 20 minutes or so, with conditional GET and a per-source backoff (fetching). - Read later. Save a link from the app, from
readctl save <url>, or from an agent with aread;writetoken (methods). - From your phone. A bookmarklet opens Read's save page with the page's address and title, and Read saves only when you tap Save, so it works in iOS Safari and needs no token (ADR-0006).
- Articles. The main content is extracted and sanitized, and its images are fetched into a content-addressed store and inlined, so the browser loads nothing remote. The original bytes are kept as sent (ADR-0005).
- Reading. Unread, starred and archived views, full-text search, and
permalinks at
/read/item/<id>(web routes). - Save as a note. A reader button writes the article into notes under
Reading/, through notes' MCP on lodge's agent socket (ADR-0004). - Agents. Any agent reaches it at
/mcp/readwith a scoped token, and the engine enforces the token's scope (MCP). - A small state. A daily mark-and-scan sweep deletes the blobs that no item uses, after a grace period (ADR-0007).
Read runs on lodge's host, beside lodge and notes: it is lodge's read
app, installed from a release's frostyard-readd .deb as
/usr/bin/readd under the system user frostread, and lodge serves its web
bundle at /app/read/. A change reaches it as a vX.Y.Z tag that
make bump CANDIDATE=vX.Y.Z-rc.N pushes on a tested candidate's commit:
the tag's workflow builds, attests and publishes the .deb, and the host
installs it by hand. The runbooks are in
packaging/README.md:
install on lodge's host,
update,
roll back,
after the sysexts and
release.
| Socket or route | What it does |
|---|---|
/run/lodge/apps/read.sock |
lodge's app socket: /health, /api, /content/ and /mcp |
/run/frostread/readctl.sock |
readctl, the CLI: readd's private socket |
/app/read/ |
the web app, which lodge serves |
CONTENT/read/item/<id>/<mac> |
an article's content page: a MAC under the daemon's secret is the only permission |
/mcp/read |
MCP, with a scoped token |
It runs as user frostread, in groups lodge-apps and lodge-agents, and
listens on no network. Its state is /var/lib/frostread, and its
configuration is /etc/frostread/frostread.json. The
API spec is the contract: the JSON-RPC methods, the
MCP tools and scope, the content routes, readctl and the state layout.
readd and the desktop app record their panics, and readd 5 coded internal errors, in the local systemd journal: a migration that fails, a second readd on lodge's host, an extraction or a render that fails in readd, and a blob sweep that fails. frostread sends nothing over a network for them. Only chairlift, when its user turns reporting on, sends reports from a host. Only a release build records. See docs/specs/problem-records.md.
mise install # the pinned golangci-lint, svu and nFPM
make check # format, then tidy, vet, lint and the Go tests
make ci # verify, the race detector, the cross builds, the web bundle and the app (nsl)
make dev-lodge # lodge's development build on loopback, with Read and Notes
make web-build # the web bundle, in an nsl machineGo 1.27 builds on the host; the web bundle and the desktop app build in the
nsl machine, never on the host. make help lists every target, and
AGENTS.md has the gates in full.
- AGENTS.md: the rules for agents and contributors.
- docs/README.md: the decisions, designs, specs and plans.
- SECURITY.md: report a vulnerability privately.
- LICENSE: MIT.