A Kindle That Reads From My Home Library
This post was drafted with Codex from a local Obsidian setup guide and a live Kindle configuration session.
I wanted the Kindle to stop being a little island.
The Amazon side of it is fine for bought books, but that is not where my reading stack lives. My library is on a Mac mini. A lot of the books came through Calibre. Articles come through wallabag. PDFs and EPUBs move around between machines. I do not want to manually copy files to the Kindle every time I decide to read something.
So the target became:
Kindle
runs KOReader
joins my tailnet
talks to BookOrbit on the Mac mini
downloads books on demand
keeps the reading UI quiet
The final result is a jailbroken Kindle Paperwhite 5 running KOReader. It connects to a self-hosted BookOrbit library over Tailscale. KOReader starts in a custom Bookshelf home screen, uses BookOrbit as the remote catalog, keeps a handful of reading statistics and sleep-screen niceties, and no longer has a giant long-press menu full of things I do not use.
This is the public version of my setup note. The internal guide has exact hostnames, IPs, and operational notes. Secrets are intentionally omitted here.
The Shape
The architecture is small:
Kindle PW5 / KOReader
-> BookOrbit KOReader plugin
-> local Tailscale SOCKS5 proxy on the Kindle
-> Tailscale userspace networking
-> Mac mini on the tailnet
-> BookOrbit Docker service
-> Calibre-derived library folder
BookOrbit runs on the Mac mini in Docker. The Kindle reaches it through Tailscale, not through a public port. KOReader does not use a global HTTP proxy. Instead, the BookOrbit plugin is patched so BookOrbit traffic to tailnet addresses goes through Tailscale’s local SOCKS proxy.
That detail matters. The naive setup is to turn on KOReader’s global HTTP proxy and hope every plugin behaves. In practice that made BookOrbit fail in weird ways. The clean path was narrower:
KOReader global HTTP proxy: disabled
BookOrbit plugin: uses 127.0.0.1:1055 SOCKS5 for tailnet BookOrbit URLs
Tailscale plugin: forced to userspace networking
Once that was true, the Kindle could browse and download from the library without the rest of KOReader being routed through a proxy it did not need.
Baseline Kindle
The device is a Kindle Paperwhite 5 on firmware 5.19.2, jailbroken with SpringBreak. KUAL worked after the jailbreak, and KOReader was installed through KPM.
The important installed pieces are:
- KOReader
- KUAL
- KPM
- KOAssistant
- Bookshelf from
AndyHazz/bookshelf.koplugin - BookEnds from
AndyHazz/bookends.koplugin - App Store plugin
- BookOrbit Sync plugin from the running BookOrbit container
- Tailscale VPN plugin from
victoria-riley-barnett/koreader-tailscale
BookOrbit provides its own KOReader plugin from inside the container:
/app/koreader-plugin/bookorbit.koplugin
When the Kindle is mounted over USB, that plugin lives at:
/Volumes/Kindle/koreader/plugins/bookorbit.koplugin
At runtime, KOReader sees it under:
/mnt/us/koreader/plugins/bookorbit.koplugin
The BookOrbit sync user is created in the BookOrbit web UI under Settings -> KOReader -> Sync Settings. The KOReader plugin stores a username and a derived user key. Do not put the provision file or derived key in notes.
BookOrbit On The Mac Mini
BookOrbit runs from:
~/Services/bookorbit
The service is a Docker Compose stack:
bookorbit-app, exposed on port3000bookorbit-db, backed by Postgres/pgvector
The Calibre import lives under a BookOrbit library folder:
/books/calibre
I used BookOrbit’s “Folder as Book” mode because Calibre stores each book as a folder with the ebook file plus metadata and cover sidecars:
/books/calibre/Author/Title (id)/book.ext
/books/calibre/Author/Title (id)/metadata.opf
/books/calibre/Author/Title (id)/cover.jpg
There was one annoying import repair worth remembering. A Stallings operating systems book looked like a PDF by filename, but the file header was really a Mobipocket/AZW4-style wrapper. BookOrbit imported it as a PDF and the web reader froze. Renaming to MOBI did not help. Converting it to EPUB with Calibre, removing the broken wrapper, and rescanning BookOrbit fixed it.
That is the kind of thing I prefer to solve on the server side. The Kindle should not have to know that a random .pdf was lying.
Tailscale On The Kindle
The Kindle joins the tailnet through the KOReader Tailscale plugin, not the older standalone KUAL extension path.
The plugin path is:
/Volumes/Kindle/koreader/plugins/tailscale.koplugin
The plugin exposes local proxy listeners inside KOReader’s environment:
SOCKS5: 127.0.0.1:1055
HTTP CONNECT: 127.0.0.1:1056
For this setup, SOCKS5 is the important one.
The key patch was forcing Tailscale into userspace networking. The plugin initially preferred kernel TUN when /dev/net/tun existed. That made Tailscale appear connected, but SOCKS upstream dials to the Mac mini timed out.
The working behavior is:
function TailscalePlugin:resolveTunFlag()
self._tun_flag = "--tun=userspace-networking"
end
After that, BookOrbit traffic through the Kindle’s local SOCKS proxy started working.
BookOrbit’s Transport Patch
The second important patch is in:
/Volumes/Kindle/koreader/plugins/bookorbit.koplugin/bookorbit_api.lua
The intent is simple:
- If the BookOrbit server URL is a tailnet URL, route through
127.0.0.1:1055. - Use that SOCKS transport for catalog and sync requests.
- Use the same path for downloads, thumbnails, and plugin-update downloads.
The symptoms during debugging were useful:
Connection refused: Tailscale’s local proxy was not running.Server timeout: traffic was not routing correctly.socks connect failed 1: BookOrbit reached SOCKS5, but Tailscale failed the upstream dial.i/o timeoutfrom the SOCKS listener: the SOCKS path was real, but the tailnet path was not working yet.
For me, forcing userspace networking was the fix.
KOReader Home Screen
I use Bookshelf as the KOReader start screen:
["start_with"] = "bookshelf"
["home_dir"] = "/mnt/us/documents/BookOrbit"
The home directory points at BookOrbit’s local document area, but I kept the BookOrbit standalone cache. That cache is useful. The cleanup I did later was only for local Kindle/Amazon copies of books I could already reach from BookOrbit.
Bookshelf gives me a calmer starting point than the default file browser. My enabled tabs are basically:
- Home
- Wallabag
- Recent
The start menu has quick access to things I actually use:
- quote
- reading calendar
- Wi-Fi toggle
- night mode toggle
- Bookshelf menu
- exit or close book
- sleep
BookEnds handles the reader progress display. The built-in KOReader footer is off; BookEnds gives a minimal bottom progress bar without filling the page with status text.
Storage Cleanup
Once BookOrbit worked, the Kindle did not need some duplicate local books anymore. I removed the local Kindle storage copies for books that were already available through BookOrbit.
I did not delete the BookOrbit standalone cache.
I also deleted all ._* AppleDouble metadata files created by macOS. KOReader plugins can warn about these, especially when directory scanners see ._filename sidecars as if they were real files.
The rule is safe:
find /Volumes/Kindle -name '._*' -type f -delete
After every round of installing or editing from macOS, I check that the count is zero again.
Plugins Added Later
After the base BookOrbit/Tailscale setup, I added a few more pieces.
First, Customisable Sleep Screen:
/Volumes/Kindle/koreader/plugins/customisablesleepscreen.koplugin
Source:
https://github.com/pxlflux/customisablesleepscreen.koplugin
The setting that enables it is:
["screensaver_type"] = "customisable_ss"
I configured it to use the current book cover as the background, with a quiet top-left information box: title, author, series, pages, reading time remaining, chapter time remaining, daily goal/streak, battery, time, and date.
I originally used Atkinson Hyperlegible Next and then immediately decided it looked bad here. The current sleep screen uses Literata, which fits the Kindle better.
Second, Reading Insights:
/Volumes/Kindle/koreader/plugins/readinginsights.koplugin
Source:
https://github.com/peterboda236/readinginsights.koplugin
Reading Insights does not create a separate reading database. It reads KOReader’s existing statistics.sqlite3. My built-in KOReader statistics were already enabled:
["statistics"] = {
["is_enabled"] = true,
["min_sec"] = 5,
["max_sec"] = 120,
}
After restart, Reading Insights appears under:
Tools -> Reading insights
When a book is open, it also exposes book-specific progress and calendar views.
Third, I installed two user patches:
/Volumes/Kindle/koreader/patches/2-bookloadcover.lua
/Volumes/Kindle/koreader/patches/2-frontlight-widget-nightmode.lua
Sources:
https://github.com/reuerendo/koreader-patches
https://github.com/peterboda236/koreader-user-patches
2-bookloadcover.lua shows the book cover while documents open. I looked at 2-bookloadcover-plus.lua, but I did not install it. The plus version is more configurable. I did not need configurability; I just wanted to see the cover.
The night-mode frontlight patch makes the frontlight behavior better when switching night mode.
Sleep Mode
This part is easy to misread.
My current settings are:
["auto_standby_timeout_seconds"] = -1
["auto_suspend_timeout_seconds"] = 900
["autoshutdown_timeout_seconds"] = 259200
["screensaver_delay"] = "disable"
["screensaver_type"] = "customisable_ss"
That means:
- automatic standby is disabled
- automatic suspend/sleep happens after 15 minutes
- full shutdown happens after 3 days
- the custom sleep screen is enabled
- the sleep screen does not linger after wake
screensaver_delay = "disable" does not disable sleep. It only disables the post-wake delay where the sleep screen remains visible until a tap or gesture.
This is the behavior I want: sleep works, the custom sleep screen appears while sleeping, and wake returns straight to reading.
Highlight Menu Pruning
The last bit of tuning was the long-press text menu.
Out of the box, text selection can become huge: highlight, note, copy, dictionary, Wikipedia, translate, search, and then a pile of KOAssistant actions like Translate, Explain, ELI5, Summarize, Connect, Fact Check, AI Wiki, and Grammar.
That is not a reading UI. That is a control panel.
I kept KOAssistant available, but removed the direct action spam from the highlight popup:
["show_quick_actions_in_highlight"] = false
["highlight_menu_actions"] = {}
Then I added a tiny KOReader patch:
/Volumes/Kindle/koreader/patches/2-simplify-highlight-menu.lua
It removes built-in menu items I do not want on every text selection:
- Add note
- Wikipedia
- Translate
- View HTML
- Hyphenate
- Search
The resulting menu is much closer to:
Select / Extend
Highlight
Copy
Dictionary
Follow Link, only when relevant
Chat/Action (KOA)
I left KOReader’s default_highlight_action = "ask" setting in place, so the simplified menu still appears. If I ever want long-press-drag to immediately highlight without a menu, that setting can become:
["default_highlight_action"] = "highlight"
For now, the smaller menu is the right compromise.
Things I Did Not Install
I skipped File Browser Plus and LocalSend.
File Browser Plus may be useful if KOReader’s file browser is your main way of moving around. It is less important when Bookshelf and BookOrbit are the primary surfaces.
LocalSend is useful for quick local file transfer. I do not need it here because BookOrbit is already the file delivery path, and I would rather avoid another transfer ritual.
The rule for this Kindle is:
install tools that make reading quieter
skip tools that create another place to manage files
Recovery Checklist
When BookOrbit stops working, check in this order:
- BookOrbit health on the Mac mini.
- Docker containers for
bookorbit-appandbookorbit-db. - Tailscale status on the Mac mini.
- Tailscale VPN status inside KOReader on the Kindle.
- Kindle Tailscale logs under the plugin’s
binfolder. - The Tailscale startup arguments include
--tun=userspace-networking. - KOReader global HTTP proxy is disabled.
- The BookOrbit plugin still has the SOCKS transport patch.
- KOReader was restarted after any plugin or settings change.
- macOS
._*metadata files are gone.
The final step is not cosmetic. KOReader plugins will sometimes warn or behave oddly when macOS sidecar files sit next to real plugin and settings files.
The Takeaway
The interesting part of this setup is not any single plugin.
It is the boundary:
BookOrbit = library and catalog
Tailscale = private transport
KOReader = reading environment
Bookshelf = home screen
BookEnds = reading progress
Reading Insights = history view
Customisable Sleep Screen = suspended state
KOAssistant = optional thinking tool, not the whole UI
Once each tool stays in its lane, the Kindle starts feeling less like a hacked gadget and more like a small reading appliance.
The server can be complicated. The tailnet can be weird. The plugin patches can be a little sharp-edged. But the thing in my hand should be simple:
open KOReader, browse the home library, read.