A newspaper belt around Hermes.
It takes messy public feeds, checks the delivery route, turns them into plain text, stores them safely, and slides the headlines past your statusbar.
Fresh headlines are easy. Fresh headlines you can trust are not.
A feed can be malformed, duplicated, slow, oversized, redirected, or hostile. The interface still needs to feel calm when one source breaks.
Imagine a receiving dock
Trucks arrive from many roads. The dock checks the destination, unloads only the expected package, removes dangerous wrapping, and puts a clean label on the shelf.
What Newswire actually does
The backend owns the risky work. The renderer gets normalized JSON through ctx.rest; it never fetches feeds or renders feed HTML.
Every story follows one narrow path.
The value is the sequence. Each stage removes a different kind of uncertainty before the next stage receives the data.
The renderer is deliberately late in the chain: by the time a headline reaches it, markup and network decisions are already gone.
DNS can change its answer. The socket cannot change its mind.
A normal “validate, then let the HTTP client connect” pattern can be tricked by DNS rebinding. Newswire resolves and checks the address set, then pins that exact set into the transport.
Feeds disagree about what “the same story” means.
Newswire does not trust one identifier. It walks down a ladder, from the feed’s strongest identity signal to a content fingerprint.
Every step is useful because real feeds omit, mutate, or decorate different fields.
- ANormalize at the boundaryDates become UTC ISO strings; summaries are capped; markup is removed before the UI sees it.
- BRemember the decisionSQLite keeps source validators, read state, errors, and articles so the next refresh can be cheap and explainable.
- CBound the worst caseA giant feed can contain many items, but one refresh inserts no more than 100 new rows.
When a feed gives a stable GUID, it is the cleanest identity signal. The other checks remain defensive fallbacks.
Offline is a state, not a surprise.
The cache keeps the ticker alive. Conditional requests avoid re-downloading unchanged feeds. Errors stick to the source that caused them instead of becoming a global outage.
Never-parsed 304
If a source has zero articles and has never successfully parsed, Newswire drops stale validators and retries. “Not modified” cannot stand in for “we have a usable cache.”
Retention-pruned 304
If the source was parsed before but old rows were pruned, it keeps the fast path and explains that the visible list is empty because of retention.
The UI is small because the boundaries did the heavy work.
The ticker is a real layout split above the statusbar. The full page adds just enough control to manage attention: search, source grouping, read state, discovery, settings, and preview.
Passing tests is evidence, not a security certificate.
The repository’s tests are deliberately offline fixtures. They are strong evidence for the seams they exercise—especially SSRF, parsing, retention, and registration—but they do not prove every live feed, browser, deployment, or future dependency version.
What was checked here
119 pytest cases passed; `node --check` passed; the ESM render smoke passed 26 checks; Hermes plugin validation returned ok: true with no warnings.
What remains open
Live feed behavior, production desktop layout, catalog admission, network conditions, and a complete independent security audit still need their own evidence.
Make the boring safeguards visible before adding cleverness.
The reusable lesson is an operating-system pattern: constrain the dangerous edges, normalize once, preserve state, and keep the experience useful under partial failure.
Gate
Make untrusted inputs pass one explicit policy boundary.
Normalize
Convert many formats into one safe internal shape.
Remember
Persist validators, state, and errors so recovery is explainable.
Isolate
Let one bad dependency fail locally, not globally.
Verify
Test adversarial seams and label what remains unproven.