diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md
index ba78cff..1a31499 100644
--- a/IMPLEMENTATION_PLAN.md
+++ b/IMPLEMENTATION_PLAN.md
@@ -4,14 +4,18 @@
**Current Problem:**
- OPDS routes are publicly accessible (security vulnerability)
-- Kobo sync uses Bearer tokens but Kobo firmware can't send them (broken)
+- Kobo sync requires custom authentication approach
- KOReader documentation is incorrect about Basic Auth
- No clear authentication strategy for different device types
-- Sidecar implementation exists but isn't integrated
**Vision:**
Transform Bookhoard into a comprehensive Kindle-replacement ecosystem with seamless device sync, supporting both Kobo (native) and KOReader (via plugin) with full functionality including progress, highlights, notes, and bookmarks.
+**Authentication Strategy:**
+- **Kobo**: API key in URL path (per-device, revocable, no jailbreak needed)
+- **KOReader**: Bearer token in Authorization header (per-device, revocable, via plugin)
+- **OPDS**: Both authentication methods supported
+
---
## 1. Current State Analysis
@@ -35,21 +39,28 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
**Database Schema:**
```sql
--- devices table already has device_identifier field
--- Can store serial numbers for Kobo devices
+-- devices table has auth_token field for all device authentication
+-- Can store revocable API keys for Kobo devices
```
### 1.2 Kobo Integration Status
-**What's Broken:**
-- Kobo sends `Authorization: Bearer {kobo-store-token}` (wrong token)
-- Kobo sends `x-kobo-device: {"SerialNumber":"N123..."}` (what we need to use)
-- Current middleware rejects Kobo requests because token doesn't match
+**Proven Approach (Komga):**
+- Komga successfully handles Kobo sync without jailbreak using API keys in URL path
+- Configuration: `api_endpoint=https://komga.example.com/kobo/{api_key}`
+- Kobo firmware supports custom `api_endpoint` in `Kobo eReader.conf`
+- API key embedded in URL path works reliably with stock Kobo firmware
+
+**What's Currently Broken:**
+- Current middleware expects Bearer token in Authorization header
+- Routes need to support API key in URL path parameter
+- Device auth tokens (API keys) already generated during registration
**What's Working:**
-- Kobo sync handler implementation exists
+- Kobo sync handler implementation exists and is functional
- Endpoints registered: `/api/sync/kobo/markup`, `/bookmark`, `/v1/initialization`, etc.
-- Can sync progress, highlights, notes, bookmarks once auth works
+- Device auth tokens (API keys) already generated during registration
+- Can sync: progress, highlights, notes, bookmarks once auth is fixed
### 1.3 KOReader Integration Status
@@ -67,12 +78,15 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
**Current:**
- Routes registered in `internal/router/opds.go:11-12`
-- **NOT using DeviceAuthMiddleware** (security hole)
-- Publicly accessible - anyone can enumerate device UUIDs
+- ✅ **Already using DeviceAuthMiddleware** (line 12: `opds.Use(cfg.DeviceAuthMiddleware.Authenticate)`)
+- ✅ Authentication is required - not publicly accessible
+- Routes: `/opds/devices/:deviceId/*` - all protected
**Required:**
-- Must require authentication
-- Needs to work with both Kobo (serial) and KOReader (Bearer token)
+- ✅ No changes needed for OPDS security
+- After Phase 1, OPDS will automatically support both auth methods:
+ - Kobo: API key in URL path (`?token=` or path parameter)
+ - KOReader: Bearer token via `Authorization` header
---
@@ -92,19 +106,20 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
**User Quote:** "This is a self-hosted app so as long as the user is aware of the security implications in the documentation it should be fine"
**Acceptable Trade-offs:**
-- Serial-based auth for Kobo (less secure but easier)
-- Security risks documented clearly
+- API key in URL for Kobo (per-device, revocable, documented security considerations)
- Token-based for KOReader (more secure)
-- Users responsible for network security (VPN, etc.)
+- Users responsible for network security (VPN, local network, HTTPS)
+- Security implications clearly documented
### Priority 3: Simple Setup
**Goal:** Minimal friction for users
**Kobo Experience:**
-- Enter serial number during registration
-- One line in `Kobo eReader.conf`
-- No token management
-- Works immediately
+- Register device in Bookhoard UI
+- Copy device API key from device management page
+- One line in `Kobo eReader.conf` with API key
+- Token can be regenerated if compromised
+- Works immediately with stock firmware
**KOReader Experience:**
- Copy plugin files
@@ -119,121 +134,164 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
### 3.1 Unified Authentication Architecture
```
-┌─────────────────────────────────────────────────────────────┐
-│ DeviceAuthMiddleware │
-│ Enhanced to support multiple auth methods: │
-│ │
-│ 1. Authorization: Bearer {token} │
-│ → Lookup device by auth_token │
-│ → Used by: KOReader, OPDS apps │
-│ │
-│ 2. X-Kobo-Device: {"SerialNumber":"N123..."} │
-│ → Extract serial from JSON │
-│ → Lookup device by device_identifier │
-│ → Used by: Kobo e-readers │
-│ │
-│ Both paths result in: device context set in echo.Context │
-└─────────────────────────────────────────────────────────────┘
+┌─────────────────────────────────────────────────────────────────┐
+│ DeviceAuthMiddleware (Enhanced) │
+│ Unified authentication using revocable API keys: │
+│ │
+│ 1. Authorization: Bearer {token} │
+│ → Extract from Authorization header │
+│ → Lookup device by auth_token │
+│ → Used by: KOReader, OPDS apps, API clients │
+│ │
+│ 2. URL Path Parameter: /api/sync/kobo/{token} │
+│ → Extract token from URL path (c.Param("token")) │
+│ → Lookup device by auth_token │
+│ → Used by: Kobo e-readers (stock firmware, no jailbreak) │
+│ │
+│ 3. Query Parameter: ?token={token} │
+│ → Extract from query string (c.QueryParam("token")) │
+│ → Lookup device by auth_token │
+│ → Used by: OPDS catalog access │
+│ │
+│ All methods result in: device context set in echo.Context │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Why This Architecture:**
+- **Simple**: All methods use same auth_token lookup (no new database logic)
+- **Secure**: API keys are random UUIDs, revocable, can be regenerated
+- **Universal**: All devices get auth_tokens (existing field, no schema change)
+- **Proven**: Komga uses this exact approach for Kobo sync successfully
+- **No Firmware Limitations**: URL path parameter works with stock Kobo firmware (no jailbreak needed)
+- **Revocable**: API keys can be regenerated by users without device re-registration
+- **Single Authentication Method**: All device types use the same auth_token lookup, simplifying codebase
```
### 3.2 Feature Matrix
-| Feature | Kobo (Native) | KOReader (Plugin) |
+| Feature | Kobo (Stock Firmware) | KOReader (Plugin) |
|---------|---------------|-------------------|
-| **Progress Sync** | ✅ Automatic | ✅ Automatic |
+| **Progress Sync** | ✅ Automatic (API key in URL) | ✅ Automatic (Bearer token) |
| **Highlights** | ✅ Native support | ✅ Via plugin API |
| **Bookmarks** | ✅ Native support | ✅ Via plugin API |
| **Notes** | ✅ Native support | ✅ Via plugin API |
-| **OPDS** | ✅ Via NickelMenu | ✅ Native OPDS support |
+| **OPDS** | ✅ Native support | ✅ Native OPDS support |
| **Setup Complexity** | Low (1 config line) | Medium (plugin install) |
-| **Auth Method** | Serial number | Bearer token |
-| **Security** | Medium (documented) | High |
+| **Auth Method** | API key in URL path | Bearer token in header |
+| **Security** | High (revocable tokens) | High (revocable tokens) |
### 3.3 User Flows
**Kobo User Journey:**
-1. Install NickelMenu (one-time, jailbreak)
-2. Open Bookhoard web UI
-3. Register device → enter serial number (from Settings)
-4. Approve device
-5. Edit `Kobo eReader.conf` → add sync URL
-6. Read books → automatic sync
-7. Access OPDS via NickelMenu
+1. Open Bookhoard web UI
+2. Navigate to Device Management → Add New Device
+3. Enter device name and select "Kobo" as device type
+4. Click Register Device
+5. From device details page, copy full Kobo Sync URL with API key:
+ ```
+ http://YOUR_IP:8765/api/sync/kobo/YOUR_API_KEY
+ ```
+6. Connect Kobo to computer, open `.kobo/Kobo/Kobo eReader.conf`
+7. Add/edit line: `api_endpoint=http://YOUR_IP:8765/api/sync/kobo/YOUR_API_KEY`
+ - Paste the entire URL from step 5 (includes device API key)
+8. Save and eject Kobo (restarts automatically)
+9. Kobo syncs to Bookhoard instead of Kobo store
+10. For OPDS: Add catalog URL with `?token=YOUR_API_KEY` parameter
+
+**Important Note - Token Regeneration:**
+- If token is compromised or lost, regenerate from device management page
+- OLD token will immediately stop working
+- User must update `api_endpoint` line with NEW token
+- One-click copy button makes this easy
**KOReader User Journey:**
1. Install KOReader on device
2. Open Bookhoard web UI
-3. Register device → copy auth token
+3. Register device → copy API key
4. Approve device
-5. Install Bookhoard plugin → paste token
+5. Install Bookhoard plugin → paste API key
6. Configure sync settings
7. Read books → automatic sync
-8. Add OPDS catalog → browse library
+8. Add OPDS catalog with API key in header (via plugin)
---
## 4. Implementation Plan
### Phase 1: Enhanced Authentication (Week 1)
-**Goal:** Make auth work for both Kobo and KOReader
+**Goal:** Make auth work for both Kobo (API key in URL) and KOReader (Bearer token)
**Tasks:**
-1. **Update DeviceAuthMiddleware** (`internal/middleware/device_auth.go`)
- - [ ] Try Bearer token lookup first (existing behavior)
- - [ ] If no Bearer token, check for `x-kobo-device` header
- - [ ] Parse JSON to extract `SerialNumber` field
- - [ ] Query database: `GetDeviceByDeviceIdentifier(serial)`
- - [ ] Set device context on successful auth
- - [ ] Return 401 if both methods fail
+1. **Update Routing for Kobo** (`internal/router/sync.go`)
+ - [ ] Change Kobo route group from `/api/sync/kobo` to `/api/sync/kobo/:token`
+ - [ ] Update all Kobo route handlers to use path parameter
+ - [ ] Test that routes still work with Bearer token (backward compatibility)
-2. **Add Database Query** (`internal/database/queries/devices.sql`)
- - [ ] Add query: `GetDeviceByDeviceIdentifier`
- - [ ] Ensure index exists on `device_identifier` column
+2. **Update DeviceAuthMiddleware** (`internal/middleware/device_auth.go`)
+ - [ ] Try Bearer token lookup first (existing behavior)
+ - [ ] If no Bearer token, check for `token` path parameter (`c.Param("token")`)
+ - [ ] Query database: `GetDeviceByAuthToken(token)` (already exists)
+ - [ ] Set device context on successful auth
+ - [ ] Return 401 if both methods fail
-3. **Update Device Registration UI** (`templates/devices.templ`)
- - [ ] Add device type selector (Kobo/KOReader)
- - [ ] Show serial number field for Kobo (required)
- - [ ] Show serial number field for KOReader (optional, for fallback)
- - [ ] Generate/display auth token after approval (for KOReader)
+3. **Add Token Regeneration Backend** (`internal/handlers/devices.go`)
+ - [ ] Add endpoint: `POST /api/devices/:id/regenerate-token`
+ - [ ] Generate new random UUID for auth_token
+ - [ ] Update device in database
+ - [ ] Return new token to user
+ - [ ] Require JWT auth (user must be logged in)
-4. **Apply Middleware to OPDS** (`internal/router/opds.go`)
- - [ ] Add `DeviceAuthMiddleware` to all `/opds/devices/*` routes
- - [ ] Update comments to reflect auth requirements
+4. **Update Device Registration UI** (`templates/devices.templ`, `templates/partials/device-management.templ`)
+ - [ ] **Show "Copy Full Sync URL" button for each device** (uses clipboard API)
+ - [ ] For Kobo: Display complete URL: `http://IP:8765/api/sync/kobo/{DEVICE_TOKEN}`
+ - [ ] **Add "Regenerate Token" button** (with confirmation dialog: "This will revoke current token. Continue?")
+ - [ ] After regeneration, show toast: "Token regenerated - update Kobo config"
+ - [ ] For KOReader: Show auth token in copyable input field
+ - [ ] Display clear setup instructions per device type with full URL example
+
+5. **Apply Middleware to OPDS** (`internal/router/opds.go`)
+ - [ ] Verify OPDS routes have DeviceAuthMiddleware (already applied)
+ - [ ] Add support for `?token=` query parameter for OPDS access
+ - [ ] Update comments to reflect dual auth support
**Testing:**
-- Kobo device sync with serial auth
+- Kobo device sync with api-key url auth
- KOReader sync with Bearer token auth
- OPDS access with both auth methods
---
### Phase 2: Kobo Integration (Week 1-2)
-**Goal:** Seamless Kobo experience with serial-based auth
+**Goal:** Seamless Kobo experience with API key-based auth (stock firmware)
**Tasks:**
1. **Kobo Setup Documentation** (`docs/user/devices/kobo-setup.md`)
- - [ ] Revise to reflect serial-based auth (not username/password)
- - [ ] Step-by-step guide for finding serial number
- - [ ] NickelMenu configuration for sync
- - [ ] Security warning about serial-based auth
- - [ ] Network security recommendations (VPN, local network)
+ - [ ] Show how to get API key from Bookhoard device management page
+ - [ ] Step-by-step guide for editing `Kobo eReader.conf`
+ - [ ] Include example config: `api_endpoint=http://192.168.1.100:8765/api/sync/kobo/{API_KEY}`
+ - [ ] Security warning about API keys in logs
+ - [ ] Network security recommendations (VPN, local network)
+ - [ ] Instructions for regenerating token if compromised
2. **Kobo Sync Validation** (`cmd/server/tests/kobo_test.go`)
- - [ ] Update tests to use serial auth
- - [ ] Test sync endpoints with `x-kobo-device` header
- - [ ] Verify all features work: progress, highlights, bookmarks, notes
+ - [ ] Update tests to use API key in URL path
+ - [ ] Test sync endpoints with `/api/sync/kobo/{token}/markup` format
+ - [ ] Verify all features work: progress, highlights, bookmarks, notes
+ - [ ] Test token regeneration doesn't break existing sync
3. **Kobo OPDS Integration**
- - [ ] Document OPDS URL format
- - [ ] Test OPDS browsing with serial auth
- - [ ] Test book downloads
+ - [ ] Document OPDS URL format: `http://IP:8765/opds/devices/{DEVICE_ID}/catalog?token={API_KEY}`
+ - [ ] Test OPDS browsing with API key in URL parameter
+ - [ ] Test book downloads
**Success Criteria:**
- New Kobo device registered and syncing within 5 minutes
-- All sync features working without token management
+- All sync features working without jailbreak
+- API key can be regenerated if compromised
- Security implications clearly documented
+- Works with stock Kobo firmware (no modifications)
---
@@ -286,12 +344,12 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
1. **Secure OPDS Routes** (`internal/router/opds.go`)
- [ ] Apply `DeviceAuthMiddleware` to all OPDS endpoints
- - [ ] Ensure auth works with both serial and Bearer methods
+ - [ ] Ensure auth works with both api-key and Bearer methods
- [ ] Update router comments
2. **Kobo OPDS Access**
- [ ] Document OPDS URL with device ID
- - [ ] Test with NickelMenu OPDS browser
+ - [ ] Test OPDS access via Kobo browser
- [ ] Test book downloads
3. **KOReader OPDS Access**
@@ -302,7 +360,7 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
4. **Test Coverage** (`cmd/server/tests/opds_test.go`)
- [ ] Test unauthenticated access → expect 401
- [ ] Test with valid Bearer token → expect 200
- - [ ] Test with valid serial header → expect 200
+ - [ ] Test with valid api-key url → expect 200
- [ ] Test book download with auth
---
@@ -322,7 +380,7 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
2. **Security Documentation**
- [ ] Security implications page
- [ ] Network security recommendations
- - [ ] Comparison: Bearer vs Serial auth
+ - [ ] Comparison: API Key (URL path) vs Bearer token - both use revocable auth_token
- [ ] Threat model for self-hosted users
3. **User Documentation**
@@ -340,32 +398,81 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
## 5. Technical Decisions
-### 5.1 Why Serial-Based Auth for Kobo?
+### 5.1 Why API Key in URL Path for Kobo?
+
+**Approach Chosen:** Per-device API key embedded in URL path: `/api/sync/kobo/{api_key}/...`
**Pros:**
-- No token management required
-- Works with unmodified Kobo firmware
-- Single configuration line
-- User can't lose/forget credentials
+- No jailbreak needed - works with stock Kobo firmware
+- Proven approach - Komga uses this successfully
+- API keys are random UUIDs (cryptographically secure)
+- Tokens can be revoked and regenerated
+- Single configuration line in Kobo config
+- Simple token management (user can regenerate)
+- Uses existing `auth_token` field (no schema change)
**Cons:**
-- Serial numbers are somewhat predictable
-- No token rotation possible
-- If leaked, can't be revoked (must delete device)
+- API key visible in Kobo logs and server logs
+- URL-based authentication (less secure than headers)
+- Key appears in Kobo config file (plain text)
**Mitigations:**
-- Document security risks
-- Recommend VPN for remote access
-- Use HTTPS (reverse proxy)
-- Acceptable for self-hosted personal use
+- Use HTTPS for sync (reverse proxy)
+- Document that API keys are sensitive
+- Recommend local network only (VPN for remote)
+- Provide one-click token regeneration
+- Acceptable security for self-hosted use (like Komga)
-### 5.2 Why Bearer Token for KOReader?
+**Alternative Considered:**
+- **Serial-based auth**: Rejected because it uses predictable serials that can't be revoked and is less secure
+- **Kobo store token**: Rejected because it's managed by Kobo, not under our control
+- **Custom HTTP headers**: Rejected because Kobo firmware doesn't support custom headers
+
+**Authentication Strategy:**
+- Kobo: API key in URL path (per-device, revocable)
+- KOReader: Bearer token (per-device, revocable)
+- OPDS: Both methods supported (API key via query parameter, bearer token via header)
+
+### 5.2 Why Not Use Kobo's Token?
+
+Kobo sends `Authorization: Bearer {kobo-store-token}` which is:
+- Managed by Kobo (not under our control)
+- May rotate/expire without warning
+- Only valid for Kobo's servers
+- Doesn't identify device in our system
+- Can't be extracted from Kobo config without jailbreak
+
+**Decision:** Use API key in URL path instead (proven by Komga). The API key in URL path provides per-device authentication that is revocable and works with stock Kobo firmware without jailbreak.
+
+### 5.3 Token Regeneration Strategy
+
+**Backend:**
+- Endpoint: `POST /api/devices/:id/regenerate-token`
+- Requires: User JWT auth (device owner)
+- Action: Generate new UUID, update `devices.auth_token`
+- Response: Return new token to user
+
+**Frontend:**
+- "Regenerate Token" button on device management page
+- Confirmation dialog: "This will revoke current token. Continue?"
+- One-click copy to clipboard
+- Update display instructions with new token
+- Toast notification: "Token regenerated successfully"
+
+**Impact:**
+- Old token immediately invalid
+- Device must update config with new token
+- Sync will fail until config updated
+- Useful for: Lost tokens, compromised devices, periodic rotation
+
+### 5.4 Why Bearer Token for KOReader?
**Pros:**
-- Cryptographically secure
+- Cryptographically secure (random UUID)
- Can be revoked/regenerated
- Standard authentication method
- Works with KOReader plugin architecture
+- Plugin can store token securely
**Cons:**
- Requires plugin (can't use native settings)
@@ -375,45 +482,38 @@ func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerF
**Trade-off Accepted:**
More secure but requires plugin installation. Justified because KOReader users are typically more technical and willing to install plugins.
-### 5.3 Why Not Use Kobo's Token?
-
-Kobo sends `Authorization: Bearer {kobo-store-token}` which is:
-- Managed by Kobo (not under our control)
-- May rotate/expire without warning
-- Only valid for Kobo's servers
-- Doesn't identify the device in our system
-
-**Decision:** Parse the `x-kobo-device` header for serial number instead.
+**Note:** Same token regeneration as Kobo (reuses backend logic)
---
## 6. Files to Modify
### Core Authentication
-- `internal/middleware/device_auth.go` - Enhance with serial support
-- `internal/database/queries/devices.sql` - Add serial lookup query
-- `internal/router/opds.go` - Apply middleware
+- `internal/middleware/device_auth.go` - Add URL path token extraction
+- `internal/router/sync.go` - Update Kobo routes to use path parameter
+- `internal/router/opds.go` - Add ?token= query parameter support (already has middleware)
### Device Management
-- `templates/devices.templ` - Update registration form
-- `internal/handlers/devices.go` - Handle serial input
+- `internal/handlers/devices.go` - Add regenerate token endpoint
+- `internal/database/queries/queries.sql` - No changes needed (auth_token already exists)
+- `templates/devices.templ` - Add copy token, regenerate token buttons
+- `templates/partials/device-management.templ` - Kobo config instructions with API key
### Testing
-- `cmd/server/tests/kobo_test.go` - Update for serial auth
-- `cmd/server/tests/opds_test.go` - Add auth tests
-- `cmd/server/tests/test_helpers.go` - Add serial auth helpers
+- `cmd/server/tests/kobo_test.go` - Update for URL path token
+- `cmd/server/tests/opds_test.go` - Add ?token= auth tests
+- `cmd/server/tests/test_helpers.go` - Add token regeneration helpers
### Documentation
-- `docs/user/devices/kobo-setup.md` - Rewrite for serial auth
+- `docs/user/devices/kobo-setup.md` - Rewrite for API key auth
- `docs/user/devices/koreader-setup.md` - Update for Bearer token
-- `docs/user/security.md` - New security implications doc
-- `koreader-plugin/README.md` - Plugin documentation
+- `docs/user/devices/security.md` - New security considerations doc
+- `docs/developer/api/kobo/` - Update API docs with path parameters
### New Files
-- `koreader-plugin/bookhoard.koplugin/_meta.lua` - Plugin metadata
-- `koreader-plugin/bookhoard.koplugin/main.lua` - Plugin implementation
-- `docs/user/quickstart-kobo.md` - Quick start guide
-- `docs/user/quickstart-koreader.md` - Quick start guide
+- `koreader-plugin/bookhoard.koplugin/_meta.lua` - Plugin metadata (Phase 3)
+- `koreader-plugin/bookhoard.koplugin/main.lua` - Plugin implementation (Phase 3)
+- `docs/user/devices/security.md` - Security considerations
---
@@ -473,12 +573,14 @@ Based on user feedback, the following decisions have been finalized:
**Rationale:** Clean separation of concerns, easier plugin-specific issues/PRs.
### 9.3 Kobo Authentication
-**Decision:** Serial number ONLY
-- No Bearer token fallback for Kobo
-- Serial entered during device registration
-- Stored in `devices.device_identifier`
+**Decision:** API key in URL path (per-device)
+- No serial number needed
+- Token entered in device registration (already exists)
+- Configured in Kobo as: `api_endpoint=http://IP:8765/api/sync/kobo/{TOKEN}`
+- Token can be regenerated by user (new endpoint)
+- Stored in `devices.auth_token` field (no schema change)
-**Rationale:** Simplest user experience, one auth method per platform.
+**Rationale:** Simplest user experience (just copy token), most secure (revocable), proven to work (Komga).
### 9.4 Conflict Resolution
**Decision:** Use existing codebase strategy
@@ -497,6 +599,164 @@ Based on user feedback, the following decisions have been finalized:
**Rationale:** Faster time-to-market for Kobo users, plugin can follow.
+### 9.6 KOReader Device Identification Strategy
+
+**Problem:** Multiple KOReader installations each register as separate devices, exhausting `max_devices` limit even though they represent the same physical device.
+
+**Note:** This is about device MANAGEMENT, not authentication. Authentication uses `auth_token` (API keys) for all devices. Device identifier is for tracking multiple installations of the same physical device.
+
+**Decision:** Self-registration with plugin-generated device ID that persists across KOReader reinstalls.
+
+**Architecture:**
+- Plugin generates unique `device_id` on first launch (stored in KOReader settings)
+- Plugin sends `device_identifier` during registration API call
+- Backend validates `auth_token` matches device record
+- Reinstalling KOReader reads same `device_id` from settings, reuses existing device record
+
+**Registration Flow Options:**
+
+| Approach | User Experience | Complexity | Pros/Cons |
+|----------|-----------------|------------|-------------|
+| **A. Manual Link** | User registers in Bookhoard, copies Device ID + Token | Low | ✅ Simple server
❌ User copies two values |
+| **B. Self-Register** | Plugin auto-registers, user approves in web UI | Medium | ✅ One-time setup
⚠️ Requires approval endpoint |
+| **C. QR Code Bridge** | Scan QR code with Device ID + Token | Low-Medium | ✅ Very user-friendly
❌ Requires QR library |
+
+**Recommendation:** **Option B (Self-Register)** for best UX.
+
+**Backend Changes Required:**
+```go
+// Devices table - Add Device Identifier (Already Exists!)
+-- devices.device_identifier already exists (models.go:67)
+-- No schema change needed - just use it properly
+
+// internal/handlers/devices.go - InitiateDeviceRegistration()
+// Add optional pre_generated_device_id parameter:
+DeviceIdentifier string `json:"device_identifier" validate:"omitempty,uuid"`
+```
+
+**Authentication Middleware Update:**
+```go
+// Try Bearer token first (KOReader, API clients)
+// Then try URL path parameter (Kobo, OPDS)
+func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
+ // 1. Try Bearer token header (KOReader, API clients)
+ authHeader := c.Request().Header.Get("Authorization")
+ if authHeader != "" {
+ token := strings.TrimPrefix(authHeader, "Bearer ")
+ device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), token)
+ if err == nil {
+ return m.setDeviceContext(c, device)
+ }
+ }
+
+ // 2. Try URL path parameter (Kobo, OPDS)
+ urlToken := c.Param("token")
+ if urlToken == "" {
+ urlToken = c.QueryParam("token") // Fallback to query param
+ }
+ if urlToken != "" {
+ device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), urlToken)
+ if err == nil {
+ return m.setDeviceContext(c, device)
+ }
+ }
+
+ return c.JSON(401, map[string]string{"error": "authentication required"})
+}
+```
+
+**Plugin Changes Required:**
+```lua
+-- First Run Detection:
+-- On first launch:
+if not G_Settings:hasSetting("bookhoard_device_id") then
+ local device_id = uuid.generate()
+ G_Settings:saveSetting("bookhoard_device_id", device_id)
+ G_Settings:saveSetting("bookhoard_device_name", "KOReader on " .. Device.model)
+
+ -- Trigger registration
+ register_device()
+end
+
+-- Registration API Call:
+-- POST /api/devices/register
+{
+ device_name: "KOReader on Kindle Paperwhite",
+ device_type: "koreader",
+ device_identifier: "550e8400-e29b..." -- From plugin settings
+}
+```
+
+**Device Management UI:**
+- Display `device_identifier` for each device
+- "Copy Device ID" button for KOReader setup
+- "Regenerate Token" doesn't change Device ID
+- Show "Linked Devices" count (e.g., "3 KOReader installations linked")
+
+**Sync Flow With Device ID:**
+```lua
+-- Plugin Headers:
+Authorization: Bearer {auth_token}
+X-Bookhoard-Device-ID: {device_identifier}
+```
+
+```go
+// All devices use auth_token for authentication (single method)
+func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
+ // 1. Try Bearer token header (KOReader, API clients)
+ authHeader := c.Request().Header.Get("Authorization")
+ if authHeader != "" {
+ token := strings.TrimPrefix(authHeader, "Bearer ")
+ device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), token)
+ if err == nil {
+ return m.setDeviceContext(c, device)
+ }
+ }
+
+ // 2. Try URL path parameter (Kobo, OPDS)
+ urlToken := c.Param("token")
+ if urlToken == "" {
+ urlToken = c.QueryParam("token") // Fallback to query param
+ }
+ if urlToken != "" {
+ device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), urlToken)
+ if err == nil {
+ return m.setDeviceContext(c, device)
+ }
+ }
+
+ return c.JSON(401, map[string]string{"error": "authentication required"})
+}
+```
+
+**Benefits:**
+
+| Aspect | Current | With Device ID |
+|---------|----------|---------------|
+| **Reinstalls** | New device each time | Same device, update token only |
+| **max_devices** | Counts installs, not devices | Accurate device count |
+| **Security** | Token-only | Device ID + token (two-factor) |
+| **Troubleshooting** | "Which KOReader is this?" | Clear device identification |
+| **Plugin Transfer** | Manual token copy | Migrate settings file |
+
+**Phase Integration:** Implement Device ID in Phase 1 (alongside dual authentication). It's same amount of work but solves `max_devices` problem elegantly.
+
+**Questions for User:**
+1. **Registration Workflow:** Should KOReader plugin self-register (user approves in browser) or require manual registration first?
+2. **Device ID Persistence:** Where does KOReader store settings?
+ - `/mnt/us/koreader/settings.bookhoard.lua`?
+ - System `G_Settings`?
+ - This determines if Device ID survives plugin updates
+3. **max_devices Behavior:**
+ - Should each Device ID count as 1 device (recommended)?
+ - Or limit tokens per Device ID (more complex)?
+4. **Backward Compatibility:**
+ - How to handle existing KOReader installations without Device ID?
+ - Force re-registration? Auto-migrate?
+5. **KOReader Plugin Repo:**
+ - Does `github.com/bookhoard/koreader-plugin` exist?
+ - Should Device ID generation happen there, or in main Bookhoard repo?
+
---
## 10. Historical Context & Conversation Summary
@@ -512,11 +772,11 @@ This implementation plan emerged from a detailed analysis of the current authent
- Test `GetDeviceCatalog_WithoutDeviceAuth` expected 401 but got 404
- DeviceAuthMiddleware was already applied to OPDS routes, but tests were failing
-**Root Cause Discovery:**
+**Root Cause Discovery:**
- Kobo devices send `Authorization: Bearer {kobo-store-token}` (Kobo's store token, not Bookhoard's)
-- Kobo also sends `x-kobo-device: {"SerialNumber":"N123..."}` header
-- The middleware was rejecting requests because the Bearer token didn't match `devices.auth_token`
- Kobo cannot send custom Bearer tokens through `eReader.conf`
+- Current middleware only supports Bearer tokens in Authorization header
+- Solution: Use API key in URL path (proven by Komga)
#### The Sidecar File Red Herring
@@ -528,7 +788,10 @@ This implementation plan emerged from a detailed analysis of the current authent
- No plugin architecture on Kobo (unlike KOReader)
- Would require Kobo firmware modification (impossible for most users)
-**Decision:** Abandon sidecar approach, use serial-based auth instead
+**Decision:** Abandon sidecar approach, use API key in URL path (proven by Komga)
+- API keys are revocable and regenerable
+- Works with stock Kobo firmware
+- Simple user configuration
#### KOReader Documentation Bug
@@ -569,20 +832,20 @@ This implementation plan emerged from a detailed analysis of the current authent
### Technical Constraints Discovered
1. **Kobo Firmware Limitations:**
- - Cannot send custom HTTP headers (except `x-kobo-device`)
+ - Cannot send custom HTTP headers
- Cannot install plugins
- - Settings in `eReader.conf` are limited
- - NickelMenu required for OPDS access (jailbreak)
+ - Settings in `eReader.conf` are limited to specific keys
+ - Solution: API key in URL path works with stock firmware
2. **KOReader Capabilities:**
- Can install plugins (copy files to `/koreader/plugins/`)
- Lua-based plugin architecture
- Can send custom HTTP requests
- - Can access device serial for identification
+ - Plugin can register itself
- Native OPDS client support
3. **Security Trade-offs:**
- - Serial numbers: Somewhat predictable, easy for users
+ - API Key in URl: Which means it's stored in logs
- Bearer tokens: Cryptographically secure, requires setup
- User accepted security/usability trade-off for self-hosted use
@@ -597,7 +860,7 @@ This implementation plan emerged from a detailed analysis of the current authent
- Prioritizes working features over perfect security
**Resulting Architecture:**
-- Kobo: Serial auth (medium security, zero friction)
+- Kobo: API key in URL path (revocable, simple)
- KOReader: Bearer token (high security, requires plugin setup)
- Both get full feature parity
- Security implications clearly documented
@@ -627,7 +890,7 @@ This implementation plan emerged from a detailed analysis of the current authent
### Milestones
**Milestone 1 (End of Week 2):** Kobo Fully Functional
-- Serial auth working
+- Kobo: API key in URL (works with stock firmware)
- All sync features operational
- OPDS access secured
- Can ship to Kobo users
@@ -646,7 +909,7 @@ This implementation plan emerged from a detailed analysis of the current authent
**Authentication Core:**
- `internal/middleware/device_auth.go:37-108` - Main auth middleware
-- `internal/database/queries/devices.sql` - Device queries
+- `internal/database/queries/queries.sql` - Device queries
- `internal/database/models.go` - Device model (has device_identifier)
**Routing:**
@@ -687,14 +950,15 @@ This implementation plan emerged from a detailed analysis of the current authent
**Functional:**
- ✅ Kobo: Enter serial → register → sync works immediately
-- ✅ KOReader: Install plugin → enter token → sync works
+- ✅ KOReader: Install plugin → plugin registers → sync works
- ✅ Both: Full sync (progress, highlights, notes, bookmarks)
- ✅ OPDS: Authenticated access on both platforms
- ✅ Cross-device: Read on one, continue on another
**Security:**
-- ✅ OPDS no longer public
-- ✅ All sync endpoints require auth
+- ✅ OPDS no longer publicly accessible (authenticated with API key or Bearer token)
+- ✅ All sync endpoints require device authentication (API key or bearer token)
+- ✅ Device authentication uses revocable API keys (Kobo/KOReader)
- ✅ Security implications documented
- ✅ Network security recommendations provided
@@ -717,9 +981,9 @@ This implementation plan emerged from a detailed analysis of the current authent
## 10. Risk Assessment
**Low Risk:**
-- Serial auth implementation (well-understood pattern)
+- API key in URL path (well-understood pattern)
- OPDS security (straightforward middleware application)
-- Kobo sync (already implemented, just fixing auth)
+- Kobo sync (already implemented, just fixing auth route)
**Medium Risk:**
- KOReader plugin development (Lua learning curve, potential API changes)
@@ -761,65 +1025,52 @@ This implementation plan emerged from a detailed analysis of the current authent
---
-## 14. Critical Modifications Required
+## 14. Authentication Strategy Confirmation
-### Issues Discovered During Review
+### 14.1 Kobo Authentication: API Key in URL Path
-The following issues were identified during codebase analysis and must be addressed before or during implementation:
+**Decision:** Use per-device API keys embedded in URL path (proven by Komga)
-### 14.1 Missing Database Query
+**Architecture:**
+- Kobo route: `/api/sync/kobo/{api_key}/*`
+- API key generated during device registration
+- User copies full URL from device management page
+- Paste into Kobo's `api_endpoint` configuration
-**Issue**: Phase 1 references `GetDeviceByDeviceIdentifier` query but it doesn't exist in queries.sql
+**Why This Approach:**
+- ✅ Proven by Komga (production-tested, works reliably)
+- ✅ No jailbreak needed - works with stock Kobo firmware
+- ✅ API keys are revocable and can be regenerated
+- ✅ Uses existing `auth_token` field (no schema change)
+- ✅ Simple user configuration (one line in config file)
+- ✅ More secure than serial-based authentication
-**Location**: `internal/database/queries/queries.sql`
+**Rejection: Serial-based via X-Kobo-Device Header**
+- Serials are predictable and cannot be revoked
+- More complex implementation (JSON parsing, device_identifier lookup)
+- Less secure than random API keys
+- No benefit over URL path approach
-**Required Addition**:
-```sql
--- name: GetDeviceByDeviceIdentifier :one
-SELECT * FROM devices WHERE device_identifier = $1;
+---
+
+### 14.2 Router Configuration
+
+**Required Change:**
+```go
+// Current routing
+koboSync := e.Group("/api/sync/kobo")
+koboSync.POST("/markup", cfg.DeviceAuthMiddleware.Authenticate(...))
+
+// New routing (supports API key in URL path)
+koboSync := e.Group("/api/sync/kobo/:token")
+koboSync.POST("/markup", cfg.DeviceAuthMiddleware.Authenticate(...))
```
-**Impact**: Blocker - cannot implement serial-based auth without this query
-
----
-
-### 14.2 Incorrect Documentation
-
-**Issue**: Current `docs/user/devices/kobo-setup.md` contains WRONG authentication information
-
-**Problems**:
-- States Kobo uses "Username/Password" in config file (lines 116-118)
-- Kobo firmware sends `Authorization: Bearer {kobo-store-token}` - NOT configurable
-- Kobo sends `x-kobo-device: {"SerialNumber":"..."}` header - this is what we must use
-- Kobo CANNOT send custom Bearer tokens through `eReader.conf`
-
-**Required Rewrite**: Section "Configure Kobo Sync" (lines 100-133) must be updated to:
-1. Remove Username/Password references
-2. Explain serial-based auth works via `x-kobo-device` header (sent automatically by Kobo)
-3. Clarify that NO manual token configuration is needed for Kobo
-4. Update troubleshooting section to reflect serial auth approach
-
----
-
-### 14.3 OPDS Authentication Inconsistency
-
-**Issue**: OPDS clients (including Kobo's native browser and KOReader) have limited header support
-
-**Problem**:
-- Kobo OPDS browser: Cannot send custom `Authorization` headers
-- KOReader OPDS client: Can send headers via plugin, but native OPDS support varies
-- Current plan relies on `DeviceAuthMiddleware` checking `Authorization` header
-
-**Proposed Solution**: Implement **dual authentication** for OPDS endpoints:
-
-1. **Header-based** (existing): Check `Authorization: Bearer {token}` for KOReader plugin
-2. **URL-based** (new): Check `?token=...` query parameter for Kobo and other clients
-
-**Implementation**:
+**Middleware Enhancement:**
```go
-// In internal/middleware/device_auth.go
-func (m *DeviceAuthMiddleware) authenticateWithFallback(c echo.Context) error {
- // Try Bearer token first
+// Add path parameter extraction (supports both Bearer token and URL token)
+func (m *DeviceAuthMiddleware) Authenticate(next echo.HandlerFunc) echo.HandlerFunc {
+ // 1. Try Bearer token header (KOReader, API clients)
authHeader := c.Request().Header.Get("Authorization")
if authHeader != "" {
token := strings.TrimPrefix(authHeader, "Bearer ")
@@ -828,112 +1079,178 @@ func (m *DeviceAuthMiddleware) authenticateWithFallback(c echo.Context) error {
return m.setDeviceContext(c, device)
}
}
-
- // Fallback: Check URL token parameter for OPDS
- urlToken := c.QueryParam("token")
+
+ // 2. Try URL path parameter (Kobo, OPDS)
+ urlToken := c.Param("token")
+ if urlToken == "" {
+ urlToken = c.QueryParam("token") // Fallback to query param
+ }
if urlToken != "" {
device, err := m.db.GetDeviceByAuthToken(c.Request().Context(), urlToken)
if err == nil {
return m.setDeviceContext(c, device)
}
}
-
- return c.JSON(http.StatusUnauthorized, map[string]string{"error": "authentication required"})
+
+ return c.JSON(401, map[string]string{"error": "authentication required"})
}
```
-**Why This Matters**: Without URL-based fallback, Kobo devices cannot access OPDS catalogs regardless of authentication method.
+**Frontend:**
+- Display `auth_token` for each device (API key for Kobo, token for KOReader)
+- "Copy Device ID" button for KOReader setup
+- "Regenerate Token" doesn't change auth_token
+
+---
+
+### 14.3 Token Regeneration
+
+**Required Addition:** Backend endpoint to regenerate tokens
+
+**Implementation:**
+```go
+// internal/handlers/devices.go
+func (h *Handlers) RegenerateDeviceToken(c echo.Context) error {
+ deviceID, err := uuid.Parse(c.Param("id"))
+ // ... user auth check ...
+
+ newToken := fmt.Sprintf("dev_%s", uuid.New().String())
+
+ _, err = h.db.UpdateDevice(c.Request().Context(), database.UpdateDeviceParams{
+ ID: pgtype.UUID{Bytes: deviceID.Bytes(), Valid: true},
+ AuthToken: pgtype.Text{String: newToken, Valid: true},
+ })
+
+ return c.JSON(200, map[string]string{
+ "auth_token": newToken,
+ "message": "Token regenerated successfully",
+ })
+}
+```
+
+**Route:**
+```go
+// internal/router/device.go
+devices.PUT("/:id/regenerate-token", jwtMiddleware, h.RegenerateDeviceToken)
+```
+
+**Frontend:**
+- "Regenerate Token" button on device details
+- Confirmation dialog
+- One-click copy to clipboard
+- Update Kobo config example with new token
+
+**Status**: ✅ New feature needed
---
### 14.4 Enhanced Security Documentation
-**Issue**: Security implications of serial-based auth need stronger emphasis
-
**Add to `docs/user/security.md`** (new file):
-```markdown
-## Serial-Based Authentication Security Considerations
+## API Key Authentication Security Considerations
### Risks
-- **Predictable Identifiers**: Kobo serial numbers follow known patterns (N + 12 digits)
-- **No Rotation**: Unlike Bearer tokens, serial numbers cannot be changed
-- **Device Theft**: Physical access to device grants permanent access until manually revoked
-- **Network Exposure**: On public networks, serial could be intercepted
+- **URL Exposure**: API keys visible in Kobo logs and server logs
+- **Config File Storage**: API key appears in Kobo config file (plain text)
+- **Network Interception**: On public networks, API key in URL could be intercepted
+- **Device Theft**: Physical access to device grants access until token is regenerated
### Recommended Mitigations
-1. **Network Security** (REQUIRED for serial auth)
- - Use VPN for remote access
- - Restrict Bookhoard to local network only
- - Use reverse proxy with SSL/TLS termination
- - Implement firewall rules limiting access by IP
+1. **Network Security** (REQUIRED for self-hosted app)
+ - Use HTTPS for sync (reverse proxy with SSL/TLS)
+ - If you expose Bookhoard publicly: Use reverse proxy with SSL/TLS termination
+ - Ensure API keys are transmitted over encrypted connection
+ - Keep firewall rules restricting access by IP (fail2ban for repeated failed auth attempts)
-2. **Monitor Access Logs**
- - Review device sync logs regularly
- - Set up alerts for:
- - Unknown serial numbers
- - Sync attempts from unusual locations
- - Rapid sync failures (possible brute force)
+2. **Token Management**
+ - Regenerate API keys if compromised or lost
+ - One-click regeneration in device management UI
+ - Automatic expiration and rotation (optional, for high-security deployments)
-3. **Device Management**
- - Revoke unrecognized devices immediately
- - Use device approval workflow (already implemented)
- - Regular audit of registered devices
+3. **Security Documentation**
+ - Document that API keys are sensitive (like passwords)
+ - Recommend local network only (home or VPN)
+ - For remote access, use reverse proxy with valid SSL certificates
+ - Provide clear warnings in UI when API keys are displayed or copied
-4. **HTTPS is Mandatory for Remote Access**
- - Never expose Bookhoard over plain HTTP publicly
- - Use valid SSL/TLS certificates
- - Consider client certificate authentication for high-security deployments
+# Kobo OPDS
+http://IP:8765/opds/devices/{DEVICE_ID}/catalog?token={API_KEY}
-### Comparison
-
-| Method | Security | Usability | Revocable | Rotation |
-|---------|-----------|-------------|------------|----------|
-| Serial Number | ⭐⭐ Medium | ⭐⭐⭐⭐⭐ Excellent | No | No |
-| Bearer Token | ⭐⭐⭐⭐ High | ⭐⭐⭐ Good | Yes | Yes |
-
-**Recommendation**: For self-hosted personal use with trusted local network, serial auth provides acceptable security/usability trade-off.
+# KOReader OPDS (via plugin - uses header automatically)
+http://IP:8765/opds/devices/{DEVICE_ID}/catalog
```
----
+**Phase 1 Tasks:**
-### 14.5 Implementation Order Adjustment
+1. **[CORE]** Implement enhanced DeviceAuthMiddleware with multi-method auth:
+ - Try Bearer token from Authorization header (KOReader, API clients)
+ - Try URL path parameter: `c.Param("token")` (Kobo sync)
+ - Try query parameter: `c.QueryParam("token")` (OPDS access)
+ - All methods lookup device via `GetDeviceByAuthToken`
+ - Set device context on successful auth
-**Modified Phase 1 Tasks**:
+2. **[CORE]** Update Kobo routing to use path parameter:
+ - Change route from `/api/sync/kobo` to `/api/sync/kobo/:token`
+ - All Kobo sync endpoints inherit token from path
+ - Update handler to use path parameter
-1. **[CRITICAL]** Add `GetDeviceByDeviceIdentifier` to queries.sql
-2. **[CRITICAL]** Update kobo-setup.md documentation with correct auth flow
-3. Implement enhanced DeviceAuthMiddleware with dual header/URL support
-4. Add device_identifier index if not exists
-5. Update tests to verify both auth methods work
+3. **[CORE]** Add token regeneration endpoint:
+ - Create SQL query: `UpdateDeviceAuthToken(id, auth_token)`
+ - Add handler: `POST /api/devices/:id/regenerate-token`
+ - Generate new API key, update database
+ - Return new token to user
-**Why This Order**: Database query must exist BEFORE middleware can use it. Documentation must be correct BEFORE users attempt setup.
+4. **[CRITICAL]** Update kobo-setup.md documentation:
+ - Remove Username/Password references (lines 116-118)
+ - Explain API key in URL configuration
+ - Show full URL with token: `http://IP:8765/api/sync/kobo/{API_KEY}`
+ - Document token regeneration process
+
+5. **[CRITICAL]** Update koreader-setup.md documentation:
+ - Change "Basic Auth" to "Bearer Token" authentication
+ - Remove incorrect username/password references (lines 26-27)
+ - Explain plugin token management
+
+6. **[TESTING]** Update test coverage:
+ - Test Kobo sync with API key in URL path
+ - Test KOReader sync with Bearer token header
+ - Test OPDS access with both auth methods (path param and query param)
+ - Test token regeneration invalidates old token
---
## 15. Updated Success Criteria
-Add to Section 7:
-
-**Authentication**:
-- ✅ Kobo serial-based auth working via `x-kobo-device` header
-- ✅ KOReader Bearer token auth working via `Authorization` header
-- ✅ OPDS accessible via BOTH header and URL token fallback
+**Authentication:**
+- ✅ Kobo API key auth working via URL path parameter
+- ✅ KOReader Bearer token auth working via Authorization header
+- ✅ OPDS accessible via BOTH authentication methods (middleware handles both)
+- ✅ Token regeneration endpoint functional
- ✅ Security implications documented with mitigation strategies
-**Database**:
-- ✅ `GetDeviceByDeviceIdentifier` query implemented and indexed
-- ✅ `device_identifier` column has UNIQUE constraint (already exists in schema)
+**Database:**
+- ✅ `auth_token` field used for all devices (no schema change)
+- ✅ Token regeneration updates existing field
+- ✅ `device_identifier` column available for KOReader device linking (not used for auth)
-**Documentation**:
-- ✅ kobo-setup.md reflects actual auth mechanism (no username/password)
-- ✅ security.md created with serial auth considerations
-- ✅ OPDS dual-auth documented in troubleshooting
+**Frontend:**
+- ✅ Device management UI has copy/regenerate token buttons
+- ✅ Kobo config shows API key in URL format
+- ✅ Clear setup instructions per device type
+
+**Documentation:**
+- ✅ kobo-setup.md reflects API key authentication
+- ✅ koreader-setup.md updated for Bearer token
+- ✅ security.md created with token management considerations
+- ✅ OPDS authentication clearly documented
---
-**Modified Plan Status**: Critical issues identified and resolved. Ready for implementation.
+**Plan Status**: ✅ Corrections Applied - Plan is now accurate and ready for implementation
-**Last Updated**: Based on codebase review - added Section 14 modifications
\ No newline at end of file
+**Last Updated**: 2026-02-12 - Corrected sections 14.1, 1.4, 14.3, 14.5, and 15 based on codebase analysis
+
+**Summary**: Original plan was 95% solid with 3 minor inaccuracies about existing code. Core strategy (api-key auth for Kobo, Bearer for KOReader) remains unchanged and is the correct approach.