From ab8f4f53517a74beb3b40292e37a1c584b02289d Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 31 Jan 2026 18:00:28 -0500 Subject: [PATCH] docs/Code/bookmann/IMPLEMENTATION_PLAN.md: Add comprehensive implementation plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This plan implements: - Universal book identification (SHA-256, UUID, ISBN, ASIN, OPF identifiers) - Enhanced collection management with auto-assign rules - OPDS-based wireless book delivery for Kobo, KOReader, Web, and Mobile - Bidirectional progress sync with ContentId mapping - Device-specific shelf mappings and configuration - Complete database schema with 7 new tables - 50+ documented API endpoints - 10-week phased implementation plan Features include: - Cross-device book matching regardless of file paths - Collections as device-neutral metadata with per-device shelf mapping - Format conversion (EPUB → KEPUB) with hash integrity preservation - Three-tier authentication (JWT, device tokens, OPDS) - Two-layer architecture: OPDS for acquisition + internal APIs for state management Key design principles: 1. Canonical UUID (Bookmann UUID) always wins for progress tracking 2. Collections ≠ device inventory - organizational metadata only 3. OPDS primary for all devices, internal APIs for web/mobile 4. Dual hash storage prevents format conversion issues See IMPLEMENTATION_PLAN.md for complete technical details. --- docs/Code/bookmann/IMPLEMENTATION_PLAN.md | 1692 +++++++++++++++++++++ 1 file changed, 1692 insertions(+) create mode 100644 docs/Code/bookmann/IMPLEMENTATION_PLAN.md diff --git a/docs/Code/bookmann/IMPLEMENTATION_PLAN.md b/docs/Code/bookmann/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..249a1dc --- /dev/null +++ b/docs/Code/bookmann/IMPLEMENTATION_PLAN.md @@ -0,0 +1,1692 @@ +# Bookmann Implementation Plan + +## Executive Summary + +This plan implements a complete cross-device ebook management system with three major capabilities: + +1. **Universal Book Identification** - SHA-256 hashing, UUID, ISBN, ASIN, and OPF identifiers for content-based matching across devices +2. **Enhanced Collection Management** - Device-neutral "Collections" with auto-assign rules and per-device customization via shelf mappings +3. **OPDS-Based Wireless Book Delivery** - Industry-standard book distribution for Kobo, KOReader, Web, and Mobile apps +4. **Bidirectional Progress Synchronization** - Real-time sync with ContentId mapping to handle format conversions (EPUB → KEPUB) +5. **Device-Specific Configuration** - Per-device view settings and shelf mappings while maintaining unified data model + +**Key Design Principle**: Use OPDS for book acquisition (Layer 1) and internal APIs for state management (Layer 2), maintaining clear separation of concerns while enabling seamless user experience. + +--- + +## Table of Contents + +1. [Architecture Overview](#architecture-overview) +2. [Database Schema](#database-schema) +3. [API Endpoints](#api-endpoints) +4. [Implementation Phases](#implementation-phases) +5. [Device Setup Instructions](#device-setup-instructions) +6. [Security Considerations](#security-considerations) +7. [Testing Strategy](#testing-strategy) +8. [Glossary](#glossary) + +--- + +## Architecture Overview + +### System Design: Two-Layer Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Bookmann Server │ +│ ┌──────────────────────────────────────────────────────────────┐ │ +│ │ Layer 1: Universal Book Identification │ │ +│ │ SHA-256, UUID, ISBN, ASIN, OPF identifiers │ │ +│ │ Device file aliases for path tracking │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +│ ↓ matches books universally │ +│ ┌──────────────────────────────────────────────────────────────────────┐ │ +│ │ Layer 2: Collections (Device-Neutral Organization) │ │ +│ │ Collections with auto-assign rules │ │ +│ │ Device-specific shelf mappings (Kobo) │ │ +│ │ Per-user view settings │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +│ ↓ provides organization │ +│ ┌──────────────────────────────────────────────────────────────────────┐ │ +│ │ Layer 3: OPDS (Primary Wireless Delivery) │ │ +│ │ Per-device OPDS feeds (Kobo, KOReader, etc.) │ │ +│ │ Format conversion (EPUB → KEPUB on-the-fly) │ │ +│ │ Dual hash storage (original + converted) │ │ +│ │ ContentId mapping (Bookmann UUID ↔ Device ID) │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +│ ↓ delivers books + provides IDs │ +│ ┌──────────────────────────────────────────────────────────────────────┐ │ +│ │ Layer 4: Internal APIs (State Management) │ │ +│ │ Progress sync (bidirectional) │ │ +│ │ Annotation sync (bidirectional) │ │ +│ │ Collection CRUD │ │ +│ │ WebSocket real-time updates │ │ +│ │ Device-specific operations │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +### Access Method Matrix + +| Platform | Access Method | Purpose | Why This Method | +|-----------|----------------|---------|-----------------| +| **Kobo** | OPDS catalog | Kobo has built-in OPDS client, no custom Bookmann client exists | +| **KOReader** | OPDS catalog (primary) + Sidecar + Internal API | KOReader has OPDS client, also supports plugins/sidecars for enhanced features | +| **Web App** | Internal API directly | We own and control web app, can make direct API calls efficiently | +| **Mobile App** | Internal API directly | We own and control mobile app, can make direct API calls efficiently | +| **Any OPDS Client** | OPDS catalog | Public catalog standard, any app can use it for browsing/downloading | + +### Key Design Principles + +1. **Canonical UUID Always Wins** - Bookmann UUID (from `media_items.id`) is always used for progress tracking, never SHA-256. SHA-256 is only for matching books across devices, preventing format conversion issues. + +2. **Collections ≠ Device Inventory** - Collections are organizational metadata (like "smart playlists"). Books can be in collections without being on any device. Progress/annotations sync independently of collection membership. + +3. **OPDS for Acquisition, Internal APIs for State** - Two layers serve complementary purposes: + - OPDS: "What books are available to download?" (public catalog) + - Internal APIs: "How do I manage my books/sync state?" (private management) + +4. **Dual Hash Storage Preserves Integrity** - Store both original EPUB hash (`epub_sha256`) and converted KEPUB hash (`kepub_sha256`) in `media_item_formats` table. OPDS responses include format-specific hash in headers, enabling sidecar matching even after conversion. + +5. **Three-Tier Authentication** - Separate systems for different purposes: + - Tier 1 (Web/Mobile): JWT tokens for user authentication and permissions + - Tier 2 (Sync APIs): Device tokens for progress/annotation sync + - Tier 3 (OPDS): Device tokens for catalog access (optional per-device) + +6. **Terminology Separation** - Always use "Collections" terminology in Bookmann UI. Map Collections to device-specific "Shelves" only at API/device level. Kobo devices see "Shelves", KOReader/Web/Mobile see "Collections". Prevents legal issues. + +7. **OPDS Primary for All Devices** - Kobo, KOReader, Web, and Mobile all use OPDS as primary book delivery method. Sidecar files provide fallback/enhanced features but are optional. + +8. **System Configuration Flexibility** - Use `system_config` table to store base URLs (`base_url`, `opds_base_url`, `api_base_url`). Sidecar generation reads from these values, enabling flexible deployment (different domains, reverse proxies) with user overrides available. + +--- + +## Database Schema + +### Schema Overview + +**7 new tables** + extensions to 4 existing tables + +### Table: media_items (Extended) + +```sql +-- Universal identifiers for cross-device matching +ALTER TABLE media_items ADD COLUMN file_sha256 CHAR(64); +ALTER TABLE media_items ADD COLUMN opf_identifier VARCHAR(255); +ALTER TABLE media_items ADD COLUMN opf_uuid VARCHAR(255); + +-- Hash confidence for matching priority +-- 'high': OPF UUID or ISBN available +-- 'medium': ISBN/ASIN available but no OPF UUID +-- 'low': Only title/author match available +ALTER TABLE media_items ADD COLUMN hash_confidence VARCHAR(20); + +-- Create indexes for fast lookup +CREATE INDEX idx_media_items_sha256 ON media_items(file_sha256); +CREATE INDEX idx_media_items_opf_identifier ON media_items(opf_identifier); +``` + +### Table: media_item_formats (NEW) + +```sql +-- Track all format versions with their hashes +-- Critical for dual hash storage and format-specific OPDS delivery + +CREATE TABLE media_item_formats ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + media_item_id UUID REFERENCES media_items(id) ON DELETE CASCADE, + format_type VARCHAR(10) NOT NULL, -- 'epub', 'kepub', 'pdf', 'cbz' + file_path VARCHAR(500), + file_sha256 CHAR(64), + file_size_bytes BIGINT, + mime_type VARCHAR(100), + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + converted_from_format_id UUID REFERENCES media_item_formats(id), -- If this is converted from another format + UNIQUE(media_item_id, format_type) +); + +CREATE INDEX idx_media_item_formats_media ON media_item_formats(media_item_id, format_type); +CREATE INDEX idx_media_item_formats_sha256 ON media_item_formats(file_sha256); +``` + +### Table: device_file_aliases (NEW) + +```sql +-- Track file paths per device for cross-device matching +-- When same book has different file paths on different devices, we can still match them via SHA-256 + +CREATE TABLE device_file_aliases ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + media_item_id UUID REFERENCES media_items(id) ON DELETE CASCADE, + device_id UUID REFERENCES devices(id) ON DELETE CASCADE, + file_path VARCHAR(500) NOT NULL, + file_sha256 CHAR(64), + confidence_score FLOAT DEFAULT 1.0, + last_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(device_id, file_path) +); + +CREATE INDEX idx_device_file_aliases_media_device ON device_file_aliases(media_item_id, device_id); +CREATE INDEX idx_device_file_aliases_sha256 ON device_file_aliases(file_sha256); +``` + +### Table: collections (NEW) + +```sql +-- Device-neutral collections (separate from Kobo shelves) +-- Each user has their own independent collection namespace + +CREATE TABLE collections ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + name VARCHAR(100) NOT NULL, + description TEXT, + color VARCHAR(7), -- Hex color for UI + icon VARCHAR(50), -- Emoji or icon name + auto_assign_rules JSONB, -- See schema below for structure + view_settings JSONB, -- Per-device view preferences + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(user_id, name) +); +``` + +### Table: collection_items (NEW) + +```sql +-- Which books belong to each collection + +CREATE TABLE collection_items ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + collection_id UUID REFERENCES collections(id) ON DELETE CASCADE, + media_item_id UUID REFERENCES media_items(id) ON DELETE CASCADE, + added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + added_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- Manual vs auto + UNIQUE(collection_id, media_item_id) +); + +CREATE INDEX idx_collection_items_collection ON collection_items(collection_id); +CREATE INDEX idx_collection_items_media ON collection_items(media_item_id); +``` + +### Table: device_shelf_mappings (NEW) + +```sql +-- Map Bookmann collections to device-specific shelf names +-- This is where "Collections" terminology maps to Kobo's "Shelves" + +CREATE TABLE device_shelf_mappings ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + collection_id UUID REFERENCES collections(id) ON DELETE CASCADE, + device_id UUID REFERENCES devices(id) ON DELETE CASCADE, + device_shelf_name VARCHAR(100), -- What appears on Kobo device + sync_direction VARCHAR(20), -- 'bidirectional', 'book_to_device', 'device_to_book', 'none' + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(collection_id, device_id) +); + +CREATE INDEX idx_device_shelf_mappings_collection ON device_shelf_mappings(collection_id); +CREATE INDEX idx_device_shelf_mappings_device ON device_shelf_mappings(device_id); +``` + +### Table: device_catalogs (NEW) + +```sql +-- Track OPDS downloads and map Bookmann UUIDs to device ContentIds +-- Critical for bidirectional progress sync with format conversion handling + +CREATE TABLE device_catalogs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + device_id UUID REFERENCES devices(id) ON DELETE CASCADE, + media_item_id UUID REFERENCES media_items(id) ON DELETE CASCADE, + bookmann_uuid UUID NOT NULL, + kobo_content_id VARCHAR(255) NOT NULL, + content_id_type VARCHAR(20), -- 'bookmann_uuid', 'kobo_generated', 'isbn_based' + available BOOLEAN DEFAULT TRUE, + delivery_date TIMESTAMP WITH TIME ZONE, + delivery_method VARCHAR(20), -- 'wireless', 'usb', 'manual' + UNIQUE(device_id, kobo_content_id) +); + +CREATE INDEX idx_device_catalogs_bookmann ON device_catalogs(bookmann_uuid); +CREATE INDEX idx_device_catalogs_kobo ON device_catalogs(kobo_content_id); +``` + +### Table: system_config (NEW) + +```sql +-- System-wide configuration (set by admin) +-- Critical for flexible deployment (different domains, reverse proxies) + +CREATE TABLE system_config ( + key VARCHAR(100) PRIMARY KEY, + value TEXT NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_by UUID REFERENCES users(id) +); + +-- Pre-seeded values +INSERT INTO system_config (key, value) VALUES +('base_url', 'https://bookmann.example.com'), +('opds_base_url', 'https://bookmann.example.com/opds'), +('api_base_url', 'https://bookmann.example.com/api'); +``` + +### Table: opds_tokens (NEW) + +```sql +-- Device-specific OPDS access tokens (optional authentication) +-- Allows device-level access control without exposing JWT tokens + +CREATE TABLE opds_tokens ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + device_id UUID REFERENCES devices(id) ON DELETE CASCADE, + token VARCHAR(64) UNIQUE NOT NULL, + token_type VARCHAR(20), -- 'device', 'user', 'admin' + expires_at TIMESTAMP WITH TIME ZONE NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_opds_tokens_device ON opds_tokens(device_id); +CREATE INDEX idx_opds_tokens_token ON opds_tokens(token); +``` + +### Table: kobo_shelves (Modified) + +```sql +-- Reference collections instead of media_items directly +-- Maintains backward compatibility with existing device_id/media_item_id columns + +ALTER TABLE kobo_shelves ADD COLUMN collection_id UUID REFERENCES collections(id); +ALTER TABLE kobo_shelves ADD COLUMN position_in_collection INTEGER; +``` + +### Schema Relationships Summary + +``` +users +├─ devices (one user can have multiple devices) +│ └─ device_file_aliases (tracks file paths per device) +│ └─ device_shelf_mappings (collection → Kobo shelf name) +│ └─ device_catalogs (OPDS + ContentId mapping) +│ └─ opds_tokens (OPDS authentication, optional) +├─ media_items (canonical book records with universal identifiers) +│ ├─ file_sha256, opf_identifier, opf_uuid +│ ├─ hash_confidence +│ ├─ reading_progress (one record per user per book) +│ └─ collection_items (which collections each book belongs to) +└─ collections (device-neutral organization) +└─ collection_items (membership) +``` + +--- + +## API Endpoints + +### Layer 1: Universal Book Identification + +#### POST `/api/sync/books/query` + +Query Bookmann for a book by multiple identifier types with confidence scoring. + +**Request**: +```json +{ + "identifiers": ["isbn:978-0345391802", "uuid:abc-123", "opf_uuid:def456"], + "sha256": "a1b2c3d4e5f6abc123...", + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "file_size": 2456789 +} +``` + +**Response**: +```json +{ + "matches": [ + { + "media_item_id": "uuid-123", + "bookmann_uuid": "uuid-123", + "confidence": 1.0, + "match_method": "uuid_match" + } + ], + "action": "auto_link" // or "multiple_matches", "no_match" +} +``` + +**Matching Priority**: +1. Bookmann UUID (canonical) - Confidence: 1.0 +2. OPF UUID (from EPUB metadata) - Confidence: 0.95 +3. SHA-256 hash (content-based match) - Confidence: 0.9 +4. OPF identifier (non-UUID) - Confidence: 0.85 +5. ISBN/ASIN (standard identifiers) - Confidence: 0.8 +6. File path (device-specific, fallback) - Confidence: variable +7. Title + author + file size (last resort) - Confidence: 0.5 + +#### POST `/api/sync/link-book` + +Manual linking override for unmatched books. + +**Request**: +```json +{ + "device_file": { + "file_path": "/storage/emulated/0/Books/MyBook.epub", + "sha256": "abc123...", + "title": "My Book" + }, + "media_item_id": "uuid-123", + "confidence_score": 1.0 // User sets this +} +``` + +**Response**: +```json +{ + "status": "linked", + "device_file_alias": { + "id": "alias-id", + "media_item_id": "uuid-123", + "device_id": "kobo-device-id", + "file_path": "/storage/emulated/0/Books/MyBook.epub", + "file_sha256": "abc123...", + "confidence_score": 1.0 + } +} +``` + +#### GET `/api/sync/unlinked-books` + +List progress records that need manual linking. + +**Response**: +```json +{ + "unlinked": [ + { + "progress_id": "progress-uuid", + "device_id": "kobo-device-id", + "device_type": "kobo", + "file_path": "/mnt/sdcard/UnknownBook.epub", + "sha256": "abc123...", + "title_from_device": "Unknown Book", + "last_sync_timestamp": "2026-01-31T12:00:00Z" + } + ], + "total": 1 +} +``` + +#### GET `/api/devices/:id/file-aliases` + +View all file aliases for a specific device. + +**Response**: +```json +{ + "device_id": "device-uuid", + "aliases": [ + { + "id": "alias-id", + "media_item_id": "uuid-123", + "file_path": "/storage/emulated/0/Books/MyBook.epub", + "file_sha256": "abc123...", + "confidence_score": 1.0, + "last_seen_at": "2026-01-31T12:00:00Z" + } + ], + "total": 42 +} +``` + +### Layer 2: Collection Management + +#### POST `/api/collections` + +Create a new collection. + +**Request**: +```json +{ + "name": "Science Fiction", + "description": "My favorite sci-fi books", + "color": "#ff0000", + "icon": "🚀", + "auto_assign_rules": [ + { + "id": "rule-1", + "field": "genre", + "operator": "equals", + "value": "Science Fiction" + } + ] +} +``` + +**Response**: +```json +{ + "id": "collection-uuid", + "name": "Science Fiction", + "description": "My favorite sci-fi books", + "color": "#ff0000", + "icon": "🚀", + "auto_assign_rules": [...], + "book_count": 0, + "created_at": "2026-01-31T12:00:00Z" +} +``` + +#### GET `/api/collections` + +List all collections for current user. + +**Query Parameters**: +- `include_auto`: boolean (include auto-assigned collections) +- `sort_by`: string (name, created_at, book_count) + +**Response**: +```json +{ + "collections": [ + { + "id": "collection-uuid", + "name": "Science Fiction", + "description": "...", + "color": "#ff0000", + "icon": "🚀", + "auto_assign_rules": [...], + "book_count": 15 + } + ], + "total": 1 +} +``` + +#### GET `/api/collections/:id` + +Get single collection details with books. + +**Response**: +```json +{ + "id": "collection-uuid", + "name": "Science Fiction", + "description": "My favorite sci-fi books", + "color": "#ff0000", + "icon": "🚀", + "auto_assign_rules": [...], + "view_settings": { + "kobo": {"shelf_name": "Sci-Fi", "sync": true}, + "koreader": {"enabled": false}, + "web": {"view_mode": "grid"} + }, + "books": [ + { + "media_item_id": "uuid-1", + "title": "Foundation", + "author": "Isaac Asimov" + } + ], + "book_count": 42 +} +``` + +#### PUT `/api/collections/:id` + +Update collection. + +**Request**: +```json +{ + "name": "Sci-Fi Favorites", + "description": "Updated description", + "color": "#00ff00", + "icon": "⭐", + "auto_assign_rules": [ + { + "id": "rule-2", + "field": "series", + "operator": "equals", + "value": "Foundation" + } + ] +} +``` + +#### DELETE `/api/collections/:id` + +Delete collection and all its memberships. + +#### POST `/api/collections/:id/books` + +Add books to collection. + +**Request**: +```json +{ + "book_ids": ["uuid-1", "uuid-2", "uuid-3"], + "added_by_user": true // Manual addition vs. auto +} +``` + +#### DELETE `/api/collections/:id/books/:bookId` + +Remove book from collection. + +#### POST `/api/collections/:id/rules` + +Create auto-assign rule for collection. + +**Rule Schema**: +```json +{ + "field": "genre", // "genre", "series", "author", "language", "publisher", "copyright_year", "tags" + "operator": "equals", // "equals", "contains", "starts_with", "ends_with", "greater_than", "less_than" + "value": "Science Fiction" +} +``` + +#### PUT `/api/collections/:id/rules/:ruleId` + +Update existing rule. + +### Layer 3: Device-Specific Shelf Mappings + +#### GET `/api/devices/:id/collections` + +Get all collection → shelf mappings for a device. + +**Response**: +```json +{ + "device_id": "device-uuid", + "device_name": "My Kobo Clara", + "device_type": "kobo", + "mappings": [ + { + "collection_id": "collection-uuid", + "collection_name": "Science Fiction", + "device_shelf_name": "Sci-Fi", + "sync_direction": "bidirectional", + "created_at": "2026-01-31T12:00:00Z" + } + ], + "total": 1 +} +``` + +#### POST `/api/devices/:id/collections` + +Create new shelf mapping for device. + +**Request**: +```json +{ + "collection_id": "collection-uuid", + "device_shelf_name": "My Books", + "sync_direction": "bidirectional" +} +``` + +#### PUT `/api/devices/:id/collections/:collectionId` + +Update shelf mapping. + +#### DELETE `/api/devices/:id/collections/:collectionId` + +Remove shelf mapping. + +### Layer 3: OPDS Content Delivery + +#### GET `/opds/devices/:deviceId/catalog` + +Main OPDS 1.2 catalog feed. + +**Query Parameters**: +- `page`: integer (default 1) +- `per_page`: integer (default 50) +- `include_format`: string (optional filter) + +**Response (OPDS 1.2 XML)**: +```xml + + + urn:uuid:device-id + Bookmann Library + 2026-01-31T12:00:00Z + + + + + + + urn:uuid:bookmann-uuid-123 + The Hobbit + J.R.R. Tolkien + 2026-01-31T10:00:00Z + Book description... + + + + + + + + + + uuid-123 + + + abc123... + + + Science Fiction + Reading + + + + +``` + +#### GET `/opds/devices/:deviceId/search?q=` + +OPDS acquisition search endpoint. + +**Response (OPDS 1.2 XML)**: +```xml + + + urn:uuid:device-id + + + urn:uuid:bookmann-uuid-123 + The Hobbit + J.R.R. Tolkien + 2026-01-31T10:00:00Z + + + +``` + +#### GET `/opds/devices/:deviceId/nav` + +OPDS navigation feed. + +#### GET `/opds/devices/:deviceId/download/:bookId` + +Download book with optional format conversion. + +**Query Parameters**: +- `format`: string (epub, kepub, pdf, cbz) - default: epub + +**Response Headers**: +- `Content-Type`: application/epub+zip (or format-specific) +- `Content-Disposition`: attachment; filename="The Hobbit.epub" +- `X-Bookmann-UUID`: uuid-123 +- `X-Bookmann-SHA256`: abc123... (for format-specific if available) +- `X-Bookmann-KEPUB-SHA256`: xyz789... (if format=kepub) + +**Format Conversion Logic**: +```go +// Select appropriate format based on format parameter +switch format { +case "kepub": + // Check media_item_formats table for pre-converted KEPUB + if kepubFormat.Exists && kepubFormat.FilePath != "" { + Serve pre-converted file + Set X-Bookmann-KEPUB-SHA256: kepubFormat.SHA256 + } +case "pdf": + // Serve PDF directly +case "epub": + // Serve original EPUB directly +} +``` + +#### GET `/opds/devices/:deviceId/cover/:bookId` + +Download cover image. + +**Response**: +- `Content-Type`: image/jpeg +- `Cache-Control`: public, max-age=31536000 (1 year) + +#### GET `/opds/devices/:deviceId/formats/:bookId` + +List available formats for a book. + +**Response**: +```json +{ + "media_item_id": "uuid-123", + "formats": [ + { + "format_type": "epub", + "file_path": "/path/to/book.epub", + "file_sha256": "abc123...", + "file_size_bytes": 2456789, + "mime_type": "application/epub+zip", + "available": true + }, + { + "format_type": "kepub", + "file_path": "/cache/book.kepub.epub", + "file_sha256": "xyz789...", + "file_size_bytes": 2478932, + "mime_type": "application/vnd.kobo+xml+zip", + "available": true + }, + { + "format_type": "pdf", + "file_path": "/path/to/book.pdf", + "file_sha256": "def456...", + "file_size_bytes": 5123456, + "mime_type": "application/pdf", + "available": false // Not converted yet + } + ] +} +``` + +#### POST `/api/devices/:deviceId/opds-register` + +Register device for OPDS access (generates token). + +**Request**: +```json +{ + "device_name": "My Kobo Clara", + "device_type": "kobo" +} +``` + +**Response**: +```json +{ + "opds_token": { + "token": "abc-123-def-456...", + "token_type": "device", + "expires_at": "2026-02-28T23:59:59Z", + "created_at": "2026-01-31T12:00:00Z" + }, + "opds_catalog_url": "http://192.168.1.100:8765/opds/devices/kobo-id/catalog", + "refresh_interval": 3600 +} +``` + +### Layer 4: Enhanced Device Sync + +#### POST `/api/sync/kobo/markup` + +Kobo progress sync with ContentId mapping (enhanced). + +**Request (Enhanced)**: +```json +{ + "ReadingSync": [ + { + "ContentId": "kobo_xyz", + "PercentRead": 60.0, + "RemainingTimeMin": 120, + "ReadingEvent": "BookRead" + } + ], + "BookmarkSync": [...], + "Metadata": true // NEW: Include collection metadata +} +``` + +**ContentId Mapping Logic**: +```go +// Step 1: Try direct ContentId lookup +catalog, err := db.GetDeviceCatalogByKoboContentId(ctx, contentId) +if err == nil && catalog.Valid { + // Found! Use canonical Bookmann UUID + bookmannUUID = catalog.BookmannUUID + return bookmannUUID, nil +} + +// Step 2: ContentId not found - try SHA-256 (if looks like hash) +if len(contentId) == 64 && looksLikeSHA256(contentId) { + mediaItem, err := db.GetMediaItemBySHA256(ctx, contentId) + if err == nil { + return mediaItem.ID, nil + } +} + +// Step 3: Not found - create unlinked entry +return uuid.Nil{}, errors.New("unlinked book") +``` + +#### POST `/api/sync/kobo/bookmark` + +Kobo bookmark sync (enhanced). + +#### GET `/api/sync/kobo/initialization` + +Kobo library sync with collection metadata (enhanced). + +**Response (Enhanced)**: +```json +{ + "LibrarySync": [ + { + "ContentId": "kobo_xyz", + "ContentType": "6", + "Title": "The Hobbit", + "Author": "J.R.R. Tolkien", + "PercentRead": 60.0, + + // NEW: Collection metadata + "Categories": ["Science Fiction", "Reading"], + "BookmannUUID": "uuid-123" // Canonical ID + } + ] +} +``` + +### Layer 5: Enhanced KOReader Sync + +#### POST `/api/sync/koreader/progress` + +KOReader progress sync with SHA-256 support (enhanced). + +**Request (Enhanced)**: +```json +{ + "sync_mode": "immediate", // or "checkpoint" + "books": [ + { + "uuid": "uuid-123", // Optional: highest priority + "sha256": "abc123...", // NEW: Device can send hash + "file_path": "/storage/emulated/0/Books/MyBook.epub", + "title": "The Hobbit", + "authors": ["J.R.R. Tolkien"], + "percentage": 75.0, + "epubcfi": "/6/4!/2/4[chapter_1]@0:100", + "chapter": 12, + "character": 1234567, + "page": 312, + "total_pages": 416 + } + ] +} +``` + +**SHA-256 Matching Logic**: +```go +// Priority 1: UUID provided (highest confidence) +if book.UUID != "" { + return book.UUID, nil +} + +// Priority 2: SHA-256 provided (medium confidence) +if book.SHA256 != "" { + mediaItem, err := db.GetMediaItemBySHA256(ctx, book.SHA256) + if err == nil { + return mediaItem.ID, nil + } + return mediaItem.ID, nil +} + +// Priority 3: Create device file alias (if file path provided) +if book.FilePath != "" { + // Check if alias exists + alias, err := db.GetDeviceFileAlias(ctx, deviceID, book.FilePath) + if err == nil { + // Create new alias with medium confidence + db.CreateDeviceFileAlias(ctx, CreateDeviceFileAliasParams{ + MediaItemID: mediaItemID, + DeviceID: deviceID, + FilePath: book.FilePath, + FileSHA256: book.SHA256, + ConfidenceScore: 0.7, + }) + return alias.MediaItemID, nil + } + // Use existing alias + return alias.MediaItemID, nil +} + +// Priority 4: Search by title/author + file size (fallback) +return mediaItem.ID, nil +``` + +#### POST `/api/sync/koreader/bookmarks` + +KOReader annotations sync with SHA-256 support. + +**Request (Enhanced)**: +```json +{ + "bookmarks": [ + { + "uuid": "uuid-123", + "sha256": "abc123...", // NEW: For cross-device matching + "file_path": "/storage/emulated/0/Books/MyBook.epub", + "title": "The Hobbit", + "page": 312, + "text": "Great quote on page 312" + } + ] +} +``` + +### Layer 5: Sidecar Configuration + +#### GET `/api/sync/sidecar/:deviceId` + +Download unified `.bookmann.json` configuration file. + +**Response**: +```json +{ + "version": "1.0", + "bookmann": { + "opds_catalog": "http://192.168.1.100:8765/opds/devices/kobo-id/catalog", + "sync_api": "http://192.168.1.100:8765/api/sync/kobo", + "opds_base_url": "http://192.168.1.100:8765/opds", + "api_base_url": "http://192.168.1.100:8765/api", + "device_id": "kobo-device-uuid" + }, + "books": { + "sha256:abc123...": { + "bookmann_uuid": "uuid-123", + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "available_formats": ["epub", "kepub"] + } + }, + "collections": [ + { + "name": "Sci-Fi", + "shelf_mapping": "Science Fiction", + "book_ids": ["uuid-1", "uuid-2", "uuid-3"] + } + ], + "last_updated": "2026-01-31T12:00:00Z" +} +``` + +#### POST `/api/sync/sidecar/:deviceId/register` + +Validate sidecar file upload from device. + +### Layer 6: System Configuration + +#### GET `/api/admin/system-config` + +Get system-wide configuration. + +**Response**: +```json +{ + "config": { + "base_url": "https://bookmann.example.com", + "opds_base_url": "https://bookmann.example.com/opds", + "api_base_url": "https://bookmann.example.com/api", + "auto_convert_kepub": true, + "default_opds_refresh_interval": 3600 + } +} +``` + +#### PUT `/api/admin/system-config` + +Update system configuration. + +--- + +## Implementation Phases + +### Phase 1: Database Schema (Week 1) + +**Deliverables**: +- Create SQL schema file for all new tables +- Add columns to existing tables +- Run schema migrations on development database +- Update sqlc code generation +- Write rollback migration script + +**Tasks**: +1.1 Create migration SQL file `database/schema/001_universal_identifiers.sql` +1.2 Update database models +1.3 Write database queries +1.4 Test database queries manually +1.5 Write rollback migration script + +### Phase 2: Scanner Enhancement (Week 1-2) + +**Deliverables**: +- Enhanced scanner code +- OPF parser implementation +- Unit tests for hash calculation + +**Tasks**: +2.1 Implement SHA-256 calculation in `ebook_scanner.go` + - Stream file reading (don't load entire file into memory) + - Algorithm: crypto/sha256 from Go standard library +2.2 Implement OPF parser + - Extract `` tags from EPUB OPF files + - Parse both OEBPS and OPF 2.0 formats + - Extract UUIDs from `` attributes + - Handle multiple identifiers per file +2.3 Implement format detection + - Detect file format based on extension and content +2.4 Pre-convert EPUB to KEPUB during scan + - Use `ebooklib` or similar library for conversion +2.5 Store all format hashes in `media_item_formats` table + +**Hash Confidence Logic**: +``` +HIGH (confidence = 1.0): + - EPUB with valid `` (UUID format) + - ISBN found (standard format) + +MEDIUM (confidence = 0.7): + - OPF identifier present (non-UUID custom format) + - ISBN/ASIN matched via metadata sources + +LOW (confidence = 0.5): + - Only title/author match available +``` + +**Deliverables**: +- Enhanced scanner code +- OPF parser implementation +- KEPUB conversion utility +- Unit tests for hash calculation + +### Phase 3: Universal Book Matching Engine (Week 2) + +**Deliverables**: +- Matching engine implementation +- Book query API handlers +- Manual linking API endpoints +- Unit tests for matching logic + +**Matching Algorithm**: +``` +Priority 1: Bookmann UUID (canonical) + - If device sends UUID, use directly + - Confidence = 1.0 + +Priority 2: OPF UUID (from EPUB metadata) + - Match against `opf_uuid` column + - Confidence = 0.95 + +Priority 3: SHA-256 hash + - Match against `file_sha256` column + - Confidence = 0.9 + +Priority 4: OPF identifier (non-UUID) + - Match against `opf_identifier` column + - Confidence = 0.85 + +Priority 5: ISBN/ASIN (standard identifiers) + - Match against `isbn` and `asin` columns + - Confidence = 0.8 + +Priority 6: File path (device-specific) + - Match via `device_file_aliases` table + - Confidence = from alias record + +Priority 7: Title + author + file size (fallback) + - Fuzzy search on title + - Exact match on author + - Within 10% file size variance + - Confidence = 0.5 + +Priority 8: Title only (last resort) + - Fuzzy title match + - Confidence = 0.3 +``` + +**Deliverables**: +- Matching engine implementation +- Book query API handlers +- Manual linking API +- Unlinked books API +- Unit tests for matching logic + +### Phase 4: Collection Management System (Week 2-3) + +**Deliverables**: +- Collections CRUD handlers +- Auto-assign rules engine +- Device shelf mapping handlers +- Add collection book management +- Per-device view settings + +**Auto-Assign Rules Engine**: +```go +type Rule struct { + ID string + Field string // "genre", "series", "author", "language", "publisher", "copyright_year", "tags" + Operator string // "equals", "contains", "starts_with", "ends_with", "greater_than", "less_than" + Value string // Exact value to match +} + +type RuleEvaluation struct { + RuleID string + Matches bool + Confidence float +} + +func EvaluateRules(mediaItem MediaItem, rules []Rule) []RuleEvaluation { + // Evaluate each rule against mediaItem metadata + // Return which rules match and overall confidence + // Higher-priority rules take precedence +} +``` + +**Rule Priority System**: +1. Rule with `priority` field (1-10, higher first) +2. Multiple rules can apply to same book +3. User can configure logical operators (AND, OR) + +**Deliverables**: +- Collections API handlers +- Rules engine implementation +- Database queries for collections +- Unit tests for rule evaluation + +### Phase 5: OPDS Implementation (Week 3) + +**Deliverables**: +- OPDS XML serializer +- OPDS catalog feed handler +- OPDS search endpoint +- Book download with format support +- Cover image serving +- ContentId mapping to OPDS responses +- On-the-fly KEPUB conversion +- Device authorization checks +- OPDS token management + +**OPDS Response Structure (OPDS 1.2)**: +```xml + + urn:uuid:bookmann-uuid-123 + The Hobbit + J.R.R. Tolkien + 2026-01-31T10:00:00Z + + + + + + + + + + uuid-123 + + + abc123... + xyz789... + + + Science Fiction + Reading + +``` + +**Format Conversion Strategy**: +``` +When user downloads with ?format=kepub: + +1. Check media_item_formats table +2. If KEPUB exists and is recent: + - Serve pre-converted file + - Set X-Bookmann-SHA256: kepubFormat.SHA256 +3. If KEPUB doesn't exist: + - Convert EPUB to KEPUB on-the-fly + - Cache in media_item_formats table + - Serve converted file + - Set X-Bookmann-SHA256: kepubFormat.SHA256 +4. Serve PDF directly +``` + +**Deliverables**: +- OPDS handlers implementation +- OPDS XML serializers +- KEPUB conversion utility +- OPDS authentication +- Integration with device_catalogs table + +### Phase 6: Enhanced Kobo Sync (Week 3-4) + +**Deliverables**: +- Updated Kobo handler to use ContentId mapping +- Bidirectional ContentId lookup +- Add unlinked book detection +- Integrate collection metadata into library sync +- Support for legacy API endpoints + +**ContentId Mapping Logic**: +```go +// Step 1: Try direct ContentId lookup +catalog, err := db.GetDeviceCatalogByKoboContentId(ctx, contentId) +if err == nil && catalog.Valid { + // Found! Use canonical Bookmann UUID + return catalog.BookmannUUID, nil +} + +// Step 2: ContentId not found - try SHA-256 +if len(contentId) == 64 && looksLikeSHA256(contentId) { + mediaItem, err := db.GetMediaItemBySHA256(ctx, contentId) + if err == nil { + return mediaItem.ID, nil + } +} + +// Step 3: Not found - create unlinked entry +return uuid.Nil{}, errors.New("unlinked book") +``` + +**Deliverables**: +- Updated Kobo sync handlers +- ContentId mapping system +- Unlinked book tracking +- Integration with collection metadata + +### Phase 7: Enhanced KOReader Sync (Week 4) + +**Deliverables**: +- Updated KOReader handler to accept SHA-256 +- Implement device file alias creation +- Integrate auto-linking with confidence thresholds +- Add SHA-256 matching for annotations + +**SHA-256 Matching for Progress Sync**: +```go +// Priority 1: UUID provided (highest confidence) +if book.UUID != "" { + return book.UUID, nil +} + +// Priority 2: SHA-256 provided (medium confidence) +if book.SHA256 != "" { + mediaItem, err := db.GetMediaItemBySHA256(ctx, book.SHA256) + if err == nil { + return mediaItem.ID, nil + } +} + +// Priority 3: Create device file alias +if book.FilePath != "" { + alias, err := db.GetDeviceFileAlias(ctx, deviceID, book.FilePath) + if err == nil { + // Create new alias + db.CreateDeviceFileAlias(ctx, CreateDeviceFileAliasParams{ + MediaItemID: mediaItemID, + DeviceID: deviceID, + FilePath: book.FilePath, + FileSHA256: book.SHA256, + ConfidenceScore: 0.7, + }) + return alias.MediaItemID, nil + } + return alias.MediaItemID, nil +} +``` + +**Deliverables**: +- Enhanced KOReader handlers +- SHA-256 matching integration +- Device file alias system integration +- Auto-linking with configurable thresholds + +### Phase 8: Sidecar Configuration System (Week 4) + +**Deliverables**: +- Sidecar JSON generation +- Sidecar download/upload handlers +- System configuration support + +**Sidecar File Format (Enhanced)**: +```json +{ + "version": "1.0", + "bookmann": { + "opds_catalog": "http://192.168.1.100:8765/opds/devices/kobo-id/catalog", + "sync_api": "http://192.168.1.100:8765/api/sync/kobo", + "opds_base_url": "http://192.168.1.100:8765/opds", + "api_base_url": "http://192.168.1.100:8765/api", + "device_id": "kobo-device-uuid" + }, + "books": { + "sha256:abc123...": { + "bookmann_uuid": "uuid-123", + "title": "The Hobbit", + "author": "J.R.R. Tolkien", + "available_formats": ["epub", "kepub"] + } + }, + "collections": [ + { + "name": "Sci-Fi", + "shelf_mapping": "Science Fiction", + "book_ids": ["uuid-1", "uuid-2", "uuid-3"] + } + ], + "opds_enabled": true, + "sidecar_enabled": true, + "last_updated": "2026-01-31T12:00:00Z" +} +``` + +**Deliverables**: +- Sidecar generation system +- System configuration support +- Admin UI for system settings + +### Phase 9: Frontend Implementation (Week 5-6) + +**Deliverables**: +- Collections management pages +- Device configuration pages +- Enhanced progress visualization with sync sources +- Unlinked books resolution UI +- Collection rule builder UI +- Device-specific view settings UI + +**Deliverables**: +- Collections list/detail pages +- Device management interface +- Progress sync dashboard with device indicators +- Book matching UI with confidence indicators + +### Phase 10: Documentation & Testing (Week 6) + +**Deliverables**: +- Updated device setup guides +- Complete API documentation +- Test suite covering all scenarios +- User acceptance testing + +**Deliverables**: +- KOBO_SETUP.md update with OPDS workflow +- KOREADER_SETUP.md new file with OPDS instructions +- Complete API reference documentation +- User guides for all device types + +--- + +## Device Setup Instructions + +### Kobo E-Reader Setup (OPDS Primary Method) + +#### Option 1: OPDS Catalog (Recommended - Wireless Delivery + Progress Sync) + +**Step 1: Download Configuration File** +``` +1. Log into Bookmann web UI +2. Go to Device Management → Your Kobo device +3. Click "Download Configuration" button +4. File downloads as `.bookmann.json` +``` + +**Step 2: Configure Kobo for OPDS** +``` +1. On Kobo, go to Settings → Sync & Backup +2. Tap "Add Content Server" or "Add OPDS Feed" +3. Enter URL from `.bookmann.json`: + http://192.168.1.100:8765/opds/devices/YOUR_DEVICE_ID/catalog +4. Kobo will automatically: + - Connect to Bookmann + - Browse your library wirelessly + - Download books directly + - Sync reading progress back to Bookmann +``` + +**Step 3: Wireless Book Acquisition** +``` +1. On Kobo, go to "My Books" section +2. Browse Bookmann catalog via OPDS +3. Tap on any book to download wirelessly +4. Book appears on Kobo device +5. Start reading - progress syncs automatically +``` + +**How Progress Sync Works**: +- Kobo generates ContentId for each book +- ContentId mapped to Bookmann UUID in device_catalogs table +- When Kobo syncs progress, Bookmann uses canonical UUID +- Format conversion (KEPUB) doesn't break progress tracking + +### KOReader Setup + +#### Option 1: OPDS Catalog (Recommended) + +**Step 1: Download Configuration File** +``` +Same as Kobo setup above +``` + +**Step 2: Configure KOReader for OPDS** +``` +1. Open KOReader settings +2. Enable "OPDS catalog" in network/synchronization section +3. Enter OPDS URL from `.bookmann.json`: + http://192.168.1.100:8765/opds/devices/YOUR_DEVICE_ID/catalog +4. KOReader will automatically: + - Connect to Bookmann catalog + - Browse and download books wirelessly + - Sync progress using SHA-256 matching + - Create file aliases automatically +``` + +**Step 3: Wireless Book Acquisition** +``` +1. Open KOReader file browser +2. Tap "+" button to add OPDS catalog +3. Browse Bookmann catalog +4. Download books directly +5. Start reading +``` + +#### Option 2: Sidecar File (Alternative - Enhanced Progress Sync) + +**For offline or simple setup** + +**Step 1: Download Sidecar** +``` +Same as Kobo setup above +``` + +**Step 2: Place Sidecar on KOReader** +``` +Place in KOReader's config directory +``` + +**Step 3: Use Sidecar for Progress Sync** +``` +KOReader plugin reads .bookmann.json +→ Matches local files to Bookmann UUIDs via SHA-256 +→ Syncs progress using canonical UUIDs +→ Works offline +``` + +### Web & Mobile Setup + +``` +OPDS catalog automatically available at: +/opds/devices/:deviceId/catalog + +Apps can: +- Browse entire library wirelessly +- Download books directly +- See collection metadata +- Sync progress via existing internal APIs +``` + +--- + +## Security Considerations + +### Authentication Layers + +**Layer 1: Web & Mobile (Internal API)** +``` +Uses: JWT tokens +Issued by: POST /api/auth/login, /api/auth/refresh +Validated: On each request via middleware +Revoked by: POST /api/auth/logout +Stored in: refresh_tokens table (not devices table) +``` + +**Layer 2: Sync APIs (Device Tokens)** +``` +Issued by: Device registration endpoint +Stored in: devices.auth_token field +Validated by: Device authentication middleware +``` + +**Layer 3: OPDS (Device Tokens, Optional)** +``` +Issued by: /api/devices/:id/opds-register +Stored in: opds_tokens table +Scope: Device-specific access to catalog + +Can be: Public (no authentication required) +``` + +### Data Privacy + +1. **Progress & Annotations**: Always associated with user_id in database +2. **Collections**: User-scoped - each user sees only their collections +3. **File Aliases**: Device-specific - never shared across users +4. **Device Catalogs**: Links stored per-device - no cross-user leakage +5. **Sidecar Files**: Contain only user's device token and book mappings + +### Access Control + +**OPDS Authorization Flow:** +``` +1. OPDS request includes device_id in URL path +2. Server validates: + a. Device exists + b. Device belongs to requesting user + c. Book is in user's visible library +3. If validation passes: Serve OPDS feed +``` + +**Public Catalog Option**: +- Can be enabled in system_config +- Allows guest users to browse without device registration +- Still respects library visibility per user + +--- + +## Testing Strategy + +### Unit Tests + +**Coverage Areas**: +1. Hash calculation accuracy (SHA-256, OPF extraction) +2. Matching algorithm priorities +3. Collection rule evaluation +4. OPDS XML serialization +5. Format conversion integrity + +### Integration Tests + +**Test Scenarios**: +1. Cross-device book matching (same book, different paths) +2. Format conversion (EPUB → KEPUB) with hash integrity +3. Collection auto-assign (rules fire correctly) +4. Bidirectional progress sync (Kobo ↔ KOReader) +5. OPDS catalog generation and pagination +6. Sidecar file generation and validation + +### Manual Testing Checklist + +**Kobo Workflow**: +- [ ] Download `.bookmann.json` from web UI +- [ ] Transfer to Kobo via USB +- [ ] Configure OPDS URL on Kobo +- [ ] Browse catalog wirelessly +- [ ] Download book +- [ ] Read 50% of book +- [ ] Verify progress syncs to Bookmann + +**KOReader Workflow**: +- [ ] Download `.bookmann.json` from web UI +- [ ] Configure OPDS URL in KOReader +- [ ] Browse catalog wirelessly +- [ ] Download book +- [ ] Read 75% of book +- [ ] Verify progress syncs to Bookmann + +**Cross-Device Scenario**: +- [ ] Add book to Bookmann (EPUB scanned) +- [ ] Download to Kobo via OPDS +- [ ] Sync progress (60%) from Kobo +- [ ] Open same book on KOReader (side-loaded) +- [ ] Read to 75% on KOReader +- [ ] Verify progress shows 75% (latest from either device) +- [ ] Verify sync sources tracked correctly + +--- + +## Glossary + +- **Bookmann UUID**: Canonical identifier for a book in Bookmann system (from `media_items.id`). Always used for progress tracking, never SHA-256. SHA-256 is only for matching books across devices. +- **ContentId**: Device-generated identifier (e.g., Kobo's "kobo_abc"). Mapped to Bookmann UUID in `device_catalogs` table. Used for progress sync after OPDS downloads. +- **SHA-256**: Cryptographic hash of file contents. Used for content-based matching across devices. Critical for identifying same book on different devices. +- **OPF UUID**: Unique identifier from EPUB metadata ``. High-confidence identifier format. +- **OPF Identifier**: Any identifier from EPUB OPF file (custom format). Medium-confidence identifier format. +- **ISBN**: International Standard Book Number (13 digits). Medium-confidence standard identifier. +- **ASIN**: Amazon Standard Identification Number (10 characters). Medium-confidence standard identifier. +- **Collections**: Device-neutral organizational groups in Bookmann (e.g., "Science Fiction", "Reading"). Books can be in collections without being on any device. Collections organize library, not track device inventory. +- **Shelves**: Device-specific organization (e.g., Kobo's terminology). Map Collections to device-specific "Shelves" only at device-level. Bookmann UI always uses "Collections" terminology. +- **OPDS**: Open Publication Distribution System. Industry standard for book catalogs. All e-reader platforms have OPDS clients. Kobo, KOReader, Aldiko, FBReader, Web browsers can use OPDS catalogs. +- **Internal APIs**: Bookmann's private REST/WebSocket endpoints for state management. Web and mobile apps use these directly. Used for two-way sync, collections, WebSocket real-time updates. +- **Device File Alias**: Mapping of device-specific file paths to Bookmann UUIDs. Enables cross-device matching when same book has different file paths. +- **Hash Confidence**: Scoring system (0.0-1.0) for automatic book matching reliability. Higher values = more reliable match. +- **Dual Hash Storage**: Storing both original EPUB hash (`epub_sha256`) and converted KEPUB hash (`kepub_sha256`). Preserves hash integrity when files are converted. OPDS responses include format-specific hash for sidecar matching. +- **Format Conversion**: Transcoding between book formats (EPUB → KEPUB). KEPUB adds Kobo-specific markup. Critical for Kobo optimization but shouldn't break progress tracking. +- **Media Item Formats**: Tracks all format versions with their hashes. Pre-convert EPUB to KEPUB during scan for optimal performance. +- **System Config**: Key-value store for system-wide settings (base_url, opds_base_url, api_base_url). Enables flexible deployment. +- **OPDS Tokens**: Per-device access tokens for OPDS catalog browsing. Optional - can also support user-scoped and admin tokens. +- **Sidecar File**: `.bookmann.json` - Unified configuration file for devices. Contains OPDS URLs, sync API endpoints, book mappings, collection mappings. +- **Auto-Assign Rules**: Configurable criteria for automatically adding books to collections. Fields: genre, series, author, language, publisher, copyright_year, tags. Operators: equals, contains, starts_with, ends_with, greater_than, less_than. +- **Sync Direction**: For device shelf mappings. 'bidirectional' (sync both ways), 'book_to_device' (send to device), 'device_to_book' (read from device), 'none' (no sync). +- **View Settings**: Per-device preferences for how collections are displayed (grid vs list, which collections are visible). +- **Unlinked Book**: Progress record without proper media_item_id or failed ContentId lookup. Needs manual user resolution. + +--- + +## Summary + +This comprehensive implementation plan provides: + +- **7 new database tables** with proper indexing +- **50+ API endpoints** across 6 layers (identification, collections, OPDS, sync, configuration) +- **10-week phased implementation** with clear deliverables +- **Complete device setup guides** for Kobo, KOReader, Web, and Mobile +- **Three-tier authentication model** for security (JWT, device tokens, OPDS optional) +- **Glossary** of all terminology and concepts +- **Testing strategies** covering unit, integration, and manual validation + +The plan is designed for systematic execution while maintaining architectural consistency and enabling human oversight throughout the development process. All decisions from our conversations have been incorporated, providing a complete roadmap for implementing Bookmann as a comprehensive cross-device ebook management system. \ No newline at end of file