This document is for people who run heron. For developers working on
it, see contributing.adoc.
# 1. Build (Zig 0.17.0 exactly)
zig build -Doptimize=ReleaseSafe # -> zig-out/bin/heron
# 2. Configure
cp examples/heron.json ~/.config/heron.json
$EDITOR ~/.config/heron.json # sources, monitors, web
heron --config ~/.config/heron.json check-config
# 3. Run
heron --config ~/.config/heron.json --data ~/.local/share/heron daemonHERON_CONFIG and HERON_DATA stand in for --config and --data.
With "web": {"enabled": true} the UI is at http://127.0.0.1:8484/.
heron fetch # poll every source once, now
heron list --unread --limit 20 # newest unread items
heron show 123 # print item 123 and mark it read
heron search sqlite # substring search (3+ characters) over titles and text
heron mark-all-read lwn # one source, or all sources with no nameAdd a monitor and run the daemon; the first successful check is a silent
baseline. A DOWN alert is raised after two consecutive failures, an UP alert
when it recovers, a change alert (at most one per hour) when the content hash
changes, and an expiry alert when the leaf certificate is within
cert_warn_days. Alerts are items of kind alert, so list and search
show them; heron monitors prints the current state per monitor.
The NDJSON log in the data directory is the source of truth; the SQLite
index is derived from it. heron rebuild rebuilds the index and the FTS5
search tables from the log. The daemon also replays any log records newer than
meta.indexed_offset on start, so an unclean shutdown needs no manual step.
One JSON file (no YAML). check-config rejects unknown keys at every level and values of the wrong JSON type (a quoted number or boolean is an error, not a default).
| Field | Type | Default | Meaning |
|---|---|---|---|
|
string |
|
Where the log, index and |
|
string |
built-in |
Sent on every request; put a contact address in it. |
|
integer |
30 |
Wall-clock timeout per request. |
|
boolean |
|
Serve the web UI from the daemon. |
|
string |
|
Bind address, numeric ( |
|
integer |
8484 |
Listening port. |
|
boolean |
|
Required to bind a non-loopback address; the UI has no authentication. |
|
string |
required |
Short unique name used by the CLI. |
|
string |
|
Adapter. Only |
|
string |
required |
http(s) URL. |
|
integer |
per adapter |
Poll interval; jitter, backoff and |
|
object |
|
Adapter-specific options (reserved for future adapters). |
|
string |
required |
Short unique name. |
|
string |
required |
Page to check. |
|
integer |
per adapter |
Check interval. |
|
integer |
200 |
HTTP status that counts as UP. |
|
boolean |
|
Fetch the body (GET), hash its visible text and alert on change (rate-limited to one alert per hour). |
|
boolean |
|
Fetch and honour |
|
integer |
14 |
Alert when the leaf certificate expires within this many days, or has expired. |
examples/heron.json is a complete, valid example.
-
Stable (pre-1.0, best effort): the CLI command names above, the JSON config keys above, the NDJSON log format (it is the source of truth; the index can always be rebuilt from it).
-
Unstable: the web UI’s URLs and markup;
sources[].options. -
Internal: the SQLite schema. Rebuild it rather than reading it.
-
Linux x86_64 only so far; no soak test against live sites has been run.
-
searchneeds at least three characters (trigram index) and has no pagination yet. -
The certificate probe has been verified only against a self-signed certificate (not CA-signed, SNI-routed or TLS 1.2-only hosts).
-
No WebSub; feeds are polled.
-
Relative entry links are resolved against the feed URL;
xml:baseis not consulted. -
The web UI serves one request at a time with a 10 s per-connection deadline; it is a personal UI, not a public server.
-
Opening an item in the web UI marks it read only when the browser sends Fetch Metadata saying the navigation came from the UI itself or was typed by you (
Sec-Fetch-Site: same-originornone,Sec-Fetch-Mode: navigate; every current browser does). Otherwise, for example from an older browser or a link on another site, the item stays unread and the page shows a Mark read button. A prefetch or prerender (Sec-Purpose: prefetch) of an item page is declined with 503, so only the real visit marks it read.
-
Architecture — how it works internally.
-
Troubleshooting — when things go wrong.
-
Roadmap — what is next and what is untested.