A persistent RSS reader for the Omarchy shell, built as a Quickshell plugin.
With Omarchy and the Go version specified in go.mod installed:
git clone https://github.com/KitsuneSemCalda/Feader-RSS.git
cd Feader-RSS
./scripts/install.shThe installer validates the manifest, builds the backend in a staging
directory, and copies the required files to
~/.config/omarchy/plugins/io.github.kitsunesemcalda.feader-rss/, then enables
the widget in the right bar section. It preserves your feed configuration and
creates a timestamped backup of the configuration, UI preferences, and SQLite
article database, then keeps the active article cache so read/unread state
persists across updates. If the shell does not detect the copy immediately, run
omarchy restart shell.
Backups are stored separately in
~/.local/state/omarchy/rss-reader/backups/. To create one manually, run
./scripts/backup.sh.
Each backup contains rss-reader.json, preferences.json, and an items.db
snapshot when those files exist. The SQLite snapshot uses SQLite's online
backup API, so committed data in the WAL is included and no -wal/-shm
sidecars need to be copied. Older backups containing only items.json remain
accepted for compatibility.
To restore a backup, run ./scripts/restore.sh /path/to/backup; the current
data is backed up first, and an items.db snapshot replaces the active
database even when it already contains articles.
omarchy plugin add copies the repository but does not run an install
hook or build feader-rss-fetch. Use the installation steps above to
install both the plugin and its backend.
Without Go, the installer downloads a release binary and verifies its
checksum and attestation using gh. For a Git checkout, the binary must
match that checkout's commit; an unreleased checkout may have no matching
binary.
The runtime requires Omarchy's Quattro shell and network access to the
configured RSS/Atom feeds. It runs with the user's permissions inside the
long-running shell process. The plugin invokes the feader-rss-fetch Go
binary (built locally or downloaded as a checksum-verified release asset by
scripts/install.sh), creates the state directory with mkdir, opens article
URLs in the browser, and may call omarchy-notification-send for new posts.
No elevated privileges, background service, or remote build is required.
Create ~/.config/omarchy/rss-reader.json:
{
"maxFeeds": 8,
"maxItems": 200,
"retentionItems": 1000,
"refreshMinutes": 5,
"feeds": []
}maxFeeds is how many feeds you can configure (default 8, at most 100); it
is also editable under "Feed limit" in the settings page, and OPML imports are
capped by it. maxItems is the most articles the panel loads. The list loads
50 at a time and fetches the next page as you scroll toward the bottom, up to
maxItems. retentionItems
controls how many articles are kept in SQLite; use 0 to keep everything.
Read and unsaved articles are pruned before unread or saved articles.
refreshMinutes accepts 1, 2, 3, 4, 5, 15, 30, 60, or 300.
The panel exposes 1 min, 5 min, 15 min, 30 min, 1 hour, and 5 hours;
the default is to refresh feeds every 5 minutes. The longer intervals are useful
for feeds that publish less often and reduce network traffic.
You can also open the reader and choose Configure feeds to edit the feed
name, optional folder, and URL without leaving the application. The
configuration screen supports adding and removing feeds, validation of
http/https URLs, keyboard focus, arrow-key navigation, Enter, Tab, and
Escape. Press s from the reader to open it directly. Duplicate feed URLs
are rejected.
OPML subscription lists can be exchanged with the Go helper. Import replaces
the configured feeds by default; pass --merge to append only new URLs:
~/.config/omarchy/plugins/io.github.kitsunesemcalda.feader-rss/feader-rss-fetch \
opml-export --config ~/.config/omarchy/rss-reader.json --output subscriptions.opml
~/.config/omarchy/plugins/io.github.kitsunesemcalda.feader-rss/feader-rss-fetch \
opml-import --config ~/.config/omarchy/rss-reader.json --input subscriptions.opml --mergeWhile the reader has focus: ↑/↓ moves the selection, Enter opens the
selected article, ← returns to the list from an open article, and Tab
switches to the next bar panel. The single-key shortcuts below can be
remapped with an optional shortcuts object in the config file — any subset
of actions can be overridden, unset ones keep their default:
{
"shortcuts": {
"refresh": "r",
"settings": "s",
"search": "/",
"markAllRead": "a",
"openArticle": "o",
"filterAll": "1",
"filterUnread": "2",
"filterRead": "3",
"filterStarred": "4"
}
}refresh re-fetches all feeds, settings opens Configure feeds, search
focuses the search field, markAllRead prompts to mark every article read,
openArticle opens the selected (or currently open) article in the browser,
and filterAll/filterUnread/filterRead/filterStarred switch the
read-state or saved filter.
Each value must be a single character; the current bindings are always shown
at the bottom of the article list.
The reader provides full-text search across titles, summaries, cached article content, authors, categories, and user tags; filters for all/read/unread/saved articles; and feed/folder filters. Saved articles and tags can be edited from the article detail view. These preferences are stored with the article state and restored when the reader starts. Failed feeds are reported in the status line without hiding articles successfully loaded from the other feeds.
Articles are stored in a SQLite database at
~/.local/state/omarchy/rss-reader/items.db (a legacy items.json from
earlier versions is imported automatically and left in place). This database
holds your reading history (which articles you opened, read, starred, or
tagged) purely as a local cache; nothing in it is transmitted anywhere beyond
the configured feed/article requests. Both the state directory and the
database file (including its WAL/SHM sidecars) are restricted to 0700/0600
so other local users cannot read your feed content or read/unread state; the
backup directory and its snapshots use the same restricted permissions. The
backup and restore scripts include both the current SQLite database and that
legacy file when present. Left-click
opens the reader,
middle-click refreshes it, and right-click opens the first unread article.
Clicking an article marks it as read and loads the full text inside the reader;
the detail view also offers saved/tag controls and a button to open the original
page in the browser. The unread badge and notification count the complete
configured-feed database, not only the first maxItems visible rows. Feed
refreshes retry transient network/5xx failures with exponential backoff, while
failed article prefetches are remembered and delayed for up to six hours.
The panel uses Omarchy's live Color and Style tokens, so surfaces, focus
states, spacing, typography, and buttons follow the active theme. When a
refresh finds posts that were not present in the persisted article store, the
plugin sends one low-urgency Omarchy notification summarizing the new posts.
The initial import does not notify, so a fresh installation does not produce a
burst of alerts.
Run the dependency-free tests, backup round-trip checks, and Omarchy validation checks with:
python3 -m unittest discover -s tests -v
go test -race ./...
omarchy plugin validate .
qmllint -I "$OMARCHY_PATH/shell" BarWidget.qml Panel.qmlGitHub Actions runs the Python tests, manifest validation, installer syntax
checks, whitespace checks, and the Go backend tests/build on pushes and pull
requests. Pushing a tag such as v0.1.0 creates a GitHub Release with a
plugin archive and the cross-compiled feader-rss-fetch binaries attached.
Before tagging a release:
- Commit the release itself (version bump, changelog) and push it to
master. Call its commit SHA<sha>. - In a separate, later commit, add
"<version>": "<sha>"toRELEASE_DIGESTS.jsonand push that tomastertoo. (It has to be a later commit: the entry records<sha>'s own hash, which doesn't exist until that commit is made, so the release commit itself can never contain the correct entry for its own hash.) - Push the tag
v<version>pointing at<sha>— the release commit from step 1, not the bookkeeping commit from step 2.
The release workflow reads RELEASE_DIGESTS.json from master's current tip
(not from the tagged commit) and refuses to publish if there's no entry for
the version, or it doesn't match the tagged commit. This is what
scripts/install.sh pins provenance verification to for installs without a
local checkout — a maintainer-reviewed, historied record on master, not the
movable release tag itself.
docs/CHANGELOG.md— release history.docs/TODO.md— roadmap and prioritized backlog.


