reader

module
v0.0.0-...-8734761 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: BSD-3-Clause

README

go-news-reader/reader

go-news-reader / reader

CI Go Reference

A pure-Go (CGO=0) multi-source news & social aggregator with a go-widgets UI. One app, one unified feed, many sources — each behind the same small source.Provider contract.

Install & run

go install github.com/go-news-reader/reader/cmd/newsreader@latest

newsreader -sub reddit:golang -sub hackernews: -o feed.png   # render the feed
newsreader -sub reddit:golang -json                          # dump merged feed
newsreader -sub reddit:golang -window                       # native window (macOS/Windows/Linux/Android)
newsreader -sub reddit:golang -serve :8080                   # read-only PNG view over HTTP

Subscriptions are kind:channel (repeatable): reddit:golang, hackernews:, syndication:https://blog/feed.xml, mastodon:#golang, bluesky:@user.bsky.social, lemmy:technology, usenet:comp.lang.go, usenet:search:ubuntu (with -indexer), … Provider endpoints/credentials are set with -mastodon, -lemmy, -usenet, -indexer.

Shipping a macOS build

Two build-tooling commands turn a compiled binary into what a Mac user installs, both pure Go with CGO_ENABLED=0 and no hdiutil:

APP=$(go run ./cmd/bundle -exe ./newsreader -dir /tmp -version 1.2.3)
codesign --force --deep --sign "NewsReader Dev" --identifier com.gonewsreader.reader "$APP"
go run ./cmd/dmg -app "$APP" -o "News Reader.dmg"

cmd/bundle assembles the .app (a bare executable is not an application to AppKit: no Dock tile, no name in the menu bar, no working tray). cmd/dmg puts that bundle in the disk image it is distributed in — the application on the left, a shortcut to /Applications on the right, and the reader's own icon on the volume — via go-macos/appdmg.

Sign before making the image. The image is a copy of the bundle as it stands, so a bundle signed afterwards is not the bundle inside it. The copy does preserve a signature — it lives in Contents/_CodeSignature and inside the Mach-O, both ordinary files — and codesign --verify on the mounted copy says valid on disk and satisfies its Designated Requirement.

Sources

Each platform has a standalone pure-Go client library in its own org; this repo adds a thin provider/<name> adapter that maps the client's objects onto the normalized source.Item.

Source Client library Adapter Access
Reddit go-reddit/reddit provider/reddit uTLS browser fingerprint
RSS / Atom / JSONFeed go-syndication/feed provider/syndication open
Hacker News go-hackernews/hackernews provider/hackernews open
Usenet / newsgroups go-newsgroups/nntp provider/usenet legacy NNRP or modern TLS+AUTHINFO + Newznab search
Mastodon go-mastodon/mastodon provider/mastodon open
Lemmy go-lemmy/lemmy provider/lemmy open
Bluesky go-atproto/atproto provider/bluesky open (AT Protocol)
Twitter / X go-birdsite/twitter provider/twitter best-effort (session)
Instagram go-instagram/instagram provider/instagram best-effort (session)
TikTok go-tiktok/tiktok provider/tiktok best-effort (session)

NewsBin-style Usenet

The Usenet provider composes a full binary stack: Newznab search (go-newsgroups/newznab, direct indexer or NZBHydra2) → NZB download over NNTP with yEnc reassembly (go-newsgroups/nzb + go-newsgroups/yenc) → AutoPAR verify/repair (go-newsgroups/par2 over the go-erasure/reedsolomon GF(2¹⁶) field) → thumbnails (go-images).

Legacy vs modern servers, and authentication

Built on go-newsgroups/nntp v0.2.0, the provider transparently bridges two kinds of server (the library negotiates CAPABILITIES lazily; the reader just benefits from the OVER→XOVER fallback):

  • Legacy NNRP servers that predate RFC 3977, reject CAPABILITIES, and carry binaries anonymously. Example: Free (news.free.fr:119, plain, no auth, alt.binaries.*). Legacy() reports true.
  • Modern servers (INN and the like) that advertise OVER/READER/ COMPRESS and gate reading behind MODE READER + AUTHINFO. Text example: Eternal-September (news.eternal-september.org:563, TLS, free account). Binary example: XSUsenet free tier (TLS + AUTHINFO, carries binaries).

Configure a Usenet account in the Accounts window with Server host:port, the Implicit TLS toggle, and Username / Password (masked). When a username is set, the provider issues MODE READER (safe everywhere) and then AUTHINFO USER/PASS before selecting a group; an AUTHINFO rejection surfaces as an in-feed "sign-in required" prompt. Leaving username empty connects anonymously (the legacy Free path). The optional Newznab indexer URL / API key enable usenet:search:<query> feeds.

The news.free.fr example is ISP-specific and is not a shipped default — it is documented here and used as the Taskfile default only.

Network integration tests (manual, tag-gated)

Real-server tests live behind the nntpnet build tag in provider/usenet/integration_nntpnet_test.go. They are excluded from the default build and the 100% coverage gate and never run in CI; run them on demand:

task usenet-free     # legacy binary  (news.free.fr:119, no auth) — run from a Free network
task usenet-modern   # modern text    — NNTP_MODERN_USER / NNTP_MODERN_PASS
task usenet-binary   # modern binary  — NNTP_BIN_ADDR / NNTP_BIN_USER / NNTP_BIN_PASS (+NNTP_BIN_TLS)
Target Env vars What it proves
Legacy binary (Free) NNTP_LEGACY_ADDR, NNTP_LEGACY_GROUP anonymous connect, OVER scan, fetch + yEnc-decode (CRC verified) a single-part .par2, assert PAR2 magic
Modern text (Eternal-September) NNTP_MODERN_USER, NNTP_MODERN_PASS, NNTP_MODERN_ADDR, NNTP_MODERN_GROUP TLS + AUTHINFO, Legacy()==false, READER capability, group select + article fetch
Modern binary (XSUsenet / any) NNTP_BIN_ADDR, NNTP_BIN_USER, NNTP_BIN_PASS, NNTP_BIN_TLS, NNTP_BIN_GROUP TLS + AUTHINFO, multipart download + reassembly, real-PAR2 verify, then corrupt-and-repair proof

Each target t.Skips cleanly when its required env vars are unset.

Architecture

 provider/*  ──▶  source.Registry ──▶ Aggregate() ──▶ app.App ──▶ ui.Scene ──▶ framebuffer
 (10 sources)     (one Provider/Kind)   newest-first    (double-buffered,      (go-widgets,
                                                          damage-gated)          cached sprites)
  • source — the Provider/Item/Query/Result contract + a concurrent Registry.Aggregate merging sources newest-first.
  • ui — the go-widgets scene: unified feed, per-source sidebar, search, thumbnails. Cards and chrome render into cached sprites so scrolling is a memcpy blit (no per-frame glyph rasterisation).
  • app — wires a registry + subscriptions into the scene; double-buffered Frame() presents only when the damage sequence advances.

Every package carries 100% test coverage and builds for the fleet's nine 64-bit targets.

License

BSD-3-Clause © the go-news-reader/reader authors.

Directories

Path Synopsis
Package app wires the aggregator together: it aggregates the active profile's subscriptions across the provider registry and renders the unified feed into the go-widgets UI scene.
Package app wires the aggregator together: it aggregates the active profile's subscriptions across the provider registry and renders the unified feed into the go-widgets UI scene.
cmd
bundle command
Command bundle assembles the News Reader .app around a built executable, via go-macos/appbundle (pure Go, CGO_ENABLED=0).
Command bundle assembles the News Reader .app around a built executable, via go-macos/appbundle (pure Go, CGO_ENABLED=0).
dmg command
Command dmg wraps the built "News Reader.app" in the disk image it is distributed in: a window with the application on the left, a shortcut to /Applications on the right, and the reader's own icon on the volume.
Command dmg wraps the built "News Reader.app" in the disk image it is distributed in: a window with the application on the left, a shortcut to /Applications on the right, and the reader's own icon on the volume.
example-mediacache-plugin command
Command example-mediacache-plugin is a minimal, self-contained reference media-cache plugin for the news reader: a SHARED / NETWORK cache backed by a plain HTTP store.
Command example-mediacache-plugin is a minimal, self-contained reference media-cache plugin for the news reader: a SHARED / NETWORK cache backed by a plain HTTP store.
example-source-plugin command
Command example-source-plugin is a minimal, self-contained reference feed source plugin for the news reader.
Command example-source-plugin is a minimal, self-contained reference feed source plugin for the news reader.
newsreader command
Command newsreader is the go-news-reader aggregator CLI.
Command newsreader is the go-news-reader aggregator CLI.
Package feeds assembles a source.Registry from user configuration, wiring every provider adapter into one place.
Package feeds assembles a source.Registry from user configuration, wiring every provider adapter into one place.
internal
appicon
Package appicon holds the application's branded icon assets, embedded into the binary so the app can set its dock / taskbar icon and a menu-bar tray icon without shipping loose files.
Package appicon holds the application's branded icon assets, embedded into the binary so the app can set its dock / taskbar icon and a menu-bar tray icon without shipping loose files.
biometric
Package biometric gates a sensitive action behind the platform's owner check — Touch ID, or the device password — so a self-signed build can protect the vault read without a keychain entitlement.
Package biometric gates a sensitive action behind the platform's owner check — Touch ID, or the device password — so a self-signed build can protect the vault read without a keychain entitlement.
browsercookies
Package browsercookies imports authentication cookies straight from a locally installed browser, so the reader can pick up a session the user already established by logging in normally — no API app registration, no manual copy-paste of an opaque token.
Package browsercookies imports authentication cookies straight from a locally installed browser, so the reader can pick up a session the user already established by logging in normally — no API app registration, no manual copy-paste of an opaque token.
feedstore
Package feedstore persists a source.FeedCache's entries to disk — one small JSON file per subscription — so relaunching the reader serves each feed's last posts and skips re-fetching what is still fresh, instead of hitting every source again on startup.
Package feedstore persists a source.FeedCache's entries to disk — one small JSON file per subscription — so relaunching the reader serves each feed's last posts and skips re-fetching what is still fresh, instead of hitting every source again on startup.
httplog
Package httplog records the HTTP exchanges the aggregator's providers make so they can be inspected in-app (the Network log tab).
Package httplog records the HTTP exchanges the aggregator's providers make so they can be inspected in-app (the Network log tab).
mediacacheplugin/transport
Package transport is the hashicorp/go-plugin + gRPC glue that carries a mediacache.Cache across a process boundary.
Package transport is the hashicorp/go-plugin + gRPC glue that carries a mediacache.Cache across a process boundary.
settings
secrets.go moves the reader's account secrets (session cookies, access tokens, passwords, API keys) off the settings.json file and into the host's native credential vault — macOS Keychain, Windows Credential Manager or the Linux Secret Service — through the pure-Go, CGO-free cross-platform façade github.com/go-keyring/keyring.
secrets.go moves the reader's account secrets (session cookies, access tokens, passwords, API keys) off the settings.json file and into the host's native credential vault — macOS Keychain, Windows Credential Manager or the Linux Secret Service — through the pure-Go, CGO-free cross-platform façade github.com/go-keyring/keyring.
sourceplugin/protoconv
Package protoconv converts between the reader's in-process source types (source.Item, source.Media, source.Query, source.Result) and their gRPC wire forms in package sourcepb.
Package protoconv converts between the reader's in-process source types (source.Item, source.Media, source.Query, source.Result) and their gRPC wire forms in package sourcepb.
sourceplugin/transport
Package transport is the hashicorp/go-plugin + gRPC glue that carries a source.Provider across a process boundary.
Package transport is the hashicorp/go-plugin + gRPC glue that carries a source.Provider across a process boundary.
viewmodel
Package viewmodel holds the news reader's core UI state as go-widgets/mvvm primitives — the "ViewModel" layer of an MVVM split.
Package viewmodel holds the news reader's core UI state as go-widgets/mvvm primitives — the "ViewModel" layer of an MVVM split.
webrender
Package webrender renders a web page (the external target of a feed item) into an image, using the pure-Go go-webengine (CGO=0, no Chromium).
Package webrender renders a web page (the external target of a feed item) into an image, using the pure-Go go-webengine (CGO=0, no Chromium).
windowapp
Package windowapp adapts an *app.App to the window.Handler contract: it exposes the app's double-buffered framebuffer to a native presenter and routes native mouse/scroll/key events into the ui.Scene.
Package windowapp adapts an *app.App to the window.Handler contract: it exposes the app's double-buffered framebuffer to a native presenter and routes native mouse/scroll/key events into the ui.Scene.
Package mediacache is the reader's pluggable media-cache backend: a small key/value store keyed by a media URL, holding the ORIGINAL downloaded bytes so media already seen is never re-fetched (across restarts too) and the cached file stays real, correctly-typed media a user can browse in Finder.
Package mediacache is the reader's pluggable media-cache backend: a small key/value store keyed by a media URL, holding the ORIGINAL downloaded bytes so media already seen is never re-fetched (across restarts too) and the cached file stays real, correctly-typed media a user can browse in Finder.
Package mediacacheplugin lets the news reader use an external process as its media-cache backend over gRPC (via hashicorp/go-plugin), so a shared/network cache can be plugged in without recompiling the reader.
Package mediacacheplugin lets the news reader use an external process as its media-cache backend over gRPC (via hashicorp/go-plugin), so a shared/network cache can be plugged in without recompiling the reader.
provider
bluesky
Package bluesky adapts the standalone github.com/go-atproto/atproto client to the aggregator's source.Provider contract.
Package bluesky adapts the standalone github.com/go-atproto/atproto client to the aggregator's source.Provider contract.
hackernews
Package hackernews adapts the standalone github.com/go-hackernews/hackernews client to the aggregator's source.Provider contract.
Package hackernews adapts the standalone github.com/go-hackernews/hackernews client to the aggregator's source.Provider contract.
instagram
Package instagram adapts the standalone github.com/go-instagram/instagram best-effort client to the aggregator's source.Provider contract.
Package instagram adapts the standalone github.com/go-instagram/instagram best-effort client to the aggregator's source.Provider contract.
lemmy
Package lemmy adapts the standalone github.com/go-lemmy/lemmy client to the aggregator's source.Provider contract.
Package lemmy adapts the standalone github.com/go-lemmy/lemmy client to the aggregator's source.Provider contract.
mastodon
Package mastodon adapts the standalone github.com/go-mastodon/mastodon client to the aggregator's source.Provider contract.
Package mastodon adapts the standalone github.com/go-mastodon/mastodon client to the aggregator's source.Provider contract.
reddit
Package reddit adapts the standalone github.com/go-reddit/reddit client to the aggregator's source.Provider contract.
Package reddit adapts the standalone github.com/go-reddit/reddit client to the aggregator's source.Provider contract.
redgifs
Package redgifs is a pure-Go (CGO=0) client for the public RedGIFs v2 API and the aggregator source.Provider built on top of it.
Package redgifs is a pure-Go (CGO=0) client for the public RedGIFs v2 API and the aggregator source.Provider built on top of it.
syndication
Package syndication adapts the standalone github.com/go-syndication/feed parser (RSS/Atom/JSONFeed) to the aggregator's source.Provider contract.
Package syndication adapts the standalone github.com/go-syndication/feed parser (RSS/Atom/JSONFeed) to the aggregator's source.Provider contract.
tiktok
Package tiktok adapts the standalone github.com/go-tiktok/tiktok best-effort client to the aggregator's source.Provider contract.
Package tiktok adapts the standalone github.com/go-tiktok/tiktok best-effort client to the aggregator's source.Provider contract.
twitter
Package twitter adapts the standalone github.com/go-birdsite/twitter best-effort client to the aggregator's source.Provider contract.
Package twitter adapts the standalone github.com/go-birdsite/twitter best-effort client to the aggregator's source.Provider contract.
usenet
Subject classification for the group content-mix estimate (GroupStats): a cheap, allocation-light heuristic over NNTP article subjects, kept in the provider (which owns the OVER scan) so it needs no UI/app dependency.
Subject classification for the group content-mix estimate (GroupStats): a cheap, allocation-light heuristic over NNTP article subjects, kept in the provider (which owns the OVER scan) so it needs no UI/app dependency.
Package source defines the common contract every news/social provider implements so the aggregator can treat Reddit, RSS, Usenet, Mastodon, Bluesky, Twitter, Instagram, TikTok and the rest uniformly.
Package source defines the common contract every news/social provider implements so the aggregator can treat Reddit, RSS, Usenet, Mastodon, Bluesky, Twitter, Instagram, TikTok and the rest uniformly.
Package sourceplugin lets the news reader load feed sources as external plugin binaries over gRPC (via hashicorp/go-plugin), so a new source can be added without recompiling the reader.
Package sourceplugin lets the news reader load feed sources as external plugin binaries over gRPC (via hashicorp/go-plugin), so a new source can be added without recompiling the reader.
Package ui renders the aggregator — a topbar with search, a sidebar of source subscriptions, and a unified newest-first feed of items from every source — into an RGBA pixel buffer.
Package ui renders the aggregator — a topbar with search, a sidebar of source subscriptions, and a unified newest-first feed of items from every source — into an RGBA pixel buffer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL