5 min read

Readest: A Modern Open-Source Ebook Reader for Every Device (GitHub, Scanned)

An ebook reader for EPUB, PDF, MOBI and more, with highlights, TTS and sync across devices.

Readest logo
✅
Scan: safe. Nothing malicious. Two things to know: sync, translation and the Pagebound link send data to outside services when you use them, and the Rust zip library it bundles has critical advisories. Scanned Oct 26, 2026; the full report is below.

Readest is an ebook reader for macOS, Windows, Linux, Android, iOS and the web. It opens EPUB, PDF, MOBI, AZW3, FB2, CBZ, TXT and Markdown, and offers scroll or page views, highlights and notes, dictionary and Wikipedia lookup, text-to-speech, read-along narration for EPUB 3 media overlays, translation through DeepL or Yandex, and a split view for reading two books side by side.

It started as a modern rewrite of Foliate, built with Next.js and Tauri, and it has grown quickly: about 25,600 stars and 150 contributors, with 108 releases since late 2024, at v0.12.16 when scanned. It connects to OPDS catalogues and Calibre, syncs progress with KOReader devices, and syncs books and notes between your own devices through an optional Readest account.

It is AGPL-3.0. The limits: it is still pre-1.0 and moves fast, Linux builds need XWayland on Wayland desktops, and Windows needs Microsoft's WebView2 runtime.

  • Repository: github.com/readest/readest
  • Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
  • Language: TypeScript. Stars: 25.6K. Forks: 1,755. Last push: Oct 25, 2026.
  • Scan: safe, Oct 26, 2026, commit f1e1836

Who it is for

Readers who want one good-looking reader on every device without a store lock-in, people with a DRM-free library in Calibre, and KOReader users who want their progress to follow them to a phone or laptop.

Getting started

1. Linux (Flathub)

flatpak install flathub com.bilingify.readest

2. Try it on Linux with Nix, without installing

nix run --accept-flake-config github:readest/readest

macOS, iPhone and iPad get it from the App Store; Windows, Android and Linux installers are on readest.com and the GitHub releases page; the web version is at web.readest.com.

Safety scan

We cloned readest/readest at commit f1e1836 on Oct 26, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • The one secret hit is a Firebase web key in PageboundClient.ts. It is the public client key of Pagebound, a reading-tracker site, used for an unofficial integration: the code says Pagebound has no public API, so Readest signs in with your Pagebound email and password the way Pagebound's own web app does. Only use it if you are comfortable with that.
  • The two persistence hits are tests in transfer_file.rs checking that a path to a LaunchAgents plist is refused, which is a safeguard, not persistence. The bare-IP URLs are private and reserved address ranges in browser_fetch.rs's address checks. The 18 binaries are EPUB test fixtures.
  • 51 known advisories: 3 critical, all the same issue in three versions of the Rust zip crate (permissions applied through symlinks on extraction), plus high ones in zip, nix and quinn-proto in Cargo.lock and body-parser, undici and zod in pnpm-lock.yaml.
  • Book and note sync goes through a Readest account and the project's servers, translation goes to DeepL or Yandex, and many hosts in the code (OPDS, WebDAV, KOReader sync) are integrations you configure. Nothing is sent until you turn a feature on.
  • 13 workflows, none using pull_request_target, and all 52 third-party actions pinned to commits. Security policy, CodeQL, Dependabot and contributing guide present. OpenSSF Scorecard 8.2, among the highest on this list.

What the scanner counted

CheckResult
Secrets1 candidate found and read; see the notes above.
Suspicious code2 pattern hits found and read; every one is listed under the raw findings.
Install-time code1 npm lifecycle script. 4 Cargo build scripts
Committed binaries18 executable or compiled objects committed; listed under the raw findings.
CI workflows13 workflows. None use pull_request_target. 0 of 52 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often web.readest.com, opds-spec.org, abs.local, www.idpf.org. 24 URLs to a bare IP address, listed under the raw findings.
Known vulnerabilities51 advisories across 2,944 pinned packages: 3 critical, 10 high, 7 moderate, 3 low, 28 unrated. Cargo.lock: 1,067 packages, 33 advisories; apps/readest-app/extensions/windows-thumbnail/Cargo.lock: 67 packages, 4 advisories; apps/readest.koplugin/native/localsend-bin/Cargo.lock: 248 packages, 5 advisories; pnpm-lock.yaml: 1,802 packages, 11 advisories.
Project hygieneHas security policy, automated dependency updates, CodeQL, licence file, contributing guide.
OpenSSF Scorecard8.2 out of 10, as of Oct 25, 2026.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Secret candidates (1, redacted)
WhereRuleMatch
apps/readest-app/src/services/pagebound/PageboundClient.ts:9google-api-keyAIzaSy…nGg (39 chars)
Pattern hits (2)
WhereRuleMatch
apps/readest-app/src-tauri/src/transfer_file.rs:552persistence"/Users/x/Library/LaunchAgents/Readest-agent.plist",
apps/readest-app/src-tauri/src/transfer_file.rs:589persistence"/Users/victim/Library/LaunchAgents/Readest-agent.plist",
URLs to bare IP addresses (24)
WhereRuleMatch
apps/readest-app/src-tauri/src/browser_fetch.rs:358ip-literal-url"http://100.64.0.1/",
apps/readest-app/src-tauri/src/browser_fetch.rs:359ip-literal-url"http://198.18.0.1/",
apps/readest-app/src-tauri/src/browser_fetch.rs:360ip-literal-url"http://224.0.0.1/",
apps/readest-app/src-tauri/src/browser_fetch.rs:361ip-literal-url"http://240.0.0.1/",
apps/readest-app/src-tauri/src/browser_fetch.rs:375ip-literal-url"https://1.1.1.1/",
apps/readest-app/src/__tests__/utils/kosync-ssrf.test.ts:42ip-literal-urlexpect(isLanAddress('https://8.8.8.8')).toBe(false);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:16ip-literal-urlexpect(isLanAddress('http://0.1.2.3/')).toBe(true);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:20ip-literal-urlexpect(isLanAddress('http://198.18.0.1/')).toBe(true);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:21ip-literal-urlexpect(isLanAddress('http://198.19.255.255/')).toBe(true);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:22ip-literal-urlexpect(isLanAddress('http://225.0.0.1/')).toBe(true);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:23ip-literal-urlexpect(isLanAddress('http://240.0.0.1/')).toBe(true);
apps/readest-app/src/__tests__/utils/network-ssrf-hardening.test.ts:51ip-literal-urlexpect(isLanAddress('https://8.8.8.8')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:38ip-literal-urlexpect(isLanAddress('http://172.15.0.1')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:39ip-literal-urlexpect(isLanAddress('http://172.32.0.1')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:64ip-literal-urlexpect(isLanAddress('http://100.64.0.1')).toBe(true);
apps/readest-app/src/__tests__/utils/network.test.ts:65ip-literal-urlexpect(isLanAddress('http://100.127.255.255')).toBe(true);
apps/readest-app/src/__tests__/utils/network.test.ts:66ip-literal-urlexpect(isLanAddress('http://100.100.100.100')).toBe(true);
apps/readest-app/src/__tests__/utils/network.test.ts:70ip-literal-urlexpect(isLanAddress('http://100.63.0.1')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:71ip-literal-urlexpect(isLanAddress('http://100.128.0.1')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:96ip-literal-urlexpect(isLanAddress('http://8.8.8.8')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:97ip-literal-urlexpect(isLanAddress('http://1.1.1.1')).toBe(false);
apps/readest-app/src/__tests__/utils/network.test.ts:98ip-literal-urlexpect(isLanAddress('http://203.0.113.50')).toBe(false);
apps/readest-app/src/__tests__/utils/opds-req.test.ts:89ip-literal-urlexpect(needsNativeImageFetch('http://100.101.102.103:8088/cover/a.jpg')).toBe(true);
apps/readest-app/src/__tests__/utils/opds-req.test.ts:100ip-literal-urlexpect(needsNativeImageFetch('http://100.101.102.103:8088/cover/a.jpg')).toBe(false);
npm lifecycle scripts (1)
  • package.json prepare: husky
Committed binaries (18)
  • apps/readest-app/src/__tests__/fixtures/data/sample-table-layout.epub: JAR, 3 MB
  • apps/readest-app/src/__tests__/fixtures/data/sample-alice.epub: JAR, 414 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-6155-footnote-images.epub: JAR, 25 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-3583.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/sample-vertical-rl.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-6129-tall-table.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-6221-image-column-spill.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-4439.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-6513.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-3688.epub: JAR, 3 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-bg-restore.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/sample-table-wide.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-5263.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/sample-table-transcript.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-duokan-cover-percent.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-4379.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-3683.epub: JAR, 2 KB
  • apps/readest-app/src/__tests__/fixtures/data/repro-inline-block-overflow.epub: JAR, 2 KB
Worst known vulnerabilities (24 of 51)
AdvisorySeverityPackageSummary
GHSA-4684-h2q9-2vcpcriticalzip@2.4.2ZipStreamReader::extract() and ZipArchive::extract() apply Unix permissions through symlinks without confining the targe…
GHSA-4684-h2q9-2vcpcriticalzip@4.6.1ZipStreamReader::extract() and ZipArchive::extract() apply Unix permissions through symlinks without confining the targe…
GHSA-4684-h2q9-2vcpcriticalzip@6.0.0ZipStreamReader::extract() and ZipArchive::extract() apply Unix permissions through symlinks without confining the targe…
GHSA-wgrg-5h56-jg27highnix@0.19.1Out-of-bounds write in nix::unistd::getgrouplist
GHSA-3g6h-r5qx-f6hqhighquinn-proto@0.11.17Peer minimum ACK delay can panic quinn-proto when ACK_FREQUENCY transmission is enabled
GHSA-ppcp-v39w-8jq2highquinn-proto@0.11.17Outgoing datagram buffer accounting can panic the sender in quinn-proto
GHSA-vv5m-jxxh-4627highzip@2.4.2Insecure Preservation of SUID/SGID Bits during ZIP Extraction Leading to Potential Privilege Escalation
GHSA-vv5m-jxxh-4627highzip@4.6.1Insecure Preservation of SUID/SGID Bits during ZIP Extraction Leading to Potential Privilege Escalation
GHSA-vv5m-jxxh-4627highzip@6.0.0Insecure Preservation of SUID/SGID Bits during ZIP Extraction Leading to Potential Privilege Escalation
GHSA-27r3-jxmh-p828highbody-parser@2.3.0body-parser vulnerable to Denial of Service via trailing bytes after a compressed request body
GHSA-xjj6-f33q-929chighundici@7.30.0undici vulnerable to Denial of Service via unhandled error on excessive content-encodings
GHSA-mrhx-c7xc-m3w5highzod@4.4.1Zod can exhaust memory while validating oversized constrained collections
GHSA-mrhx-c7xc-m3w5highzod@4.4.3Zod can exhaust memory while validating oversized constrained collections
GHSA-wrw7-89jp-8q8gmoderateglib@0.18.5Unsoundness in `Iterator` and `DoubleEndedIterator` impls for `glib::VariantStrIter`
GHSA-76w9-p8mg-j927moderatenix@0.19.1Out-of-bounds Write in nix
GHSA-4grx-2x9w-596cmoderatersa@0.9.10Marvin Attack: potential key recovery through timing sidechannels
GHSA-w74x-v6f9-j3xgmoderatefast-uri@3.1.8fast-uri vulnerable to URI scheme spoofing via percent-encoded scheme decoding
GHSA-wjr9-j795-jxcrmoderatefast-uri@3.1.8fast-uri vulnerable to host confusion via unchecked WHATWG host canonicalization
GHSA-4qv8-2p9j-2685moderateundici@7.30.0undici vulnerable to Denial of Service via unbounded memory in the EventSource parser
GHSA-f72v-6w45-q472moderateundici@7.30.0undici vulnerable to Subresource Integrity bypass via non-lowercase algorithm tokens
GHSA-333x-w4r6-6crplowfast-uri@3.1.8fast-uri vulnerable to scheme injection via an unescaped first-segment colon in suffix references
GHSA-xhq7-xmqj-769hlowfast-uri@3.1.8fast-uri vulnerable to improper path equivalence in relative reference normalization
GHSA-m5mm-p8h7-m29vlowserialize-javascript@7.0.5Serialize JavaScript: Cross-site scripting (XSS) via unescaped </script at the end of a serialized function body
RUSTSEC-2025-0141unknownbincode@1.3.3Bincode is unmaintained

By the numbers

Stars25.6K
Forks1,755
Contributors150
Commits3,541
Open issues130
Open pull requests11
Releases108
Latest releasev0.12.16
LicenceAGPL-3.0
Main languageTypeScript
Project age2 years
Last pushOct 25, 2026
Tracked files4,468
Lines of code787K
Checkout size131 MB

Lines by language: TypeScript 550.3K, JSON 101.5K, Markdown 51.2K, Rust 25.4K, Lua 18.5K, Kotlin 13.3K.

Questions

Is Readest free?

Yes. Readest is free and open source under AGPL-3.0 on every platform. Sync through a Readest account has a free tier, with paid plans for more cloud storage.

Does Readest work with Kindle books?

It opens DRM-free MOBI and AZW3 files. Books bought from Amazon with DRM cannot be opened by Readest or any other third-party reader.

Can Readest sync with KOReader?

Yes. It supports KOReader's sync server for reading progress, notes and bookmarks, so you can read on an e-ink device running KOReader and pick up in Readest on a phone or computer.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.