No description
  • Go 82.5%
  • HTML 10.2%
  • CSS 6.5%
  • JavaScript 0.4%
  • Shell 0.4%
Find a file
2026-04-24 09:40:14 +02:00
design Description reveal and dropcap on the book page 2026-04-13 21:36:50 +00:00
docs Per-book reading stats on the book page (#25) 2026-04-24 09:40:14 +02:00
internal Per-book reading stats on the book page (#25) 2026-04-24 09:40:14 +02:00
scripts Add Basefile and release script for bases deployment (#18) 2026-04-14 10:34:04 +02:00
.gitignore Foundation: importer pipeline + storage (#1) 2026-04-10 20:32:45 +02:00
Basefile Release v0.1.3 2026-04-14 11:39:43 +00:00
go.mod Kobo: progress tracking and kepubify (#8) 2026-04-12 15:24:43 +02:00
go.sum Kobo: progress tracking and kepubify (#8) 2026-04-12 15:24:43 +02:00
main.go First-login owner (#19) 2026-04-14 13:33:11 +02:00
README.md docs: refactor Kobo API findings into a reference document (#22) 2026-04-20 11:02:20 +02:00

books

Personal book server with a Kobo sync endpoint. Imports EPUBs from an inbox directory, stores them in a library, exposes a web UI over OIDC, and pretends to be the Kobo store so a stock-firmware Kobo will treat the shelved subset as "purchased" books and download them.

Status: used in production for one user and one device (a Libra Colour on firmware 4.45.23646). Everything else is speculative.

Running the server

The books binary reads config from environment variables. Minimum set:

BOOKS_DATA_DIR=/opt/books              # sqlite db, inbox/, library/
BOOKS_LISTEN_ADDR=0.0.0.0:8090
BOOKS_PUBLIC_URL=https://books.example.com
BOOKS_OIDC_ISSUER=https://auth.example.com
BOOKS_OIDC_CLIENT_ID=...
BOOKS_OIDC_CLIENT_SECRET=...

The first successful OIDC login claims ownership of the server. Subsequent logins are treated as regular users.

BOOKS_PUBLIC_URL is used as the base for every URL the Kobo is handed (api_endpoint, download URLs, covers). It must be publicly reachable from the device.

Build:

CGO_ENABLED=0 go build -o books .

modernc.org/sqlite is pure-Go, so CGO is off and the binary is statically linked. It runs fine on Alpine / musl.

Pointing a Kobo at the server

One-time: register the device on the web UI

  1. Sign in to the books web UI.
  2. Go to Shelf. Under "your kobo", enter a name and click Register.
  3. Copy the generated URL — it looks like https://books.example.com/api/kobo/<token>. It's per-device and acts as the device's credential; anyone with the URL can sync.

Edit Kobo eReader.conf over USB

The setup changes one line in the Kobo's config file. It does not need SSH, a factory reset, or any other Kobo-side modification. Nickel will rewrite every other URL it needs onto the new api_endpoint host automatically on next sync.

  1. Plug the Kobo into a computer via USB. It appears as a mass-storage volume called KOBOeReader.

  2. Open .kobo/Kobo/Kobo eReader.conf in a text editor.

  3. Under the [OneStoreServices] section, replace the line

    api_endpoint=https://storeapi.kobo.com
    

    with

    api_endpoint=https://books.example.com/api/kobo/<token>
    

    where <token> is the value from the web UI registration.

  4. Save the file, eject the volume, and reboot the Kobo.

On next sync the device will talk to books. Every other URL in [OneStoreServices] still points at storeapi.kobo.com, but Nickel rewrites the host at request time, so Cover images, metadata lookups, device auth, analytics, and the book download all land on the books server.

Verifying it worked

From the Kobo home screen, open Settings → Accounts → Sync now. In the books web log you should see:

GET /api/kobo/<token>/v1/initialization
POST /api/kobo/<token>/v1/auth/device
GET /api/kobo/<token>/v1/library/sync
...

and the device download flow (visible in the web log or the packetdump.debug Qt channel on the Kobo, see docs/kobo-api.md).

Internals and protocol notes

See docs/kobo-api.md for the full reference: endpoint-by-endpoint wire shapes, protocol conventions, known failure modes, and the nc :5001 debugging channel.

Admin API

A token-authenticated JSON API at /api/admin/* for editing book metadata (search, PATCH fields, replace authors, rename authors/series, replace cover/file). See docs/superpowers/specs/2026-04-13-admin-api-design.md for the prose spec and docs/admin-api.openapi.yaml for the OpenAPI 3.1 schema. Authenticate with Authorization: Bearer $BOOKS_ADMIN_TOKEN; if the env var is unset the entire mount returns 404.

Frontend

Server-rendered HTML with htmx for interaction polish (shelve toggle, library search). htmx is vendored at design/htmx.min.js (version recorded in design/htmx.min.js.version). To bump: download the new release artifact from https://github.com/bigskysoftware/htmx/releases, overwrite both files, and verify the existing tests pass.