Skip to content

Commit d0f4084

Browse files
authored
docs: update timeout chain and botasaurus wait caps (#1246)
* docs: align feed-directory contracts with registry sync * chore: format monitoring.mdx for Prettier lint Unblocks lint CI on docs PRs; formatting drift on main. * docs: update timeout chain and botasaurus wait caps
1 parent 407005c commit d0f4084

6 files changed

Lines changed: 51 additions & 42 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ What this repo owns:
1414
What this repo does not own:
1515

1616
- runtime extractor behavior and CLI semantics (`html2rss/`)
17-
- catalog metadata, YAML configs, or catalog serialization (`html2rss-configs/``Html2rss::Configs::Catalog`)
18-
- catalog HTTP API (`html2rss-web/``GET /api/v1/configs`)
17+
- feed YAML and signed registry bundle publishing (`html2rss-configs/``configs/`, `tool/registry-build`)
18+
- catalog HTTP API and registry sync (`html2rss-web/``GET /api/v1/configs`, `Registry::Sync`)
1919

2020
When docs describe behavior from other repos, treat those repos as source-of-truth and update docs to match them.
2121

@@ -30,9 +30,10 @@ Before substantial edits, state cross-repo context in your notes:
3030
Common contracts:
3131

3232
- Feed Directory browse data comes from `{instance}/api/v1/configs` on a running `html2rss-web` instance (see OpenAPI in `html2rss-web`).
33+
- Catalog wire rows include `id`, `path`, `directory`, `channel`, `parameters`, plus `source` (`registry` | `local`) and `registry` (registry id for bundle entries). The browse UI ignores `source`/`registry` today — wire parsing stays in `adapters/catalog-api.ts`.
3334
- Instance URL persistence: default public instance, `#!url=` hash deep link from the web app, browser localStorage, and filter state in URL query params (`q`, `topic`, `lang`, `sort`, `page`).
3435
- Deep link from `html2rss-web`: `https://html2rss.github.io/feed-directory/#!url={encodedInstanceUrl}` must keep working.
35-
- Catalog metadata in YAML (`directory.title`, `directory.summary`, `directory.topics`) is authored in `html2rss-configs` only.
36+
- Catalog metadata in YAML (`directory.*`, `registry.id`) is authored in `html2rss-configs` only; instances load verified bundles via registry sync.
3637
- Ruby gem docs should match `html2rss` behavior and CLI output.
3738
- Web application docs should match `html2rss-web` behavior and published OpenAPI.
3839

src/content/docs/ruby-gem/guides/backward-compatibility.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Upgrade notes for configs and integrators targeting gem **0.27.0** (current `mas
1616
3. **Capture output**`html2rss capture` and MCP `capture_config` emit an items selector plus `enhance: true` only (no per-field title/url/description selector soup). Capture stamps `strategy:` when AutoFallback selects a concrete transport.
1717
4. **CLI `--explain`**`html2rss auto --explain` and `html2rss capture --explain` print telemetry JSON on stderr; stdout stays the feed or YAML. Status includes `admission_drops` when Cleanup dropped items.
1818
5. **`Html2rss.auto_feed_result`** — URL-only auto discovery returns `FeedResult` with the same `status` telemetry as config builds.
19-
6. **`request.botasaurus` OpenAPI 2.0** — Keys must match the scrape-API `ScrapeRequest` (unknown keys rejected). Use `scroll: true` (not `scroll_to_bottom`), `window_size: { width:, height: }` (not a two-item array), and `wait_timeout_seconds` in `1..20`. See [Strategy](/ruby-gem/reference/strategy/#botasaurus).
19+
6. **`request.botasaurus` OpenAPI 2.0** — Keys must match the scrape-API `ScrapeRequest` (unknown keys rejected). Use `scroll: true` (not `scroll_to_bottom`), `window_size: { width:, height: }` (not a two-item array), and `wait_timeout_seconds` in `1..30`. See [Strategy](/ruby-gem/reference/strategy/#botasaurus).
2020
7. **Botasaurus wire format** — Client expects OpenAPI 2.0 `ScrapeSuccess` / `ScrapeError` envelopes from the scrape API.
2121
8. **`--limit` default** — Auto discovery default is `25` (`AutoSource::DEFAULT_LIMIT`).
2222

@@ -107,7 +107,7 @@ Update any occurrences of `link`, `pubDate`, or `updated` in your `selectors` bl
107107
When upgrading to **0.27.0** or later:
108108

109109
1. **MCP integrators**: Read tool results from the envelope `payload`; configure `BOTASAURUS_SCRAPER_URL` on the MCP process env.
110-
2. **Align `request.botasaurus` with OpenAPI 2.0**: Use `scroll`, `{ width, height }` for `window_size`, and `wait_timeout_seconds` in `1..20` (see [Strategy](/ruby-gem/reference/strategy/#botasaurus)).
110+
2. **Align `request.botasaurus` with OpenAPI 2.0**: Use `scroll`, `{ width, height }` for `window_size`, and `wait_timeout_seconds` in `1..30` (see [Strategy](/ruby-gem/reference/strategy/#botasaurus)).
111111
3. **Validate configurations**: Run `html2rss validate config.yml`.
112112

113113
When upgrading from releases before **0.26.0**:

src/content/docs/ruby-gem/guides/handling-dynamic-content.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Configure browser actions under `request.botasaurus`:
6363
lang="yaml"
6464
/>
6565

66+
`wait_timeout_seconds` applies to selector wait on the scraper **after** the browser is ready. Slow Chrome or Xvfb boot consumes the scrape **total** wall (`SCRAPE_TIMEOUT_SECONDS`, default **45s**), not this work budget (`SCRAPE_WORK_TIMEOUT_SECONDS`, default **30s**).
67+
6668
### Scroll for Infinite Content
6769

6870
<Code

src/content/docs/ruby-gem/reference/strategy.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Requirements:
4141
- `BOTASAURUS_SCRAPER_URL` set to your Botasaurus scrape API base URL (for example `http://localhost:4010`)
4242
- pin `strategy: botasaurus` when you want to skip Faraday and force this transport directly
4343

44-
html2rss enforces local request policy preflight and timeout budget. When a total request timeout remains, Botasaurus `max_retries` and `wait_timeout_seconds` are clamped so upstream work fits the remaining budget.
44+
html2rss enforces local request policy preflight and a feed-build timeout budget. The Botasaurus scrape API splits **total** scrape time (queue, boot, navigate, wait) from **work** time (navigate, selector wait, and scroll after the browser is ready). Feed YAML `wait_timeout_seconds` is validated against the work cap (`1..30` by default); the gem forwards `request.botasaurus` options to the scrape API without shrinking retries or waits to fit a remaining feed budget. Faraday transport timeout for POST `/scrape` is the lesser of the remaining feed-build budget and `BOTASAURUS_SCRAPE_TIMEOUT_SECONDS` plus a small buffer (default **45s** scrape total, **47s** transport cap).
4545

4646
During the **browser** execution tier, the Botasaurus scrape API always captures JSON XHR/fetch response bodies (`xhr_responses`). html2rss forwards them as `Response#captured_responses` so AutoSource `xhr_articles` can extract articles without extra requests. The HTTP-request tier returns an empty capture list. Caps and filtering live in the scrape API (see its README); article-likeness filtering is a client concern.
4747

@@ -70,7 +70,7 @@ Supported `request.botasaurus` options (unknown keys are rejected):
7070
- `navigation_mode` (`auto`, `get`, `google_get`, `google_get_bypass`, `organic_get`; default `auto`)
7171
- `max_retries` (`0..3`; omit to use the scrape-API default of `2`)
7272
- `wait_for_selector` (string)
73-
- `wait_timeout_seconds` (integer `1..20`; omit to use the scrape-API default of `15`)
73+
- `wait_timeout_seconds` (integer `1..30`; omit to use the scrape-API default of `15`; counts against post-boot **work** budget on the scraper, not browser boot)
7474
- `scroll` (boolean)
7575
- `block_images` (boolean)
7676
- `block_images_and_css` (boolean)

src/content/docs/web-application/reference/env-variables.mdx

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,42 @@ description: "Configuration reference for html2rss-web environment variables."
55

66
## Supported ENV variables
77

8-
| Name | Description |
9-
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
10-
| `HTML2RSS_SECRET_KEY` | required in production; development/test gets a temporary default |
11-
| `HTML2RSS_ACCESS_TOKEN` | token for the main feed-creation flow; paste the same value into the web UI when prompted |
12-
| `HEALTH_CHECK_TOKEN` | optional bearer token for authenticated `GET /api/v1/health`; not part of the first-run quickstart; use `/api/v1/health/ready` for standard readiness checks |
13-
| `BUILD_TAG` | release metadata used in logs; published Docker images set this to the release version |
14-
| `GIT_SHA` | deployed commit metadata used in logs; published Docker images set this to the released commit |
15-
| `SENTRY_DSN` | optional; enables Sentry errors/logs when set |
16-
| `BOTASAURUS_SCRAPER_URL` | optional; Botasaurus scrape API base URL (e.g. `http://botasaurus:4010`) for browser rendering and anti-bot bypass |
17-
| `AUTO_SOURCE_ENABLED` | `true` by default in development/test, `false` otherwise |
18-
| `ASYNC_FEED_REFRESH_ENABLED` | optional boolean; default `false` |
19-
| `ASYNC_FEED_REFRESH_STALE_FACTOR` | optional integer `>= 1`; default `3` |
20-
| `FEEDS_CACHE_MAX_SIZE` | optional integer `>= 1`; default `500`; maximum number of feeds to cache in memory before eviction |
21-
| `RATE_LIMIT_ENABLED` | optional boolean; default `true` (except `false` in `test`); enables or disables client IP rate limiting |
22-
| `RATE_LIMIT_MAX_REQUESTS` | optional integer `>= 1`; default `60`; maximum requests allowed per IP per window |
23-
| `RATE_LIMIT_WINDOW_SECONDS` | optional integer `>= 1`; default `60`; rate limiter sliding window duration in seconds |
24-
| `RETRY_AFTER_TIMEOUT_SECONDS` | optional integer `>= 1`; default `300`; Retry-After header backoff duration returned on timeouts |
25-
| `PORT` | app listen port; compose uses `4000` |
26-
| `RACK_ENV` | Rack environment; quickstart uses `development`, production examples use `production` |
27-
| `RACK_TIMEOUT_SERVICE_TIMEOUT` | optional; total web-request timeout in seconds; default `30` |
28-
| `HTML2RSS_TOTAL_TIMEOUT_SECONDS` | optional; internal gem request timeout in seconds; default `25` |
8+
| Name | Description |
9+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
10+
| `HTML2RSS_SECRET_KEY` | required in production; development/test gets a temporary default |
11+
| `HTML2RSS_ACCESS_TOKEN` | token for the main feed-creation flow; paste the same value into the web UI when prompted |
12+
| `HEALTH_CHECK_TOKEN` | optional bearer token for authenticated `GET /api/v1/health`; not part of the first-run quickstart; use `/api/v1/health/ready` for standard readiness checks |
13+
| `BUILD_TAG` | release metadata used in logs; published Docker images set this to the release version |
14+
| `GIT_SHA` | deployed commit metadata used in logs; published Docker images set this to the released commit |
15+
| `SENTRY_DSN` | optional; enables Sentry errors/logs when set |
16+
| `BOTASAURUS_SCRAPER_URL` | optional; Botasaurus scrape API base URL (e.g. `http://botasaurus:4010`) for browser rendering and anti-bot bypass |
17+
| `AUTO_SOURCE_ENABLED` | `true` by default in development/test, `false` otherwise |
18+
| `ASYNC_FEED_REFRESH_ENABLED` | optional boolean; default `false` |
19+
| `ASYNC_FEED_REFRESH_STALE_FACTOR` | optional integer `>= 1`; default `3` |
20+
| `FEEDS_CACHE_MAX_SIZE` | optional integer `>= 1`; default `500`; maximum number of feeds to cache in memory before eviction |
21+
| `RATE_LIMIT_ENABLED` | optional boolean; default `true` (except `false` in `test`); enables or disables client IP rate limiting |
22+
| `RATE_LIMIT_MAX_REQUESTS` | optional integer `>= 1`; default `60`; maximum requests allowed per IP per window |
23+
| `RATE_LIMIT_WINDOW_SECONDS` | optional integer `>= 1`; default `60`; rate limiter sliding window duration in seconds |
24+
| `RETRY_AFTER_TIMEOUT_SECONDS` | optional integer `>= 1`; default `300`; Retry-After header backoff duration returned on timeouts |
25+
| `PORT` | app listen port; compose uses `4000` |
26+
| `RACK_ENV` | Rack environment; quickstart uses `development`, production examples use `production` |
27+
| `BOTASAURUS_SCRAPE_TIMEOUT_SECONDS` | optional; Faraday client cap for POST `/scrape` in seconds; default `45`; mirrors `SCRAPE_TIMEOUT_SECONDS` on botasaurus-scrape-api |
28+
| `BOTASAURUS_SCRAPE_WORK_TIMEOUT_SECONDS` | optional; maximum `wait_timeout_seconds` in feed YAML; default `30`; mirrors `SCRAPE_WORK_TIMEOUT_SECONDS` on botasaurus-scrape-api |
29+
| `SCRAPE_WORK_TIMEOUT_SECONDS` | optional on botasaurus-scrape-api; post-boot navigate, selector wait, and scroll budget in seconds; default `30`; set on the scraper service in compose |
30+
| `RACK_TIMEOUT_SERVICE_TIMEOUT` | optional; total web-request timeout in seconds; default `55` |
31+
| `HTML2RSS_TOTAL_TIMEOUT_SECONDS` | optional; internal gem feed-build timeout in seconds; default `50` |
2932

3033
## Timeout Chain
3134

32-
When fetching feeds from complex websites, multiple components work together. To prevent premature request termination, ensure your timeouts are configured in a "chain" where the outer layers wait longer than the inner layers.
35+
When fetching feeds from complex websites, multiple components work together. Configure timeouts so outer layers wait longer than inner layers and the scraper exhausts its budget before the web tier aborts the request.
3336

34-
For an overall 30-second budget:
37+
Default compose aligns **scrape total (45) ≤ feed build (50) ≤ Rack (55)**. The **work** budget (**30s**) applies on botasaurus-scrape-api only after the browser is ready; browser boot and queue time count against the **total** scrape wall (**45s**), not against `wait_timeout_seconds`.
3538

36-
1. **Botasaurus Scraper (`SCRAPE_TIMEOUT_SECONDS`)**: Set to **20s**. This is the time allowed for the browser to fetch and render the page.
37-
2. **html2rss Gem (`HTML2RSS_TOTAL_TIMEOUT_SECONDS`)**: Set to **25s**. This allows the gem enough time to wait for Botasaurus and process the results.
38-
3. **html2rss-web (`RACK_TIMEOUT_SERVICE_TIMEOUT`)**: Set to **30s**. This is the total time the web server will wait before killing the request.
39+
1. **botasaurus-scrape-api (`SCRAPE_TIMEOUT_SECONDS`)**: **45s** handler wall (queue, boot, navigate, wait).
40+
2. **botasaurus-scrape-api (`SCRAPE_WORK_TIMEOUT_SECONDS`)**: **30s** post-boot navigate, `wait_for_selector`, and scroll.
41+
3. **html2rss client (`BOTASAURUS_SCRAPE_TIMEOUT_SECONDS`)**: **45s** Faraday cap for POST `/scrape` (mirrors scrape total).
42+
4. **Feed YAML (`wait_timeout_seconds`)**: **1..30**, capped by `BOTASAURUS_SCRAPE_WORK_TIMEOUT_SECONDS` (default **30**).
43+
5. **html2rss-web gem budget (`HTML2RSS_TOTAL_TIMEOUT_SECONDS`)**: **50s** for scrape plus extraction.
44+
6. **html2rss-web (`RACK_TIMEOUT_SERVICE_TIMEOUT`)**: **55s** Rack outer wall.
3945

40-
Older environment-variable examples from previous docs revisions are obsolete. Use only the supported table above and the `Environment & Runtime Flags` table in [`docs/README.md`](https://github.com/html2rss/html2rss-web/blob/main/docs/README.md).
46+
Set matching `SCRAPE_*` on botasaurus-scrape-api and `BOTASAURUS_SCRAPE_*` on html2rss-web when you override defaults. See the `Environment & Runtime Flags` table in [`docs/README.md`](https://github.com/html2rss/html2rss-web/blob/main/docs/README.md) for compose examples.

src/content/docs/web-application/reference/monitoring.mdx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { Code } from "@astrojs/starlight/components";
77

88
## Health Endpoints
99

10-
| Endpoint | Auth | Purpose |
11-
| --- | --- | --- |
12-
| `GET /api/v1/health/live` | none | Process liveness |
13-
| `GET /api/v1/health/ready` | none | Config readiness |
14-
| `GET /api/v1/health` | bearer | Operator and uptime checks |
10+
| Endpoint | Auth | Purpose |
11+
| -------------------------- | ------ | -------------------------- |
12+
| `GET /api/v1/health/live` | none | Process liveness |
13+
| `GET /api/v1/health/ready` | none | Config readiness |
14+
| `GET /api/v1/health` | bearer | Operator and uptime checks |
1515

1616
Set `HEALTH_CHECK_TOKEN`, then call the authenticated endpoint:
1717

@@ -31,9 +31,9 @@ Set `SENTRY_DSN` to enable Issue capture in html2rss-web (Rack middleware and op
3131

3232
Use separate Sentry projects for html2rss-web and botasaurus-scrape-api. Never share a DSN.
3333

34-
| Env var | Service |
35-
| --- | --- |
36-
| `SENTRY_DSN` | html2rss-web |
34+
| Env var | Service |
35+
| ----------------------- | --------------------- |
36+
| `SENTRY_DSN` | html2rss-web |
3737
| `BOTASAURUS_SENTRY_DSN` | botasaurus-scrape-api |
3838

3939
In `docker-compose.yml`, the botasaurus service maps `BOTASAURUS_SENTRY_DSN` into its own `SENTRY_DSN`, requires it at startup, and does not fall back to the web project's DSN.

0 commit comments

Comments
 (0)