Codex integration
tsundoku is discovery-centric: it surfaces series you might want, with no inherent idea of what you already own. The optional Codex integration closes that gap. It periodically reads your Codex library and overlays, on each series, whether you already own it and whether newer volumes or chapters have surfaced since.
The overlay is admin-only. A public (read-tier) visitor never sees it, in the payload or the UI — what's in your library stays private.
Enabling it
Add a [codex] block to your config (see Configuration):
[codex]
enabled = true
base_url = "https://codex.example.com"
api_key = "codex-reader-api-key"
sync_cron = "0 */6 * * *" # every 6 hours; omit to disable the cron
Two things to get right:
api_keyis Codex's key, not tsundoku's. It's anX-API-Keyfor a Codex account with theseries:readscope — unrelated to tsundoku's own[auth] api_key/admin_token. You can supply it via theTSUNDOKU_CODEX__API_KEYenvironment variable instead of the file.base_urldoes double duty. It's both the API base for the sync and the host used to build the deep links the badges point at (<base_url>/series/{uuid}). There is no separate deep-link setting.
When enabled = true, base_url and api_key are required — tsundoku
refuses to start otherwise, rather than silently failing every sync tick.
How the sync works
A scheduled job (and a manual trigger) does one sweep:
- Preflight. Pings Codex's public
GET /api/v1/infoto confirm it's reachable and log its name/version. If Codex is down, the sweep is skipped and your existing ownership data is left untouched — a transient outage never wipes the overlay. - Sweep. Pulls Codex's series index and matches each entry to a tsundoku series by shared external ID (MangaBaka / AniList / MangaUpdates / MAL).
- Persist. Records the link plus Codex's owned volume/chapter maxima locally, so browsing only joins local data — no per-request calls to Codex.
A successful /info proves Codex is reachable; it does not prove your
api_key is valid. The first authenticated sweep is what validates the key —
see Connection status below.
What the badges mean
On the feed (cards and list rows) and the series detail page, an owned series gets a colored badge:
| Badge | Meaning |
|---|---|
| 🟢 owned | On Codex and caught up — Codex owns at least as much as has surfaced. |
| 🔵 behind | On Codex, but newer volumes/chapters have surfaced than Codex owns. Worth grabbing. |
| ⚪ owned? | On Codex, but the relevant count didn't parse on Codex's side, so we can't tell if you're caught up. |
| (no badge) | Not on Codex (or you're not signed in as admin). |
The status compares the highest volume/chapter number discovered from releases against the highest number Codex owns. It deliberately ignores Codex's raw owned-file count (shown only as an approximate "~N owned" tooltip): that count can't tell volumes from chapters reliably, so it's display-only and never drives the badge color.
Click a badge to open the series in Codex.
Note: a series surfaced as volumes but owned on Codex only as loose chapters (or vice-versa) can read as behind even when you effectively have it. Blue means "look at this," which is the safe direction to err.
Filtering by ownership
The feed's Codex filter (admin-only) narrows the list by status: on Codex (any), up to date, behind, unverified, or not on Codex. The filter is enforced server-side — a non-admin request carrying the filter just gets the normal unfiltered feed, so it can't be used to probe your library.
Connection status
The admin maintenance page shows a Codex integration card with the live connection health:
- Reachable / Unreachable — whether the last preflight reached Codex.
- Auth state —
Connected, or one of two distinct failures:- API key rejected (401) — the key is wrong or missing. Fix
codex.api_key. - API key lacks series:read (403) — the key authenticates but the Codex
account doesn't have the
series:readscope. Grant it on the Codex side.
- API key rejected (401) — the key is wrong or missing. Fix
- Linked series, last sync, and the last error message.
- A Refresh now button to trigger a sweep immediately instead of waiting for the cron.
Badges stay hidden until the first successful sweep, so a freshly-enabled integration doesn't briefly show everything as "not owned."
Series Codex can't match automatically
A series with no external ID tsundoku shares with Codex (a manual entry, or one
whose IDs didn't line up) won't auto-link. The API exposes
POST /api/v1/series/{id}/codex-link (and DELETE to unlink) to hand-link such
a series to a Codex UUID; the next sweep then fills in its owned counts. A UI
for this is not yet wired up — use the API directly for now.