Dubhe

A quiet core for reading.

Bring subscriptions, local documents, and mail into one reader. Dubhe handles sources, storage, navigation, and safe content—your application decides the interface.

reader / library3 sources
SourcesFeeds 12Documents 8Local mail 4
Latest entries
A calmer reading listField notes / today
Designing with constraintsEngineering / yesterday
Release notesDocuments / Monday
Field notes / 21 September 2026

A calmer reading list

Everything worth reading belongs in one place. A feed, a document on disk, or a saved message can share the same familiar reading flow.

Content remains available locally, with a clear boundary between what came from a source and what is safe to display.

file-backed entries and stateremote images blocked by default

One library. Several kinds of source.

Dubhe supplies the reader core. Feeds, documents, and mail bring their own parsers while sharing navigation and state.

Subscriptions

RSS, Atom, and JSON Feed with conditional HTTP requests and OPML transfer.

Documents

Local HTML and Markdown from a root-confined directory.

Mail

Local .eml and .mbox messages with MIME parts and CID image replacement.

Reading state

Persistent entries, read/star status, search, and back/forward history.

Trust is a boundary.

Sanitize arbitrary HTML before display. Remote images are blocked by default, and the blocked count can be shown to readers.

No JavaScript execution, WebView, or mail transport. A UI can be built on top without changing the core.

require "dubhe"
store = Dubhe::Store.new
docs = Dubhe::Sources::Docs.new(root: "notes", store: store)
reader = Dubhe::Reader.new(sources: [docs], store: store)
reader.refresh
reader.entries.each { |entry| puts entry.title }