← back to the office

My Kindle joined the tailnet, and my Pi steals its notes

Sep 2026 · kindle, jailbreak, tailscale, self-hosting

The native Kindle keyboard is a crime against note taking, and pulling highlights out over USB is archaeology. So I jailbroke a 2015 Paperwhite, put it on my tailnet with userspace Tailscale, and now jacinto steals my reading notes into Obsidian every ten minutes. No cable, no touching the device. Same pattern as the pentest: sensor on one side, Pi on the other.

The jailbreak that should not have worked

My Paperwhite 3 (codename Coyote, 7th gen, 2015) sits on firmware 5.16.2.1.1, the last build Amazon ever gave it. That abandonment is what makes the jailbreak possible: LanguageBreak (nov 2023) works up to exactly that firmware. Demo mode, a secret two-finger gesture, and a payload whose filename is literally the exploit, dropped byte-identical on FAT32. A drag-and-drop file manager breaks it silently; the .demo folder is hidden. Full backup first: the jailbreak wipes everything.

First, kill the update path

Kindle on wifi with fresh firmware equals dead jailbreak. Before any network, the OTA binaries die: renameotabin renames otaupd and otav3 to .bck in /usr/bin. No KUAL installed, so I ran it through the bridge emergency hook, a script that runs as root at boot. Log says "OTA BLOCKED OK".

Tailscale on 512 MB of RAM

tailscale_kual runs tailscaled in userspace mode, no TUN device needed — good, because a 3.0.35 kernel from 2015 has none. Static ARM binaries. The killer feature: tailscale up --ssh gives integrated Tailscale SSH, zero dropbear, and the node shows up as "kindle" on my tailnet. One lesson: the auth key must be reusable; the first single-use one burned itself when the wifi slept mid-registration. And screen off means wifi asleep means node offline. The Kindle is not a 24/7 citizen.

The thing that broke the boot

First persistence attempt: an upstart job with respawn that waited up to ninety seconds for wifi. Result: white screen after reboot. The framework never renders the UI, but the system stays alive underneath and the USB volume mounts fine. Recovery ran through the same bridge hook: remove the job, kill tailscaled, reboot. Lesson: no extra services on Kindle upstart; the framework does not tolerate a job that hangs.

The hook that stuck

Final design: nothing at boot. KOReader's launcher carries a hook: if tailscaled is not running, it starts the autostart in the background. Screen on, wifi active — the right moment, because it is exactly when you are reading.

# koreader.sh, right after the FW version check
if ! pidof tailscaled >/dev/null 2>&1; then
    TS_AUTOSTART="/mnt/us/extensions/tailscale/bin/tailscale-autostart.sh"
    if [ -f "${TS_AUTOSTART}" ]; then
        logmsg "tailscale: launching autostart (bg)"
        nohup sh "${TS_AUTOSTART}" >/dev/null 2>&1 &
    fi
fi

The autostart is an idempotent watchdog: start tailscaled userspace, tailscale up --ssh, retry with backoff. The whole pipeline lives in a private repo, including a patch script that re-applies the hook after KOReader updates, with a byte-identical roundtrip on remove.

The pipeline

KOReader stores highlights and notes in the book sidecar (documents/<book>.sdr/metadata.pdf.lua): an annotations table with page, chapter, color, note and timestamp. jacinto runs a cron every ten minutes: tailscale ping kindle, and if online, scp the sidecars down — rsync does not exist in the Kindle busybox. The mini pulls them from jacinto and a parser merges them into the vault with idempotent dedup.

Verification

End to end, live: opened KOReader with the Kindle unplugged. The hook fired, tailscaled came up, the node answered with a direct LAN pong in 13 ms, no DERP relay. ssh root@kindle from the laptop, banner SSH-2.0-Tailscale. jacinto's log: "kindle online, scraping... done: 3 sidecars". The vault sync: "sync OK: 3 sidecars -> vault", unchanged, idempotent.

What it taught me

  • An abandoned device is a perfect sensor. Nobody updates it, so the jailbreak is stable by neglect.
  • Persistence should match when the device is awake, not force it awake. A daemon that fights the sleep cycle loses.
  • An e-ink with 512 MB runs userspace Tailscale and SSH without drama, as long as nothing else competes for the RAM.
  • The note flow is now: highlight on the Kindle, notes in Obsidian, cable never involved.

The reliable choice, again

The fleet grew a limb. Same lesson as always: self-hosting is a chain of small, boring decisions, and the most reliable ones are the ones that run while you sleep. My highlights walk to my vault on their own now.