Poll comix.to through the browser sidecar (#98) (#105)

Closes #98.

comix.to began answering plain-TLS fetches with a Cloudflare JavaScript
challenge on 2026-08-12, so every poll got a 403 interstitial. Its cover host
`static.comix.to` is gated the same way. comix therefore joins kagane and
novelfull as a browser-backed Site.

## What changed

- **Registry** (`internal/latest/sites.go`): comix gains a `Browser` entry —
  `comixRead`, `Done: body != "" && !isInterstitial(body)`, `Fallback: false`.
  Skip-when-no-browser falls out of the existing routing; no site-string compare
  was added anywhere.
- **Read shape** (`internal/latest/browser.go`): an in-tab `fetch()` of the
  Series URL, not a DOM render. comix is an SPA — rendering it costs ~65
  requests for the same server-rendered HTML one fetch returns (24.5 KB,
  ~480 ms measured). `comixSeriesPageURL` pins scheme + host + `/title/<slug>`
  and rebuilds the address, so a client-supplied `series_url` cannot aim the
  browser anywhere else.
- **Cover bytes**: `comixImageURLRe` pins `https://static.comix.to/<path>.<ext>`;
  `BrowserFetcher.Image` now gates on `browserOnlyCoverURL` rather than a
  kagane-only regex, so both Sites' image URLs route through the one path.
  Bytes come from direct navigation, not a page-context fetch — comix's Series
  page sets `cross-origin-embedder-policy: require-corp`, which fails one.
- **Parsers and stored Series identity: untouched.** The in-tab body is the same
  server-rendered HTML the existing fixtures were cut from.

## Verification

- `go test ./...` green (needs Docker).
- New seam tests: comix routes to the browser when one is configured, and is
  not fetched at all when none is (`TestComixUsesBrowserFetcher`,
  `TestComixSkippedWhenNoBrowserFetcher`); URL-pin and cover-gate table tests.
- Live proof against the real browser unit, `TestSmokeComix` (env-gated):
  page 24793 bytes in one in-tab fetch, chapter 53, cover accepted by the pin,
  26862 bytes of `image/jpg` retrieved.
- Two-axis review run; findings were stale comments on `BrowserFetcher`, `Get`
  and the `Fallback` field, fixed in f000cc7.

Docs updated: root `AGENTS.md` (constraint + smoke command, including the note
that this dev machine's ISP DNS-hijacks `comix.to`), `backend/AGENTS.md`
(poller, cover pipeline, `BROWSER_WS_URL`), `REDEPLOY.md` §8 degrade note.

Reviewed-on: #105
Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
This commit was merged in pull request #105.
This commit is contained in:
2026-08-16 12:13:59 +07:00
committed by sulthan
parent 3f53c79cf4
commit ddbd57070d
16 changed files with 1265 additions and 1006 deletions
+33 -28
View File
@@ -140,8 +140,8 @@
"semantic_hash": "3a08979e4603aae5c32a58d5b6c39765"
},
"CLAUDE.md": {
"mtime": 1786501942.7367291,
"ast_hash": "16b34d73e066d56d8f04523e6b3f6bd9",
"mtime": 1786856633.445473,
"ast_hash": "fab288c23be960d9c6afbfe3f21ff41c",
"semantic_hash": ""
},
"DEPLOY.md": {
@@ -200,8 +200,8 @@
"semantic_hash": "e69a8340a371579ca3ea689660f7d7bd"
},
"AGENTS.md": {
"mtime": 1786501942.7367291,
"ast_hash": "16b34d73e066d56d8f04523e6b3f6bd9",
"mtime": 1786856633.445473,
"ast_hash": "fab288c23be960d9c6afbfe3f21ff41c",
"semantic_hash": ""
},
"userscript/test/logic.test.js": {
@@ -215,9 +215,9 @@
"semantic_hash": "8f3c0132eb4787a2c8736eb99f7689af"
},
"REDEPLOY.md": {
"mtime": 1786363889.552731,
"ast_hash": "d0baf08b95e7b5986234a9f36759c12e",
"semantic_hash": "d0baf08b95e7b5986234a9f36759c12e"
"mtime": 1786856643.0770833,
"ast_hash": "e5e910f0244a040e2ccdc669b17eb4db",
"semantic_hash": ""
},
"docs/design-system.md": {
"mtime": 1786022513.9623306,
@@ -245,14 +245,14 @@
"semantic_hash": "385b36f58488b7e6d93eb6d6034e9ee3"
},
"backend/internal/latest/browser.go": {
"mtime": 1786499529.7688599,
"ast_hash": "ed129a7f00601ea90c877ff29fa21220",
"mtime": 1786856879.3397639,
"ast_hash": "fbdba4bb56d8c804087bcb6e141e52ad",
"semantic_hash": ""
},
"backend/internal/latest/browser_test.go": {
"mtime": 1786262323.6964688,
"ast_hash": "e900f92971486f47d7ef76e9a95217fe",
"semantic_hash": "e900f92971486f47d7ef76e9a95217fe"
"mtime": 1786856254.36163,
"ast_hash": "800fa6aa471ada054a3c48943ad17ab7",
"semantic_hash": ""
},
"backend/internal/latest/fetch.go": {
"mtime": 1786499529.7688599,
@@ -260,23 +260,23 @@
"semantic_hash": ""
},
"backend/internal/latest/poller.go": {
"mtime": 1786499529.7688599,
"ast_hash": "44fef6074ac2eaffc8233f46aad5236b",
"mtime": 1786856670.129715,
"ast_hash": "6b7060ff52994729832d9fcc3cc97dad",
"semantic_hash": ""
},
"backend/internal/latest/poller_test.go": {
"mtime": 1786499529.7688599,
"ast_hash": "64bc838c822f1bf33bbf9e291215454b",
"mtime": 1786856311.8535168,
"ast_hash": "b6837fab377c94fa1d2f20a931975a4a",
"semantic_hash": ""
},
"backend/internal/latest/sites.go": {
"mtime": 1786499529.7688599,
"ast_hash": "b744cc685363317a526cc3bebceea39e",
"mtime": 1786856865.4529688,
"ast_hash": "2f00456ac9d1d8148ba1bc6cbceb24f6",
"semantic_hash": ""
},
"backend/internal/latest/sites_test.go": {
"mtime": 1786499529.7725692,
"ast_hash": "eabca9014a306e3c71d238b0ae499f61",
"mtime": 1786856890.1231265,
"ast_hash": "0db2028a6073f342fee61ad15c2e5f0f",
"semantic_hash": ""
},
"backend/internal/latest/smoke_image_test.go": {
@@ -415,7 +415,7 @@
"semantic_hash": "e44a2f6f624db044e19508bc5ab05592"
},
"CONTEXT.md": {
"mtime": 1786850406.6525955,
"mtime": 1786855457.9327722,
"ast_hash": "24548f60414b4c5ff58538acaada5345",
"semantic_hash": ""
},
@@ -425,13 +425,13 @@
"semantic_hash": "6c6f3e4c4c2f57867894280bce728c50"
},
"backend/AGENTS.md": {
"mtime": 1786501942.7367291,
"ast_hash": "23d8dbbcb9796c679d25a74bb3847f88",
"mtime": 1786856656.6826186,
"ast_hash": "d5610ba24076b57e17b9d76241acaa65",
"semantic_hash": ""
},
"backend/CLAUDE.md": {
"mtime": 1786501942.7367291,
"ast_hash": "23d8dbbcb9796c679d25a74bb3847f88",
"mtime": 1786856656.6826186,
"ast_hash": "d5610ba24076b57e17b9d76241acaa65",
"semantic_hash": ""
},
"backend/internal/web/templates/app.html": {
@@ -560,9 +560,9 @@
"semantic_hash": "46cf7822d4f667e3cab36b547abe5e97"
},
"backend/internal/latest/cover.go": {
"mtime": 1786363889.5565126,
"ast_hash": "e6749cfe3cd7c2e71d4392dde84f55f9",
"semantic_hash": "e6749cfe3cd7c2e71d4392dde84f55f9"
"mtime": 1786856664.2957466,
"ast_hash": "d5f2248c3d11de74bf5a3977651b17c2",
"semantic_hash": ""
},
"backend/internal/latest/cover_fetch_test.go": {
"mtime": 1786363889.5565126,
@@ -623,5 +623,10 @@
"mtime": 1786501942.7367291,
"ast_hash": "1aa17575ab20f2f36583602999a6a60f",
"semantic_hash": ""
},
"backend/internal/latest/smoke_comix_test.go": {
"mtime": 1786856356.3432186,
"ast_hash": "111fdbb75fc68ac2ab1013bc916063cf",
"semantic_hash": ""
}
}