Product & architecture documentation

Features are visible.
Boundaries make them durable.

A code-grounded guide to the reading surface and the three decoupling layers underneath it: field-clock synchronization, constrained source connectors, and a shared UI separated from runtime and storage.

Current product state

Development preview · Local-first
Primary store
SQLite per device
AI & sync
Optional
Primary target
macOS
Last reviewed
July 31, 2026
01 System overview

Every device owns a complete reading library.

The cloud is an optional collaborator, not the database of record. Reading, saved content, search, and knowledge begin on the device.

01 Protocol boundary

Sync operations and field clocks

Devices exchange durable intent and user state. They do not copy SQLite files, article bodies, indexes, or model caches.

Follow the merge path ↓
02 Source boundary

Connectors declare what they need

A versioned descriptor, normalized entry model, and constrained network context keep ingestion separate from storage.

Inspect the contract ↓
03 Runtime boundary

One UI, replaceable data adapters

React depends on platform capabilities and a reader repository rather than OS branches or a specific database transport.

See the adapter stack ↓
Your device
InterfaceReact reading surfaceDesktop · tablet · phone
BoundaryPlatform + RepositoryHTTP today · native SQLite migration
Primary storeSQLiteArticles · state · knowledge · indexes
Extraction queueTranslation cacheEmbedding queueEncrypted backup
Optional knowledgeObsidianManaged Markdown sections
Optional intelligenceAI providerOnly after explicit consent
Optional continuityRinwa SyncOperations, never raw SQLite
Transitional boundary

The desktop/web path currently uses the local Node API. Mobile SQLite adapters compile, but the complete reading-path cut-over and real-device verification are not finished.

Implemented

Present in current source and covered by the existing build or tests.

Optional

Off by default or only used after you configure it.

Early foundation

Useful today, but deliberately narrower than the eventual system.

Not released

Shared implementation exists; packaging, signing, stores, or device validation remain.

02 Reading & feeds

A reader that recedes when the article begins.

Implemented

Rinwa organizes subscriptions around reading rather than notification pressure. Unread items are expressed through type weight and color, while controls stay quiet until they are needed.

Reading surface

Original content, preserved structure

The reader keeps headings, paragraphs, lists, quotations, code, tables, images, and media in their original order. Full-text extraction runs asynchronously; cached or feed content remains readable if a refresh fails.

Ways to read

Today, Unread, Starred, and each feed

Smart views sit beside per-feed and per-group reading. A feed can be excluded from aggregate views without being removed, and item counts remain local.

Feed management

Health, history, and settings

Feed management exposes the last attempt and success, HTTP status, consecutive failures, item and new-item counts, grouping, target languages, and automatic AI preferences.

Portability

OPML in and out

Import includes preview, selection, duplicate handling, and group preservation. Export keeps an ordinary OPML exit route that other readers can understand.

Source identity

Favicons with a quiet fallback

Site icons appear where available. If a favicon fails, Rinwa falls back to a simple source initial rather than leaving a broken image.

Command palette · ⌘K

Navigation and actions by keyboard

Open smart views, groups, feeds, and saved searches; add or manage feeds; mark items read or starred; open the original; or mark the current list read. Search terms include English and Chinese aliases.

01Ingest

RSS, Atom, JSON Feed, and built-in connectors become one normalized item model.

02Preserve

The feed copy is stored first; extraction may improve it without deleting the previous successful body.

03Read

Smart feeds and source views share the same reader, keyboard, touch, and trackpad behavior.

03 Original-first translation

The source never waits for the translation.

Optional · consent-gated

The original article paints first. Translation arrives asynchronously beneath the matching source block, preserving the article as evidence rather than turning it into a replacement-language page.

Always shown first Original block

Structure, media, links, and executable code remain the author’s.

then
Arrives when ready Translated block

Aligned below its source; a failure never hides the original.

Skip rules

Rinwa does not translate Chinese text, code or preformatted blocks, URL-only content, dates, numbers, punctuation-only lines, or other text without natural-language value.

Per-feed language

Each feed keeps its own target language. Automatic translation is opt-in per feed and runs only after full text is cached.

Provider choice

Choose environment configuration, DeepL, OpenAI-compatible translation, or a custom endpoint. Provider settings and model choice stay separate from the reading surface.

No provider required

Without a configured translation provider, Rinwa remains a complete local reader. The deterministic development fallback is not presented as human-quality translation.

05 AI reading assistant

Evidence first, provider second.

BYOK · consent-gated

AI assistance starts by assembling bounded evidence. A short article can use the whole text; a long article or library question retrieves passages. Paragraph offsets and knowledge records become citation IDs before the provider receives the request.

ArticleHighlightsNotesLibrary search
LOCAL CONTEXT BUILDER Scope → retrieval → token budget → citation IDs
[P] article[H] knowledge[S] library
↓ explicit consent
OpenAIOpenRouterOllamaCustomDeepL
Streamed answercitation validation · retry policy · usage accounting
Context scopes

Summary, article, article + knowledge, or library

The scope is explicit. Short articles may fit whole; long articles and library questions retrieve passages within a fixed input budget.

Transparent evidence

Paragraph, highlight, and library citations

Article paragraphs use [P], saved highlights use [H], and other library sources use [S]. Citation IDs not present in the supplied context are removed before display.

Conversation memory

Recent turns plus bounded rolling memory

The latest messages remain verbatim within per-message limits. Earlier user questions are compacted into a small reminder and previous assistant answers are never treated as evidence.

Answer controls

Concise, balanced, or deep

Choose answer language, response style, context scope, citations, temperature, token limit, and supported reasoning effort.

Provider choice

OpenAI, OpenRouter, Ollama, or custom

Rinwa discovers models through an OpenAI-compatible /models endpoint and passes supported reasoning effort without binding the UI to one vendor.

No-provider behavior

Reading remains local and usable

Rinwa keeps deterministic local fallbacks for development and non-AI paths. No configured provider means no external answer—not a broken library.

Automatic AI is still a request

Per-feed automatic translation and summary are off by default, require current data-sharing consent, and include a background token-cost hint. AI may suggest note text, but it cannot modify notes without confirmation.

06 Saved searches → virtual feeds

A question can become a lasting reading stream.

Implemented

Saved searches turn a structured local query into an always-current virtual feed. They appear in the sidebar beside smart feeds, carry their own unread count, and open the ordinary article list and reader.

KeywordANDFeeds or groupsANDAuthor or domainANDRead / starredANDTime / tags

Explainable filters

V1 uses an AND-filter, not an opaque AI rule: keyword, feed IDs, groups, author, domain, read state, starred state, 24-hour / 7-day / 30-day windows, and tags.

Portable identity

Queries store stable feed global IDs rather than one device’s database row IDs. Saved views can therefore travel through the operation log.

Shared foundation

Evaluation happens on the API side through one resolver, so the sidebar, unread counts, mark-all-read, backup, sync, and future automation can share the same meaning.

07 Highlights & stable anchors

Prefer an unanchored highlight to a wrong one.

Implemented

Rinwa stores the selected quote with bounded prefix and suffix context. When extraction changes the article HTML, it relocates exact normalized text conservatively across block and inline-element boundaries.

Saved evidencequote + prefix + suffix
Local re-anchoringexact text + context score
Resultanchored or clearly unanchored
Across extraction changes

Piecewise inline marks

A quote may span multiple text nodes or inline elements. Rinwa renders one logical highlight as accessible pieces without flattening the article.

Duplicate text

Context disambiguation

Repeated quotes are scored against their saved prefix and suffix. Weak or absent evidence produces an unanchored state instead of choosing the earliest match.

Return to evidence

Jump to source

Selecting a saved highlight closes the knowledge sheet, centers the matching source mark, and respects reduced-motion preferences.

Inline note

Read-only popover

Activating the mark shows its saved note, or a quiet no-note state. Editing and single-highlight removal are intentionally not offered in this v1 surface.

08 Knowledge, Obsidian & MCP

Your reading can leave the app.

Implemented

Starred articles can carry an editable summary, personal note, tags, highlights, translated excerpts, and external references. These are user data, not disposable AI output.

Bidirectional Obsidian

Rinwa writes only clearly marked managed sections. User-authored Markdown around them is preserved, and changes made in the Vault can return to Rinwa through the local sync worker.

Safe removal

Removing a knowledge item clears the bookmark and its managed record. The exported Vault file is kept by default and deleted only when the user explicitly chooses that option.

Local MCP over stdio

A standalone local server exposes feeds, items, search, knowledge, tags, statistics, health, and OPML to AI agents. Safe writes require a local token; destructive actions and Rinwa’s external AI calls are not exposed.

The MCP process calls Rinwa’s local HTTP API rather than opening SQLite directly. It is intended for desktop and local development environments, not as an on-device mobile service.

09 Content source connectors

A plugin boundary begins with less authority.

P0/P1 foundation

SourceConnector separates source-specific discovery and pulling from Rinwa's database. RSS and public YouTube are registered through the same factory and implement the same interface that a future connector would: probe, discover, resolveSubscription, and pullEntries.

Declaration ConnectorDescriptor identity · API version · capabilities · permissions · rate limits
Host-granted surface ConnectorContext one constrained fetch(); no database or filesystem handle
Portable output ConnectorEntry stable remote ID · content completeness · media · extraction policy

Capabilities describe behavior

The v1 contract recognizes discovery, URL resolution, incremental pulling, authentication, inline content, and media assets. Unknown capabilities fail descriptor validation instead of silently expanding authority.

Network and pacing are explicit

The descriptor carries allowed hosts, minimum request interval, maximum concurrency, and an optional preferred page size. The current host uses the declared network hosts when it creates the fetch context.

The SDK does not import the app

Descriptors, subscription intents, entries, media assets, requests, responses, and the JSON message ABI live in packages/connector-sdk. Storage remains a host concern.

Leastquery

May receive the user's discovery text or URL input needed to find a source.

Scopedsource-id

May work with the stable identifier for the subscribed remote source.

Most sensitivearticle-body

Reserves explicit access to article text for a connector that genuinely needs it.

These tiers are a permission vocabulary, not a claim that article access is already exposed. Both built-ins declare only query and source-id; the current ConnectorContext supplies no article-body API at all.

01

Validate every hop

Redirects are followed manually. Every destination is checked again for protocol, declared host policy, and public addressing before the next request.

02

Deny private networks

Loopback, private, link-local, carrier-grade NAT, cloud metadata, and equivalent IPv6 ranges are rejected so a connector cannot be a convenient path into the user's LAN.

03

Limit the response

The direct context caps redirects at five, response bodies at 8 MiB, and requests at 15 seconds. Sensitive headers are stripped when a redirect crosses origins.

04

Normalize before storage

Connector output enters the same ingestion service used by RSS, where stable IDs, content hashes, media, extraction policy, and later indexing have one meaning.

Early boundary, not a marketplace

RSS and YouTube prove that the interface is used, not merely proposed. But connectors are still built into the API process: there is no third-party package loader or security sandbox for arbitrary code. DNS rebinding also remains a documented time-of-check/time-of-use limit until the resolved address can be pinned to the socket.

10 Backup & restore

An exit route that can also bring you back.

Implemented

Backup is separate from sync. It can preserve only subscriptions in a readable file, or create a full encrypted snapshot of user-owned reading and knowledge data.

Feeds only

Readable export

Subscriptions, groups, aggregate settings, languages, and automatic AI preferences are exported without encryption.

Full library

AES-256-GCM

A passphrase is strengthened with scrypt; a random salt, nonce, and authenticated encryption protect the full backup.

Restore

Merge or replace

Merge preserves local records where appropriate. Replace runs transactionally and is designed to avoid partial destructive restores.

Scheduling

Desktop folders

Daily or weekly backups can write to a chosen local folder, keep a configured number of recent files, and use OS secure storage for the passphrase.

Web development mode supports manual download and upload. Mobile background scheduling is not part of the current backup implementation.

11 Optional multi-device sync

Sync operations, not database files.

Optional server

Each device remains independently useful. A local change is recorded before upload as an operation with a stable ID, entity, action, JSON payload, device sequence, timestamp, and HLC ordering key. The server relays the log; clients materialize and merge it into their own SQLite database.

DEVICE ASQLitelocal writes
operation queue
pushpull
RINWA SYNCCouchDBimmutable, idempotent operation log
opaque _changes cursor
pullpush
DEVICE BSQLitelocal writes
field-clock merge
01

Record

deviceId:sequence, entity, action, payload, timestamp, and HLC are persisted locally before upload.

02

Relay

The server validates bounded batches. Duplicate operation IDs become harmless CouchDB conflicts.

03

Merge by field

sync_field_clocks stores one HLC per entity field. shouldApplyField advances only the field whose incoming clock is newer.

04

Delete with memory

A deleted field clock survives the row. An older upsert cannot recreate it; a genuinely newer re-add can.

Current protocol entities

connector_subscriptionfeedhighlight item_stateitem_summaryknowledge preferencesread_later_capturereading_position rulesaved_search
Ordering key created by createHlc() epochMillis:sequence:deviceId
Physical time

Thirteen-digit epoch milliseconds provide the broad chronological order.

Logical sequence

A per-device sequence orders multiple operations even when they share the same millisecond.

Deterministic tie-break

The device ID makes equal time and sequence values compare consistently on every client.

A wall clock alone can produce equal timestamps, and devices can disagree about time. Rinwa's current HLC-shaped key guarantees deterministic lexical ordering, but it does not eliminate clock skew: a badly wrong device clock can still influence which same-field write is considered newer.

DEVICE AisRead = trueadvances the isRead clock
DEVICE BisStarred = trueadvances the isStarred clock
MERGED ITEM STATEread and starredboth unrelated edits survive

Naive last-writer-wins at row level would replace the entire item-state row and lose one edit. Rinwa compares fields independently. If both devices edit the same field, the newer HLC wins.

Intent-only sync

Move subscriptions and URLs, not fetched bodies

A connector subscription carries source identity and settings. A read-later capture carries its URL and capture time. Each receiving device fetches and stores the resulting content locally, reducing replicated private content and bandwidth.

Atomic logical fields

Values that must agree share one clock

Reading position uses one anchor clock for item ID plus sort key. Read later uses one capture clock for URL plus capture time, so paired values cannot be assembled from different writes.

Materialization is local

The log records intent; services do the work

A newly imported feed is scheduled for local fetching. Read-later capture is queued outside the apply loop, so network work does not make operation application destructive or blocking.

Deletion is ordered

Tombstones arbitrate delete versus re-add

Feed, knowledge, connector, saved-search, reading-position, rule, highlight, and read-later deletion paths advance deleted, preventing stale snapshots from resurrecting removed records.

SYNCED AS OPERATIONS

Feed and connector intent · item state and summaries · knowledge and highlights · reading preferences and positions · rules · saved searches · read-later captures

MATERIALIZED PER DEVICE

Article bodies · extracted HTML · media · translations · model files · embeddings · full-text and vector indexes

Open client, optional managed continuity

kappain develops Rinwa as an open-source reader. Managed Rinwa Sync is the planned commercial service; local reading remains independent, and self-hosting stays within the protocol boundary.

Remote vectors are not active

The server can provision Qdrant, but the protocol advertises semanticSearch: false. Current semantic retrieval is local; a configured collection is not a shipped remote-search feature.

12 Local-first privacy & cache

App versions and content versions are separate.

Core invariant

SQLite is the primary store on each device. A visual update, new setting, or gesture fix does not invalidate article data. Extraction, translation, generated summaries, and embeddings each own a compatibility version.

LayerInvalidates whenFailure behavior
Feed contentThe upstream content hash changesKeep current feed copy
Extracted articleExtractor contract and content hash changeKeep last successful article
TranslationPipeline, target language, or source hash changesOriginal remains readable
Generated summarySummary contract, language, or source hash changesManual summary is untouched
EmbeddingsModel, revision, chunk, or vector contract changesSearch falls back to FTS
External sends require consent

Translation, summary, and chat content is not sent until the current data-sharing consent is accepted. Consent can be withdrawn.

Secrets stay out of content storage

Packaged desktop clients use OS secure storage for provider keys and sync tokens. Legacy plaintext fields are migration paths, not the design to copy.

Private text stays out of logs

Article bodies, prompts, API keys, sync tokens, and Vault contents must not be logged.

Schema changes are additive

Existing databases are migrated in place. Development does not delete or recreate a user’s library.

User data is durable

Notes, tags, highlights, references, and manually edited summaries are never treated as disposable generated cache.

Export remains available

OPML, Obsidian, and backup keep a practical route out of the application.

13 Platform architecture

One interface, two replaceable seams.

Mobile not released

React and Vite provide the shared reading surface. Tauri 2 packages it for desktop and mobile. Storage transport is hidden behind ReaderRepository; runtime abilities are hidden behind PlatformAdapter. Components should ask what the runtime can do, not which operating system name it has.

SHARED REACT UIfeeds · items · state · settingsdepends on one interface
DATA CONTRACTReaderRepositorylistFeeds · getItems · writes
TODAYHttpReaderRepositorybrowser + desktop local API
MOBILE FOUNDATIONSqliteReaderRepositorynative tauri-plugin-sql

The repository chooses at runtime

getReaderRepository() selects SQLite only when the shell reports native API transport and a local SQL capability. Every other environment receives the HTTP implementation.

The interface is deliberately narrow

Its current reading-path core covers feed listing, item queries, read/star state, feed settings, and feed deletion. AI, knowledge, OPML, and sync remain remote operations outside this interface.

The cut-over is not complete

Main feed reads and feed-setting writes use the repository. Scattered item-state calls and real iOS/Android device verification remain, and the mobile database still needs a population path.

Shared UIReact · responsive reading · gestures · knowledge
Platform adaptertransport · links · share · folders · secure storage · background refresh · local SQL
macOSWindowsiOS / iPadOSAndroid
Capabilities are reported, not invented

The browser adapter returns false or null where that is an honest answer and throws PlatformCapabilityError for unavailable secure storage or in-app browsing. The native shell can override its capability map instead of pretending every platform has finished support.

macOS · primary quality target

The shared UI and desktop shell exist, including native windows, file selection, SQLite bridge, secure-storage foundation, and in-app web windows. A signed, notarized release is not available yet.

Android · development preview

The shared UI can be packaged, but local SQLite cut-over, Keystore, background refresh, real-device validation, signing, and store distribution remain.

iOS / iPadOS and Windows · implemented, not released

They reuse the same React/Tauri architecture. Native build, database population, signing, device validation, and distribution workflows are unfinished.

14 Source-synchronized references

The details below are generated from the product repository.

Build-checked

These pages follow selected Markdown documents in feedsuite. A site build regenerates them, validates local links, and packages the result; product documentation does not need to be copied into two repositories by hand.

Curated guide + generated source of truth

This page remains intentionally editorial. The reference pages carry implementation detail and link back to the exact source document on GitHub.

15 Honest capability status

Implemented is not the same as released.

Implemented in current source

Reading and feed management, OPML, original-first translation, multi-provider AI, local hybrid retrieval, saved searches, stable highlight anchors, knowledge and Obsidian, local MCP, P0/P1 connectors with YouTube, encrypted backup, operation-log sync, command palette, and one shared responsive UI.

Early or optional by design

Local embeddings are opt-in; AI and sync require configuration; connectors are a P0/P1 built-in foundation rather than a marketplace; Android is a development preview.

Not shipped

Native mobile background refresh, complete Android Keystore validation, full mobile SQLite cut-over and population, signed/notarized macOS packages, iOS and Windows release packages, store submission, and a third-party connector marketplace.

Need implementation-level detail?

Read the source on GitHub ↗