Kobo sync API #4

Merged
arne merged 19 commits from kobo-sync into main 2026-04-11 14:09:40 +02:00
Owner

Plan 3 of 4. The /api/kobo/{token}/v1/... HTTP surface that lets a stock-firmware Kobo Libra Color browse the shelf, download books, and push reading state. Wire shapes are taken verbatim from grimmory.

What's in here

  • internal/kobo — Handler, requireToken middleware, snapshot builder + delta, sync token codec, Kobo 7-decimal timestamp codec, every JSON DTO, and one file per endpoint group
  • internal/store — kobo_sync_sessions CRUD with auto-supersede, reading_state CRUD with per-field LastModified merge, device_removals CRUD, GetBookByID
  • main.go — mounts the kobo router on the same ServeMux as the web router

Endpoints

  • GET /v1/initialization (with x-kobo-apitoken: e30= header)
  • POST /v1/auth/device (random per-call tokens, explicit Content-Length)
  • GET /v1/library/sync (paginated 5/call, snapshot-based, x-kobo-synctoken round-trip)
  • GET /v1/library/{uuid}/metadata
  • GET /v1/library/{uuid}/state
  • PUT /v1/library/{uuid}/state (merge by per-sub-object LastModified)
  • DELETE /v1/library/{uuid} → device_removals
  • GET /v1/books/{uuid}/download
  • GET /v1/books/{uuid}/thumbnail/{w}/{h}/{greyscale}/image.jpg
  • POST /v1/analytics/gettests (mandatory)
  • POST /v1/analytics/event (mandatory)
  • GET /v1/products/{id}/nextread[/rest...] (mandatory)

Out of scope (plan 4)

  • deploy.sh, systemd unit, Caddy config
  • Goroutine that calls DeleteOldKoboSyncSessions (function exists and is unit-tested, just not wired)
  • Web UI for clearing device_removals (minor follow-up)

Fixes applied before PR

  • Superseded session guard in LibrarySync — a stale sync token for a superseded session now starts a fresh session rather than walking the stale one
  • nextread route now catches trailing sub-paths via {rest...} wildcard

Test plan

  • go test ./... -race clean across all 9 packages
  • Hands-off bootstrap fails fast at OIDC discovery
  • Full smoke (you, after merge): paste the URL into your Libra Color's Kobo eReader.conf, trigger sync, confirm shelved books appear on the device, open one, let it read, confirm progress syncs back via /state

Known sharp edges

  • Removed entitlements omit the BookMetadata block. Grimmory includes a minimal metadata block even for removals. The spec allows omission; real-device testing will confirm whether the Libra Color tolerates it.
  • nextread matches both the exact path and /nextread/{rest...} — if the device sends something stranger the catch-all returns {} per plan 2's design.

Plan: docs/superpowers/plans/2026-04-11-kobo-sync.md

Plan 3 of 4. The /api/kobo/{token}/v1/... HTTP surface that lets a stock-firmware Kobo Libra Color browse the shelf, download books, and push reading state. Wire shapes are taken verbatim from grimmory. ## What's in here - internal/kobo — Handler, requireToken middleware, snapshot builder + delta, sync token codec, Kobo 7-decimal timestamp codec, every JSON DTO, and one file per endpoint group - internal/store — kobo_sync_sessions CRUD with auto-supersede, reading_state CRUD with per-field LastModified merge, device_removals CRUD, GetBookByID - main.go — mounts the kobo router on the same ServeMux as the web router ## Endpoints - GET /v1/initialization (with x-kobo-apitoken: e30= header) - POST /v1/auth/device (random per-call tokens, explicit Content-Length) - GET /v1/library/sync (paginated 5/call, snapshot-based, x-kobo-synctoken round-trip) - GET /v1/library/{uuid}/metadata - GET /v1/library/{uuid}/state - PUT /v1/library/{uuid}/state (merge by per-sub-object LastModified) - DELETE /v1/library/{uuid} → device_removals - GET /v1/books/{uuid}/download - GET /v1/books/{uuid}/thumbnail/{w}/{h}/{greyscale}/image.jpg - POST /v1/analytics/gettests (mandatory) - POST /v1/analytics/event (mandatory) - GET /v1/products/{id}/nextread[/rest...] (mandatory) ## Out of scope (plan 4) - deploy.sh, systemd unit, Caddy config - Goroutine that calls DeleteOldKoboSyncSessions (function exists and is unit-tested, just not wired) - Web UI for clearing device_removals (minor follow-up) ## Fixes applied before PR - Superseded session guard in LibrarySync — a stale sync token for a superseded session now starts a fresh session rather than walking the stale one - nextread route now catches trailing sub-paths via {rest...} wildcard ## Test plan - [x] go test ./... -race clean across all 9 packages - [x] Hands-off bootstrap fails fast at OIDC discovery - [ ] Full smoke (you, after merge): paste the URL into your Libra Color's Kobo eReader.conf, trigger sync, confirm shelved books appear on the device, open one, let it read, confirm progress syncs back via /state ## Known sharp edges - Removed entitlements omit the BookMetadata block. Grimmory includes a minimal metadata block even for removals. The spec allows omission; real-device testing will confirm whether the Libra Color tolerates it. - nextread matches both the exact path and /nextread/{rest...} — if the device sends something stranger the catch-all returns {} per plan 2's design. Plan: docs/superpowers/plans/2026-04-11-kobo-sync.md
arne added 19 commits 2026-04-11 13:32:23 +02:00
arne merged commit 1869bfe630 into main 2026-04-11 14:09:40 +02:00
arne referenced this pull request from a commit 2026-04-11 14:09:42 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
arne/books!4
No description provided.