aboutsummaryrefslogtreecommitdiff
path: root/docs/feed-backed-news.md
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-08-23 12:44:53 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-08-23 12:44:53 -0700
commit68f615cc6e6ac177109db5b3be4f9767c9cfd1a8 (patch)
tree37380267c1356e67783e9273495bfa099048486c /docs/feed-backed-news.md
parent4df6b97311e321c1ef178efce905307b10ce6fa7 (diff)
Add feed-backed threaded news imports
Diffstat (limited to 'docs/feed-backed-news.md')
-rw-r--r--docs/feed-backed-news.md129
1 files changed, 129 insertions, 0 deletions
diff --git a/docs/feed-backed-news.md b/docs/feed-backed-news.md
new file mode 100644
index 0000000..8edcf79
--- /dev/null
+++ b/docs/feed-backed-news.md
@@ -0,0 +1,129 @@
+# Feed-backed threaded news
+
+Mobius can import a public RSS or Atom feed into an existing Hotline news
+category. This is useful for project announcements, Sparkle appcasts, and
+GitHub release feeds.
+
+When a user opens a mapped category, Mobius checks its source and adds entries
+it has not seen before as ordinary root articles. Imported articles live in
+`ThreadedNews.yaml`, alongside locally posted articles and replies. The category
+continues to work as ordinary Hotline news: authorized users can post roots,
+reply, and delete any article.
+
+## Configure a category
+
+First, create the target as an ordinary news category with a Hotline client.
+Every component in `CategoryPath`, including the final category, must already
+exist before the server starts.
+
+Then add a mapping to `config.yaml` and restart Mobius:
+
+```yaml
+NewsFeeds:
+ - CategoryPath: ["Software Updates", "Afterglow"]
+ URL: "https://morphing.cloud/afterglow/appcast.xml"
+
+ - CategoryPath: ["Software Updates", "Mobius"]
+ URL: "https://github.com/jhalter/mobius/releases.atom"
+```
+
+`CategoryPath` is the complete path from the root of threaded news to the
+existing category. `URL` must be an absolute public `http` or `https` URL and
+cannot contain embedded credentials. Only one feed may map to a category.
+
+Mobius detects RSS and Atom automatically. Sparkle appcasts are RSS; GitHub's
+`releases.atom` endpoints are Atom. JSON Feed and provider-specific APIs are not
+supported.
+
+## Import behavior
+
+Every article-list request for the mapped category performs a source check.
+Mobius sends the saved `ETag` and `Last-Modified` values when the source
+provides them, allowing an unchanged source to answer with a small `304 Not
+Modified` response. Concurrent requests for the same category share one
+in-flight check.
+
+On a successful response, Mobius:
+
+1. Finds entries not previously observed from that URL.
+2. Converts HTML descriptions to plain text and retains useful source, release
+ notes, and enclosure links.
+3. Imports new entries oldest-first as ordinary root articles.
+4. Saves the articles and import metadata together in one atomic update to
+ `ThreadedNews.yaml`.
+
+An entry needs a stable identity: a GUID or Atom ID, an HTTP(S) article link,
+or an HTTP(S) enclosure URL, in that order. Entries without any of these are
+skipped and logged. Mobius intentionally does not derive identities from titles
+or bodies because an edited entry could then be mistaken for a new article.
+
+Once an identity has been imported, later changes to that entry's title,
+author, date, or body are ignored. If an imported article is deleted locally,
+its identity remains in the seen set, so the feed does not recreate it. Items
+also remain in Hotline when they disappear from the source. This prevents a
+feed's rolling window from rolling articles out of the Hotline category.
+
+The initial import can only include entries returned by the source. Mobius does
+not paginate GitHub or reconstruct older releases that are already absent from
+its Atom feed.
+
+## Failures and limits
+
+A network error, timeout, non-success HTTP response, invalid feed, or failed
+disk write is logged. Mobius still returns the category's current local article
+list, including an empty list before the first successful import. A later
+category load tries the source again. Requests time out after 10 seconds and a
+response body may be at most 2 MiB.
+
+The Hotline protocol limits the complete encoded article-list payload for one
+category to 65,535 bytes. This listing contains article metadata—not article
+bodies—so the practical capacity depends mostly on the number and encoded
+length of titles and authors. Mobius checks the full list after each candidate
+article. It saves the oldest prefix that fits and stops before an addition
+would exceed the limit. It does not save new HTTP validators while unseen
+entries remain, so the next category load fetches and retries them. Mobius does
+not prune local or imported articles automatically; split a large source across
+categories when a category approaches this protocol limit.
+
+Individual imported titles and authors are limited to 255 encoded bytes, and
+article bodies to 65,535 encoded bytes, matching Hotline's field sizes.
+
+## Durable state and backups
+
+Feed history is part of each category in `ThreadedNews.yaml` under a YAML-only
+`FeedState` key. It records the current source URL, HTTP validators, and hashes
+of identities that have already been imported. This metadata is not sent to
+Hotline clients. Back up `ThreadedNews.yaml` as usual; there are no additional
+feed state or cache files.
+
+Changing a category's URL clears its HTTP validators but retains its seen
+history. Identity hashes include the source URL, so the new source can import
+its entries even if it uses the same GUID values. Switching back to a previous
+URL does not duplicate entries that URL imported earlier.
+
+The earlier experimental `FeedNewsState.yaml` and `FeedNewsCache.json` formats
+are not migrated or read. If they exist from a development build, Mobius
+ignores them; their contents do not participate in this feature.
+
+## Text encoding
+
+Feed parsers produce UTF-8 text. Mobius converts each entry once, when it is
+imported, according to the server-wide `Encoding` setting. The default
+`macintosh` setting converts to Mac Roman and replaces unsupported characters;
+`utf8` stores UTF-8 unchanged. Existing imported articles are ordinary raw-byte
+news data, so changing `Encoding` affects only future imports and does not
+rewrite history.
+
+## Troubleshooting
+
+- If startup fails, verify that every mapped path already exists and ends at a
+ news category rather than a bundle. Also check for duplicate paths and
+ invalid or credential-bearing URLs.
+- If new entries do not appear, open the category and inspect the server log.
+ Confirm that the URL is public RSS or Atom and that entries have stable IDs,
+ links, or enclosures.
+- If old releases are missing on the first import, inspect the source feed. Its
+ published window is the initial cutoff; Mobius does not use pagination or a
+ provider API.
+- If a source is unavailable, existing local news remains usable and the next
+ category load retries it.