Build Readwise Reader
KINDAreplaces $12.99/mosaves $155.88/yrback to the verdict
Save a page from anywhere, extract a clean reader view, highlight and annotate, and have those highlights still point at the right text when the extractor improves, because they are anchored by quoted text and its surroundings rather than by a character offset.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the server, the extractor and the review job.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - have readyfree
Why Phase 1's extraction and Phase 3's re-extraction are both only meaningful against real pages: a clean article, a JavaScript-only page, one with an aggressive content security policy, a very long piece and one that gets edited.
Get it Collect them from your own reading and keep them as fixtures with their expected outcomes.
Verify
All five are saved with expected extraction results recorded - have readyfree
Why Phase 2's anchoring is only tested by a document where the same words appear twice, which is what the surrounding context exists to disambiguate.
Get it Any long article will contain repeated phrases. Highlight one deliberately and check it resolves to the right occurrence.
Verify
A highlight on a repeated phrase resolves to the correct occurrence - API keyfree tiers cover this
Why Phase 5 only. Everything else works without delivery.
Get it A transactional provider with a verified sending domain.
Verify
A digest arrives in an inbox - have ready5 to 10 US dollars a month
Why Saving from a phone needs a reachable endpoint, and the review digest needs something running on a schedule.
Get it A small virtual server, or a machine at home behind a tunnel. Authenticate before exposing it.
Verify
A save from a phone reaches the endpoint over HTTPS
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- documents(id, url, canonical_url, title, author, saved_at, published_at, state, word_count) · state is inbox, later, archived - extractions(id, document_id, extractor_version, text, html, extracted_at, content_hash) - highlights(id, document_id, quote, prefix, suffix, note, colour, created_at, resolved_offset, resolve_state) - tags(id, name), document_tags(document_id, tag_id) - reviews(highlight_id, last_shown_at, next_due_at, times_shown) - A highlight belongs to a document, not to an extraction. That is what lets the extraction be replaced.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ./data/reader.db | Documents, extractions and highlights. |
FETCH_TIMEOUT_MS | required | 10000 | Per-page fetch timeout. A slow page should fail rather than hold up a save. |
FETCH_MAX_BYTES | required | 5000000 | Streaming byte cap, enforced during download. |
ANCHOR_CONTEXT_CHARS | required | 40 | How much prefix and suffix is stored with a quote. Enough to disambiguate a repeated phrase. |
REVIEW_DAILY_CAP | required | 5 | Highlights per daily review. Small enough that the review actually happens. |
SAVE_TOKENsecret | required | replace-with-a-random-token | Used by the bookmarklet and the share endpoint. Scoped to saving only. |
The build, in order
Save and extract
A saved page becomes readable text, and a page that cannot be extracted says so.
Timeout, streaming byte cap, robots respected. You are a well-behaved client saving for yourself.
Files
src/fetch.jsReadability for the article, then strip scripts and remote references so the copy is inert.
Files
src/extract.jsterminalnpm install @mozilla/readability jsdom dompurify
So you know which extractor produced a stored copy, which matters in Phase 3.
Files
src/extract.js
done when · tick each as it passeswatch out- Storing raw fetched HTML and rendering it later means every time you open your own archive you load the publisher's scripts and trackers. Sanitise at capture: strip scripts, event handlers and remote references, so the stored copy is inert.
Highlights anchored to text, not to positions
A highlight knows what it selected, not where in a string it was.
The selected text plus a short prefix and suffix. Enough context to disambiguate a repeated phrase.
Files
src/anchor.jsExact match first, then approximate. Record which strategy succeeded.
Files
src/anchor.jsMarked, still readable as a quote, never silently attached to the wrong place.
Files
src/anchor.js
done when · tick each as it passeswatch out- Storing a character offset into the extracted text means every highlight breaks the moment the extraction changes by one character, which happens whenever the extractor improves or the publisher edits the page. Quote plus context resolves against new text and degrades gracefully when it cannot.
Re-extraction, which is the point
Improving the extractor does not lose a year of highlights.
A document has several; one is current. Rollback is then trivial.
Files
src/extractions.jsCounts by outcome, shown before the new extraction becomes current.
Files
src/reextract.jsA re-extraction that loses highlights should be reversible in one action.
Files
src/extractions.js
done when · tick each as it passeswatch out- Replacing an extraction without re-resolving highlights leaves them pointing at text that has moved, and the damage is silent and cumulative. Re-resolve as part of re-extraction, report the counts, and keep the old extraction so a bad one can be reverted.
A queue you can actually clear
Saved articles are triaged rather than accumulating into a wall of guilt.
Inbox, later, archive. One action between them, keyboard reachable.
Files
src/queue.jsFrom word count. It is what lets you pick something that fits the time you have.
Files
src/queue.jsArchive without reading, recorded as such. It is the action that keeps the queue survivable.
Files
src/queue.js
done when · tick each as it passeswatch out- A read-later queue with no triage becomes a list of a thousand articles nobody opens, and the tool stops being used rather than the queue being cleared. Make archiving without reading a first-class action, since deciding not to read something is a legitimate outcome and the queue depends on it.
Highlight review
What you highlighted comes back, spaced out, so it is not written once and never seen again.
Recency and annotation as weights, with a daily cap.
Files
src/review.jsEmail with the highlights and their sources. This is where a review actually gets read.
Files
src/digest.jsMarked not useful and never shown again. Without it, the review degrades over time.
Files
src/review.js
done when · tick each as it passeswatch out- Showing highlights at random from a large archive produces mostly forgettable ones and trains you to skip the review. Weight towards annotated highlights and recent reading, and cap the daily count so the review takes a minute rather than being abandoned.
Capture everywhere, and exportproduct builder
Saving takes one action from anywhere, and nothing is trapped.
No extension to maintain. The endpoint covers the phone case.
Files
public/bookmarklet.jssrc/save.jsOne file per document. This is the format that outlives the application.
Files
src/export.jsA reading list is personal. Put a login in front before it leaves your machine.
Files
src/auth.js
done when · tick each as it passeswatch out- A reading queue and its highlights are a detailed record of what you are thinking about, which is more revealing than most people expect. Authenticate before exposing it anywhere, and be deliberate about whether the highlights export includes your notes.
That is the whole plan for Readwise Reader. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Extraction quality matching the commercial product.
- Mobile applications with offline reading.
- Reading device integrations and their highlight formats.
- Access to paywalled material.
- excellent parsing
- mobile/offline apps
- Kindle integrations
- RSS/PDF workflows
- spaced repetition
- exports
- Full text search across every document and highlight
- Automatic tagging from article content, proposed rather than applied
- A weekly digest of what you saved and did not read, which is the honest metric
Need the files? The project pack on the verdict page hands your agent the whole brief · more read it later.