Kindle / KOReader Ebook Sync¶
Last Updated: 2026-08-03 Status: ✅ Active
Purpose¶
Delivers the Calibre-Web Automated library to a jailbroken Kindle Paperwhite 4 running KOReader, and keeps reading progress in sync across devices. Calibre-Web Automated (CWA) on Lotus is the single source of truth — what reaches the Kindle, how books are named, and how they are grouped are all controlled server-side. No routine administration happens on the Kindle itself.
Location¶
| Server | Lotus — calibre-web-automated container |
| Library path | /mnt/user/data/media/books/library → /calibre-library |
| Ingest folder | /mnt/user/data/media/books/import → /cwa-book-ingest |
| Local URL | http://192.168.1.80:8083 |
| Device | Kindle Paperwhite 4, IoT VLAN, 192.168.20.244 |
The device¶
| Model | Kindle Paperwhite 4 (10th gen), i.MX6 SoloLite, 512 MB RAM |
| Board ID | P001F20911120E92 |
| Firmware | 5.18.1 — must never be updated |
| Jailbreak | AdBreak (supports 5.18.1 – 5.18.5.0.1) |
| Reader | KOReader v2026.07.1 (kindlehf build) |
| Recovery kit | /mnt/user/backups/kindle/recovery-kit-20260802/ on Lotus |
Never let this Kindle take a firmware update
OTA is blocked by renaming /usr/bin/otaupd and /usr/bin/otav3 to .bck
(the renameotabin KUAL extension). An Amazon update would remove the
jailbreak and there may be no jailbreak for the newer firmware.
How it works¶
1. What gets sent — the Magic Shelf¶
The Kindle syncs from a CWA Magic Shelf (id 17, named Kindle), not the
whole library:
{"condition": "AND", "rules": [
{"id": "tag", "field": "tag", "type": "string",
"operator": "not_in", "value": ["Cookbooks", "Recipes", "Cooking"]}]}
OPDS feed: http://192.168.1.80:8083/opds/magicshelf/17
To stop a book reaching the Kindle, tag it Cookbooks in CWA. Nothing needs
doing on the device. Image-heavy cookbooks are excluded because they are large
(100 MB+) and render poorly on a 6" e-ink screen.
Magic Shelf feeds are ordered newest-first, which the sync guard depends on.
2. How it arrives — OPDS sync¶
KOReader's OPDS plugin writes flat into sync_dir, as
Author - Title.ext. It does not create folders. The device library is
therefore one flat directory of ~570 files, which is deliberate — see
"Why flat" below.
Config lives in /mnt/us/koreader/settings/opds.lua:
| Field | Value |
|---|---|
url |
.../opds/magicshelf/17 |
username |
kindle |
sync |
true |
sync_dir |
/mnt/us/books |
sync_max_dl |
100 |
last_download |
href of the newest already-synced book |
Triggering a sync: OPDS catalog → long-press the catalogue → Sync.
Never Force sync (see Known Issues).
3. How it is found — metadata and collections¶
Two independent mechanisms:
Sort by → Series reads series metadata from inside the book files.
KOReader's crengine understands both calibre:series (EPUB2) and
belongs-to-collection / group-position (EPUB3, which Calibre writes).
This is automatic and identical on every device, because the metadata travels
inside the file.
Collections (/mnt/us/koreader/settings/collection.lua) provide the
hierarchy — one tap to a series rather than scrolling a flat list.
Currently 46: Reading Now, To Read, Favourites (pinned, orders 1–3), then
43 series collections generated from CWA series data.
Collections are a snapshot — they do not update themselves and must be regenerated after books are added.
4. Reading progress — KOSync¶
CWA exposes a KOReader sync endpoint at /kosync, enabled by
koreader_sync_enabled = 1 in /config/cwa.db. KOReader's cwasync plugin
(third-party, not in stock KOReader) authenticates with HTTP Basic using the
same kindle credentials, stored in settings.reader.lua.
auto_sync is on, paired with wifi_enable_action = turn_on so it enables
Wi-Fi silently rather than prompting.
Documents are matched by file hash
cwasync identifies a book by partial_md5_checksum — a hash of the file
bytes. Rewriting book files (e.g. calibredb embed_metadata) changes every
document ID and breaks cross-device progress matching. Do metadata work
before building up reading history, or accept the reset.
Accounts¶
| User | Role | Purpose |
|---|---|---|
kindle |
258 (Download + Viewer) | Device account — OPDS and KOSync |
danclark |
479 (full) | Admin |
admin |
384 (Viewer + Delete) | Deliberately restricted |
The device password is stored in cleartext in opds.lua and
settings.reader.lua — that is KOReader's design, not a choice. This is why the
device account is limited to Download + Viewer. A previous test account had
admin rights with the password user1; it was deleted 2026-08-03.
Why flat¶
The nested Calibre layout (Author/Title (id)/file) is fundamentally
incompatible with OPDS sync:
- Sync writes to
sync_dir/Author - Title.ext, flat - Its duplicate check (
lfs.attributes(item.file)) tests that flat path - With a nested tree, every book fails that check and is re-downloaded
Folder-per-shelf is also impossible: sync_dir is a single global setting
(OPDSBrowser:getCurrentDownloadDir), so every synced catalogue writes to the
same directory regardless of source.
With ~570 books, no folder hierarchy is navigable anyway. Discovery is by search + collections, not by browsing.
Device access¶
USB networking via usbnetlite:
- Unplug the cable
- KUAL → USBNetLite →
* Toggle USBNetwork * - Plug the cable back in → Kindle appears at
192.168.15.244 - Host side needs
192.168.15.201/24
| Gotcha | Detail |
|---|---|
| SSH keys | Go in /mnt/us/usbnetlite/etc/dropbear/authorized_keys — hardcoded in the binary, ignores $HOME and -H |
| No syslog | dropbear has syslog compiled out; auth failures never reach /var/log/messages |
| Interface name | USB gadget MAC is random per boot, so the host interface name changes each time — a NetworkManager profile bound to a fixed ifname silently fails |
| USB identity | In usbnet mode it enumerates as 0525:a4a2 Netchip, not Lab126 |
| USB drive mode | Requires exiting KOReader. mass_storage_actions is not available on this device, so there is no in-KOReader shortcut |
Never enable 'SSH at boot'
Setting /mnt/us/usbnetlite/auto makes the Kindle enter usbnet mode
during boot. This leaves the framework unable to launch Kindlets — KUAL
shows a blank screen forever — and dropbear does not start either. It
disables the very recovery path it is meant to provide. Tried and reverted
2026-08-03.
Data and backup¶
| What | Where | Backed up |
|---|---|---|
| Book library | /mnt/user/data/media/books/library (2.4 GB) |
Array + pre-change snapshot at /mnt/user/backups/kindle/library-pre-embed-20260803 |
| CWA config | /mnt/cache/appdata/calibre-web-automated |
Appdata backup |
| Kindle config | /mnt/us/koreader, /mnt/us/extensions |
recovery-kit-20260802/device-config/ on Lotus |
| Jailbreak toolkit | AdBreak, hotfix, KUAL, MRPI, KOReader | recovery-kit-20260802/toolkit/ on Lotus |
Known issues¶
KOReader sync-cap bug. fillPendingSyncs advances last_download to the
newest feed entry even when the download list was truncated at
sync_max_dl. Any bulk addition larger than the cap silently loses the
overflow, and sync still reports success. Mitigated by raising the cap to 100,
not fixed. After a bulk import, always compare device book count against
shelf count.
Never use Force sync. It sets sync_force = true, bypassing both the
last_download guard and the per-file duplicate check, re-downloading books
already present.
Duplicate scan can wedge CWA. duplicate_scan_frequency was
after_import, which triggered a full-library scan on every ingest. Importing
60 books deadlocked the web worker and took Calibre-Web down for ~10 minutes.
Now set to manual. See Lessons Learned.
Display modes cause apparent hangs. CoverBrowser's first-run defaults set
History and Collections to mosaic_image, which extracts a cover thumbnail per
book. On 500+ books on a PW4 the device appears frozen for many minutes. All
three display modes are now classic/no-images. These are stored separately —
changing the file browser setting does not change History or Collections.
Multi-author naming divergence. KOReader names multi-author books using the
last <author> element (Kevin J. Anderson - House Atreides), while
books seeded from Calibre use all authors. Both conventions exist on the device.
Harmless — search matches either — but exact-filename comparison between device
and library will produce false mismatches. Compare by title.
Tailscale cannot work on this device. The kernel has CONFIG_TUN unset, so
tailscaled can only run -tun userspace-networking, which gives applications
no route to 100.x addresses. Inbound SSH over the tailnet would work; outbound
(what sync needs) does not.
Partner note¶
👤 No partner interaction. If the Kindle stops receiving books it is a convenience issue, not a household outage.
Related¶
- Primary NAS — Lotus
- Network — IoT VLAN isolation
- Lessons Learned