Files
MangaCircus/docs/reading-limiter.md
T
MaashkinoozheandClaude Opus 5.5 b2bb34174c Fold the sentinel into the scraper and drop the sentinel service (BL-0046)
The stall watchdog now runs inside the scraper (ScraperService.checkStalls,
every 30 s from the work loop): same rule as before, one checkpoint-safe
restart, quarantine on the second stall, missing checkpoint is no evidence,
and paused time no longer counts. Stalls are recorded as scraper incidents
(failure.kind 'stall'). The private /api/recovery endpoint is gone.

Dropped with the service: reader/scraper-unreachable incidents (supervisor
and Docker handle crashes; the health page shows "Scraper: unreachable"),
duplicate job-failure incidents and the unused /metrics endpoint.
The stack is now reader + scraper (+ optional peer-gateway).
The supervisor test moved to runtime/test (npm run test:runtime stays).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:58:03 +02:00

5.0 KiB
Raw Blame History

Deployment-controlled reading hours and quiz

The reader enforces reading hours on both GET /api/chapter and chapter page-image requests under /lib/ (HTTP 423 outside hours). The dashboard, manga metadata, covers, progress, reading-time summaries, scraper and administrator pages remain available. A reader tab already open when a window ends checks access every 15 seconds and returns to the gate. Chapter images use Cache-Control: no-store when the limiter is configured. Already downloaded images in a browser or files available directly on disk cannot be recalled; this is a self-imposed server access rule, not DRM. There is no schedule-changing API or UI. Signed-in readers and admins can view the effective weekly schedule and timezone under Settings → Reading hours; that page is read-only.

Docker configuration

Set these deployment variables in the Compose .env file next to docker-compose.yml, then recreate only the reader. The reader service passes them through to the container. READER_IMAGE selects a reader-only release while APP_IMAGE continues to select the scraper image. Leaving READING_WINDOWS_JSON empty disables the limiter. Day keys are 0 Sunday through 6 Saturday. Each day has any number of [start,end] intervals; an end earlier than its start carries into the next day. 24:00 is valid only as an end. Intervals are start-inclusive and end-exclusive, resolved using the IANA timezone, including daylight-saving changes.

READING_WINDOWS_JSON='{"0":[["08:00","01:00"]],"1":[["07:00","12:00"],["13:00","24:00"]],"2":[["07:00","24:00"]],"3":[["07:00","24:00"]],"4":[["07:00","24:00"]],"5":[["07:00","24:00"]],"6":[["08:00","01:00"]]}'
READING_TIMEZONE=Europe/Berlin
READING_QUIZ_UNLOCK_MINUTES=60
READING_QUIZ_CATALOG_PATH=/data/reading-quiz.json

The Monday split above illustrates multiple on/off periods; replace it with [["07:00","24:00"]] for an uninterrupted weekday. The Saturday and Sunday windows extend to 01:00 on the following mornings. A malformed timezone, schedule, unlock length or catalog stops the reader at startup rather than silently removing the restriction. READING_QUIZ_UNLOCK_MINUTES accepts 1–1440. The scraper is independent and must not be restarted for this configuration. Until a reviewed catalog is installed at READING_QUIZ_CATALOG_PATH, the outside-hours gate cannot offer a quiz. It does not relax the schedule.

Question bank and progress watermark

The bank is a deployment-managed JSON file, separate from manga.json and from the scraper. See config/reading-quiz.example.json. Its manga keys are exact library folder IDs. Each question has a stable ID, prompt, accepted answer variants, an evidence citation, and at least one requires chapter ID. The chapter IDs are the reader's IDs (for example 1/12, not the on-disk v01/c0012). Every required chapter must be marked completed for that user before the question enters their pool. This supports a single watermark or a set of specific sections. Five eligible questions are needed to offer a quiz; an empty bank simply leaves outside-hours reading locked. A question's answers and evidence never go to the browser.

The challenge randomly samples five progress-eligible questions across all library manga, not only the one being opened. It asks them sequentially, accepts unlimited retries, and has no answer timer. Accepted variants are normalized for case, punctuation and accents; one typo is tolerated for answers of five or more characters, two for answers of eleven or more. Starting a new visit invalidates the previous challenge, and leaving the page abandons it. Passing all five grants that user reading access across manga for the configured number of minutes, persisted in db.json across reader restarts. Questions are not shown before their watermark; this depends on the reader's existing self-reported completion records.

Validate a proposed file with node tools/validate-reading-quiz.js <catalog.json> before pointing the reader at it. For an eventual research agent: write drafts with source citations, aliases and proposed chapter watermarks, then have a human verify identity, answer and spoiler boundary. Only a reviewed catalog should be copied to the configured path and loaded by restarting the reader. No wiki or transcript fetching is performed by the reader itself.

Testing and rollback

node tools/test-compose.js --reading-limit-ui creates a disposable Compose fixture with two progress-qualified sample manga, tests chapter and image denial, quiz flow and the one-hour unlock, then tears it down. Add --keep to leave an isolated playable instance locked for manual review; its printed URL and fixture path identify it. Main data and the running main scraper are not mounted. To roll back a future main deployment, restore the previous reader image and configuration, recreate only the reader, and preserve current db.json and library. The additional readingAccess field is backward-safe.