17ee0bd3f8
Docs only. No code changes - `git diff origin/main --stat` touches five Markdown files and adds one research note. ## What was wrong Several docs explained Cloudflare challenges as a "bot score" that our request rate could worsen. That mechanism does not exist on these sites. Researched live on 2026-08-12 against Cloudflare's own documentation and blog plus RFC 9309 - 22 primary pages, every claim carrying a source URL and read date, seven areas explicitly marked `Not publicly documented`. The note is `docs/research/cloudflare-bot-scoring-and-poll-cadence.md`. - The 1-99 bot score is **Enterprise Bot Management only**. A free-plan zone has no score at all; it gets Bot Fight Mode, which matches *signatures* (headless browsers, cloud-hosting IPs). - **No per-IP request rate is documented as an input to challenge issuance.** Volume is policed by Rate Limiting Rules, a separate opt-in product: one rule, IP-only counting, 10-second windows on Free. Published DDoS thresholds are ~1,000 errors/sec. - **`cf_clearance` defaults to 30 minutes**, so every cadence at or above 1 hour re-solves the challenge anyway. Cadence changes how many ~4s solves happen per day and nothing else. - The documented risk is **fingerprint quality**, which this repo already solved (real Chrome, stock UA, non-UTC clock). ## What changed | File | Correction | |---|---| | `AGENTS.md` | The block is per-zone configuration plus request fingerprint, not IP reputation. comix.to turning its gate on 2026-08-12 is the worked example. Residential egress avoids the cloud-hosting-IP *signature* rather than earning a better score. The UTC measurement stands; its mechanism is now marked undocumented. | | `backend/AGENTS.md` | Says why `_BROWSER_COOLDOWN` is longer: cost, not safety. | | `docs/adr/0003` | Dated correction - the sites do not "bot-score" the VPS IP. Decision stands on its sweep-depth argument. | | `docs/adr/0006` | Dated correction - no score to be better at. Decision stands on VPS memory. | | `DEPLOY.md` | A red kagane smoke run means the Site's settings or this Chrome's fingerprint moved, not "Cloudflare's scoring". | ADRs got dated `Corrected 2026-08-12:` paragraphs rather than silent rewrites - the record of what was decided stays intact, only the wrong mechanism is retracted. ## Deliberately not in this PR - **The 6h browser cooldown is unchanged.** I had lowered it to 1h and reverted that; cadence is a behaviour change and belongs with the comix work in #98, not in a docs correction. - **Two code comments still carry the myth**: `backend/main.go:83-84` ("a hammer against sites that are already bot-scoring us"). Left alone to keep this diff docs-only. Related: #98. Reviewed-on: #99 Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com> Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
52 lines
2.8 KiB
Markdown
52 lines
2.8 KiB
Markdown
# Series is a shared entity, and only the Poll may update it
|
|
|
|
Status: accepted
|
|
|
|
Facts about a Series that are true regardless of who is reading — title, cover, canonical
|
|
URL, Latest Chapter — moved off the Bookmark onto a shared `series` row keyed
|
|
`(site, series_id)`. A Bookmark now holds only what differs between Readers: Progress,
|
|
Favourite, Lifecycle bucket. Fifty Readers tracking one Series produce fifty Bookmarks
|
|
and one Series, so the Series is polled once rather than fifty times.
|
|
|
|
## Why
|
|
|
|
The poller checks at most 84 series/hour (batch 14 per 10-minute tick). With ~50 Readers
|
|
holding ~30 Series each, polling per Bookmark means a 1,500-item sweep — roughly 18 hours
|
|
against a configured 1-hour cooldown, quietly breaking the New Chapter signal that is the
|
|
product's reason to exist. Deduplicating to distinct Series cuts the sweep several-fold,
|
|
and because the Series row now knows how many Readers hold it, the poll queue is ordered
|
|
`reader_count DESC, latest_checked_at ASC` — popular Series stay fresh and the long tail
|
|
absorbs the shortfall. That ordering is only expressible because the split happened.
|
|
|
|
Raising throughput instead was rejected: sweeping 400 Series hourly needs the stagger
|
|
cut from 20s to ~9s, doubling request rate against sites already fronted by Cloudflare
|
|
from the single VPS IP.
|
|
|
|
Corrected 2026-08-12: the original wording said those sites "bot-score" the VPS IP.
|
|
They do not — the 1-99 bot score is Enterprise Bot Management only, and no per-IP
|
|
request rate is documented as an input to challenge issuance
|
|
(`docs/research/cloudflare-bot-scoring-and-poll-cadence.md`). The decision stands on
|
|
its first argument, sweep depth versus the 1-hour cooldown; the rate-limit fear was
|
|
never evidenced.
|
|
|
|
## Only the Poll writes Series fields
|
|
|
|
A client may supply `title`, `cover` and `series_url` only when creating a Series nobody
|
|
has bookmarked yet. After that, client-supplied values are ignored; only the backend's
|
|
own fetch updates them.
|
|
|
|
This is a security boundary, not tidiness. Those values are scraped from third-party
|
|
pages, which `AGENTS.md` requires be treated as attacker-controlled. Before the split, a
|
|
hostile or compromised site could corrupt exactly one Reader's row. After it, the same
|
|
write lands on a row every Reader sees — one Reader's browser becomes a write path into
|
|
everyone else's UI, and a cover URL can point anywhere. The backend's own fetch is the
|
|
higher-trust source: its network, its parser, no third-party JavaScript in the path.
|
|
|
|
## Consequences
|
|
|
|
- `Store.Upsert` decomposes one incoming flat body across two tables and enforces the
|
|
ownership rule at that seam.
|
|
- The `updated_at` ordering rule stays on the Bookmark, where Progress lives. Unchanged.
|
|
- Per-Reader title overrides are deliberately not supported; they would reintroduce the
|
|
duplication this removes.
|