From 8599e5c2503b567a7747e56041fd17162183b000 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Fri, 14 Aug 2026 08:26:47 -0400 Subject: [PATCH] docs(sync): document SHA-256 fields and format-aware matching in koreader protocol Bring the koreader protocol doc in line with the hash-sharing work: - Request table: uuid is no longer required (it is absent on the first sync of a newly downloaded book); document sha256 and file_path and the resolution priority uuid -> sha256 -> file_path alias -> title/author - Note that SHA-256 matching is format-aware (media_items hash first, media_item_formats fallback) so converted KEPUB/PDF downloads match - Document the sha256 field returned by the metadata and library endpoints - Add a 'Book identification' section pointing current and future clients (koreader, kobo, OPDS, device-link UI, mobile apps) at the shared BookResolver as the single resolution path --- docs/developer/api/sync/koreader-protocol.md | 53 ++++++++++++++------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/developer/api/sync/koreader-protocol.md b/docs/developer/api/sync/koreader-protocol.md index 64c6a10..5ab71e9 100644 --- a/docs/developer/api/sync/koreader-protocol.md +++ b/docs/developer/api/sync/koreader-protocol.md @@ -17,20 +17,29 @@ KOReader uses a custom JSON-based sync protocol. ### Request Body -| Field | Type | Required | Description | -| ------------------ | ------- | -------- | ----------------------------- | -| library_id | string | No | Library UUID | -| books | array | Yes | Array of book sync data | -| books[].uuid | string | Yes | Book UUID | -| books[].title | string | Yes | Book title | -| books[].authors | array | Yes | Array of author names | -| books[].progress | float | Yes | Progress percentage (0-1) | -| books[].percentage | float | Yes | Progress percentage (0-1) | -| books[].last_read | string | Yes | ISO 8601 timestamp | -| books[].chapter | integer | No | Current chapter | -| books[].epubcfi | string | No | EPUB CFI location | -| books[].character | integer | No | Character offset | -| books[].bookmarks | array | No | Array of bookmarks/highlights | +| Field | Type | Required | Description | +| ------------------ | ------- | -------- | ---------------------------------------------------- | +| library_id | string | No | Library UUID | +| books | array | Yes | Array of book sync data | +| books[].uuid | string | No\* | Book UUID (highest-confidence match; omitted on first sync of a newly downloaded book) | +| books[].sha256 | string | No\* | Full-file SHA-256 (64 hex chars); used to resolve the book when `uuid` is absent | +| books[].file_path | string | No | Device-local file path; used to create/look up a device file alias | +| books[].title | string | Yes | Book title | +| books[].authors | array | Yes | Array of author names | +| books[].progress | float | Yes | Progress percentage (0-1) | +| books[].percentage | float | Yes | Progress percentage (0-1) | +| books[].last_read | string | Yes | ISO 8601 timestamp | +| books[].chapter | integer | No | Current chapter | +| books[].epubcfi | string | No | EPUB CFI location | +| books[].character | integer | No | Character offset | +| books[].bookmarks | array | No | Array of bookmarks/highlights | + +\* At least one of `uuid` or `sha256` should be present. The server resolves the +book through the shared `BookResolver` with this priority: `uuid` → `sha256` → +`file_path` alias → `title`/`author`. SHA-256 matching is **format-aware**: it +checks `media_items.file_sha256` first, then `media_item_formats.file_sha256`, so +a converted file (e.g. KEPUB or PDF) downloaded via OPDS matches even though its +hash differs from the primary format's hash. ### Example Request @@ -102,6 +111,7 @@ Authorization: Bearer device-token ```json { "uuid": "book-uuid", + "sha256": "ff3e4501bf9d72dea2ae28731a6cb5b83d7a7532c05b5d2dd083d0dbc9193ebf", "title": "Book Title", "authors": ["Author Name"], "progress": { @@ -119,3 +129,18 @@ Authorization: Bearer device-token "last_sync": "2026-01-30T20:00:00Z" } ``` + +`sha256` is the canonical primary-format hash of the book on the server. It is +returned so clients can cache it regardless of how the book was originally +obtained. The library list endpoint (`GET /api/sync/koreader/library`) includes +the same `sha256` field on each book. + +## Book identification + +Every client/sync interface (KOReader, Kobo, OPDS, the device-link UI, and any +future mobile app) resolves books through a single shared service: +[`internal/services/book_resolver.go`](../../../internal/services/book_resolver.go). +The import-time SHA-256 (stored on `media_items.file_sha256`, plus a per-format +hash on `media_item_formats.file_sha256` for KEPUB/PDF) is the canonical shared +identifier. New clients should resolve by SHA-256 via `BookResolver` rather than +re-implementing their own matcher.