Reference for limits we can verify from repo code and checked-in config.
This document intentionally focuses on:
- limits enforced in code
- budgets encoded in config
- runtime assumptions the scheduler is explicitly designed around
It intentionally does not treat vendor pricing-plan quotas as source of truth. Cloudflare, CoinGecko, Alchemy, Etherscan, Anthropic, X, and similar providers can change those independently of this repo. Re-check official vendor docs or your live account dashboard before making spend-sensitive or capacity-sensitive changes.
Primary Sources
worker/wrangler.tomlshared/lib/cron-jobs.tsworker/src/lib/rate-limit.tsshared/lib/ops-limits.tsworker/src/lib/api-keys.tsworker/src/lib/circuit-breaker.tsworker/src/handlers/http/gates.tsworker/src/cron/sync-blacklist.tsworker/src/cron/sync-mint-burn.tsworker/src/cron/sync-live-reserves.tsworker/src/cron/sync-live-reserves-config.tsworker/src/lib/address-price-providers/index.tsworker/src/cron/dex-discovery/orchestrator.tsworker/src/cron/sync-stablecoins/enrich-prices.tsworker/src/cron/sync-fx-rates.tsworker/src/cron/publish-report-card-cache.tsworker/src/cron/daily-digest.tsworker/src/cron/sync-yield-data.tsworker/src/cron/sync-yield-supplemental.ts
Worker Runtime
| Constraint | Current repo value | Source | Notes |
|---|---|---|---|
| Worker CPU budget per invocation | 30000 ms | worker/wrangler.toml | Hard repo-configured CPU cap via [limits].cpu_ms |
| Cron expressions / trigger slots | 19 | worker/wrangler.toml, shared/lib/cron-jobs.ts, shared/lib/scheduled-runner-registry.ts | Public status tooling groups around these trigger slots; the shared runner registry is the dispatch authority checked by npm run check:cron-sync; check:cron-connections models chained jobs and budget-only scheduled surfaces with connectionGroup metadata. 2026-04-17: added daily0300Utc slot for prune-status-probe-runs (90d retention). 2026-04-19: added dewsPsiOffset (26,56 * * * *) so DB-only DEWS/PSI publication is not coupled to DEX-liquidity CPU usage. 2026-04-23: added halfHourlyChartsOffset (16,46 * * * *) so sync-stablecoin-charts no longer shares an invocation budget with sync-dex-liquidity. 2026-05-11: added daily0810Utc (10 8 * * *) so discovery-scan no longer shares the 08:05 lane. |
| Status-tracked cron jobs | 40 | shared/lib/cron-jobs.ts | These are the jobs expected by /api/status. Latest additions include cron-staleness-watchdog (quarter-hour freshness alerting), project-tape (DB-only dewsPsiOffset projection from existing source tables into tape_events), telegram-pulse-snapshot and telegram-disambiguation-cleanup (DB-only five-minute sidecars), telegram-inactive-cleanup and telegram-retention-cleanup (daily0300Utc), and yield-coverage-audit (monthly). |
| Runtime jobs actually scheduled | 40 status-tracked jobs, plus 2 budget-only scheduled surfaces | shared/lib/cron-jobs.ts, shared/lib/scheduled-runner-registry.ts | Runtime scheduling matches the shared status metadata set for /api/status. The additional budget-only surfaces are Telegram registration reconciliation on the five-minute Telegram slot and the */5 * * * * digest-trigger poll, which executes pending manual requests under the existing daily-digest lease. |
| API key default limiter | 120 requests / 60 seconds per key | shared/lib/ops-limits.ts, worker/src/lib/api-keys.ts | Non-exempt /api/* requests require a valid X-API-Key; the no-key public exceptions are health, OG images, feedback, self-serve key request/verification, the Telegram webhook, and Telegram Mini App session/mutation endpoints authenticated by signed initData. The D1-backed api_key_rate_limit table enforces per-key quotas in the normal path, and per-key overrides live in api_keys.rate_limit_per_minute. Protected cacheable GET edge-cache hits can use a bounded isolate-local fast path only for recently verified non-self-serve keys; cold, unknown, self-serve, stale-cache, cache-miss, cache-bypass, and non-GET requests stay on the D1-backed path or fail closed. After repeated D1 limiter failures, protected cacheable GET routes open a 60-second isolate-local fallback circuit capped at the self-serve quota (30/min); cold or unknown keys still fail closed with 503 and Retry-After: 60. Last-used metadata writes are best-effort. |
| Self-serve API key policy | 30 requests / 60 seconds, 60 days expiry | shared/lib/ops-limits.ts, worker/src/api/api-key-requests.ts | Email-verified key issuance through /api/; one active/pending self-serve key claim per normalized email. |
| Feedback limiter | 3 submissions / 10 minutes per salted IP hash | worker/src/api/feedback.ts, worker/src/lib/rate-limit.ts | Separate from the per-key limiter |
| Self-serve request limiter | 5/hour per salted IP hash, 3/day per private email hash | shared/lib/ops-limits.ts, worker/src/api/api-key-requests.ts | Protects POST /api/api-key-requests; dependency failures fail closed with 503 and Retry-After: 60. |
| Self-serve verification limiter | 20/10 minutes per salted IP hash, 5/10 minutes per token hash | shared/lib/ops-limits.ts, worker/src/api/api-key-requests.ts | Protects POST /api/api-key-requests/verify; issuance is also capped to one creation per salted IP hash per 24 hours. |
| Request attribution telemetry retention | 35 days | worker/src/lib/request-source-attribution.ts, functions/lib/request-attribution.ts | Total site-vs-external demand, worker-lane load, and per-key public-API load buckets in api_request_consumer_stats / site_data_request_stats / api_key_request_stats are pruned opportunistically. Worker route/source and Pages site-data counters use short isolate-local batching before D1 upsert. Set REQUEST_SOURCE_ATTRIBUTION_DISABLED=true on Worker and/or Pages to pause low-value route/source writes without disabling API-key auth, D1-backed rate limiting, or per-key public API load telemetry. Set API_KEY_REQUEST_ATTRIBUTION_DISABLED=true on the Worker only for keyed public-API spikes where per-key observability writes also need a D1 pressure relief valve; auth, rate limiting, and last-used metadata stay enabled. |
Connection-budget operating assumption
The scheduler is deliberately structured around the repo's six-connection-per-trigger operating constraint:
- heavy lanes get isolated trigger slots (
sync-blacklist,sync-mint-burn,sync-mint-burn-extended,sync-dex-discovery,sync-dex-liquidity) - shared slots bundle only related work
- the quarter-hourly handler sequences jobs instead of fanning them out blindly
npm run check:cron-connectionsfails any trigger at or above6/6and reports5/6triggers as headroom full- the connection check includes budget-only scheduled surfaces that do not create separate
cron_runsrows, currentlytelegram-registration-reconciliationanddigest-trigger-poll
Treat any new fetch-heavy work added to an existing trigger slot as competing for the same trigger-wide outbound connection budget. A trigger at 5/6 must be treated as full for new fetch-heavy work unless the change also reduces existing peak usage or moves work to a different slot.
Current state: no job-bearing trigger is intentionally operated at 5/6. The former full slots were given headroom by reducing Telegram send batches to 4, running supplemental yield source families serially, and moving discovery-scan from the 08:05 daily lane to its own 08:10 trigger.
For sync-stablecoins, failed upstream responses must be consumed or canceled before later passes start. Leaving non-OK bodies unread can strand the same trigger-local connection slots and starve the late fallback phase (CoinMarketCap -> Jupiter -> DexScreener).
The same rule applies to Worker-side integration clients. Telegram delivery, X posting, and GitHub feedback submission should always consume or cancel response bodies before returning so one idle stream does not pin a scarce connection slot.
Cron Budgeting
| Area | Current repo budget | Source | Notes |
|---|---|---|---|
| DEX discovery overall deadline | 12 minutes | worker/src/cron/dex-discovery/orchestrator.ts | Shared deadline for the discovery pass before persistence/cleanup tail work |
| DEX discovery per-coin budget | 25 seconds | worker/src/cron/dex-discovery/orchestrator.ts | Prevents one slow coin from consuming the whole staging lane |
| Live reserve sync outer deadline | 12 minutes | worker/src/lib/cron-lease.ts | Explicit wrapper budget for the serialized reserve loop before the rest of the 4-hourly slot |
| Live reserve sync internal run budget | 10 minutes | worker/src/cron/sync-live-reserves-config.ts | Default cursoring budget; if the remaining budget drops below one adapter attempt, the untouched tail is marked deferred and resumed from cursor on the next run, leaving tail room for D1 cleanup and cron logging |
| Live reserve adapter I/O peak | 2 outbound operations per adapter attempt | worker/src/cron/reserve-adapters/concurrency.ts, shared/lib/cron-jobs.ts | Coin loop is serialized, but individual adapters can fan out internally; shared fetch/RPC helpers enforce the per-attempt limiter |
| Yield publication overall deadline | 10 minutes | worker/src/lib/cron-lease.ts | Dedicated hourly sync-yield-data timeout after moving off the half-hourly lane |
| Yield supplemental overall deadline | 12 minutes | worker/src/lib/cron-lease.ts | Dedicated 4-hour sync-yield-supplemental timeout for optional protocol families |
| Telegram dispatch overall deadline | 14 minutes | worker/src/lib/cron-lease.ts, worker/src/handlers/scheduled/context.ts | Dedicated five-minute Telegram lane timeout plus 30-second lease heartbeat; sized for 5,000-watcher normal SLO modeling under the 15-minute scheduled-event ceiling |
| Telegram safety source stale threshold | 2 producer intervals (30 minutes) | worker/src/lib/alert-safety-source-cache.ts, shared/lib/cron-jobs.ts | Safety alerts remain suppressed until publish-report-card-cache republishes a fresh generation-valid source snapshot |
| Telegram safety source serialized cache budget | <= 1.5 MB for 401 report-card rows in tests | worker/src/lib/__tests__/alert-safety-source-cache.test.ts | The optional safety explain payload is kept compact so cache["alert:safety-source-cache"] remains a single D1 cache row with operational headroom |
| Telegram registration reconciliation peak | 1 outbound Bot API call at a time | worker/src/lib/telegram-webhook-registration.ts, shared/lib/cron-jobs.ts | Runs serially before dispatch-telegram-alerts when 15-minute cache markers expire; modeled as the budget-only telegram-registration-reconciliation entry in the same five-minute Telegram connection group. |
| Telegram load simulation targets | 500, 1,000, 5,000, 10,000 active watchers | scripts/ci/check-telegram-load.ts | npm run check:telegram-load estimates drain time and D1 operations for single depeg, market-wide burst, DEWS+safety burst, admin broadcast, and Telegram 429 storm scenarios using the 3,600/run sender budget, 900/run pending drain, Bot API broadcast pacing, p95 send latency, and D1 write cost. The 5,000-watcher target is the next milestone; 10,000 is exploratory headroom. |
| Telegram group admin membership cache | 5 minutes for cached denial/admin-list copy; mutating auth revalidates per webhook | worker/src/lib/telegram-chat-member.ts, worker/src/api/telegram-webhook-auth.ts | /subscribe, /unsubscribe, /set in group/supergroup chats use a fresh getChatMember lookup for mutation authorization and fail closed if Telegram cannot confirm admin status. Soft-launch warnings and denial copy can still use the cached getChatAdministrators list (telegram:chat-admins:<chat_id>). These calls happen on webhook ingress, not inside the dispatch cron lane. |
| Live reserve history retention | 90 days | worker/src/lib/live-reserves-store-write.ts | reserve_composition_history and reserve_sync_attempt_history are pruned during reserve-sync cleanup |
| Redemption route-status producer | D1-free/static plus live-reserve metadata | worker/src/lib/redemption-backstop-route-status.ts, worker/src/cron/sync-redemption-backstops.ts | v4 route status remains four-hour snapshot data. sync-redemption-backstops does not add outbound route-status feed fetches; route availability comes from existing live-reserve adapter metadata, reviewed static policy, and market-implied severe-depeg overlays. |
| Blacklist sync runtime budget | 7 minutes | worker/src/cron/sync-blacklist.ts | Guardrail before the trigger wrapper times out |
| Blacklist sync subrequest budget | 900 | worker/src/cron/sync-blacklist.ts, worker/src/lib/evm-logs.ts | Covers explorer/RPC calls for a single run |
| Blacklist amount-recovery batch | 100 rows / run | worker/src/cron/blacklist/amount-recovery.ts | Conservative per-run EVM recovery cap on the 6-hour blacklist lane; statements are chunked through the shared D1 batch helper and stay under the sync subrequest budget |
| Mint/burn global request budget | 200 | worker/src/cron/sync-mint-burn.ts | Shared per-run request ceiling |
| Mint/burn per-config budget (critical) | 60 | worker/src/cron/sync-mint-burn.ts | Prevents one hot config from consuming the full run |
| Mint/burn per-config budget (extended) | 25 | worker/src/cron/sync-mint-burn.ts | Lower ceiling for long-tail backlog drain |
| Mint/burn max scan range | 50,000 blocks | worker/src/cron/sync-mint-burn.ts | Keeps per-request log scans bounded |
Mint/burn SQL IN chunk size | 90 ids | worker/src/cron/sync-mint-burn.ts | Current safeguard for large batched SQL |
| Mint/burn event insert batch size | 50 statements | worker/src/lib/mint-burn-pipeline/persistence.ts | Each insert binds 18 values; chunked to stay below D1 batch bind ceilings |
D1 overload retry posture
Cron persistence helpers retry transient D1 queue pressure through runWithOverloadRetry() in worker/src/lib/cron-lease.ts. Retried errors include D1 DB is overloaded, Requests queued for too long, D1 storage-operation reset timeouts (D1 DB storage operation exceeded timeout ...), and Cloudflare D1 internal-reference errors (D1_ERROR: internal error; reference = ...). Live reserve, redemption-backstop, cache-sentinel, and DEWS persistence paths should route bursty run-manifest writes, cleanup, prune, and chunked batch work through this helper or batchExecute() so one transient D1 queue spike does not fail a whole scheduled run.
Upstream Fetch Budgets
| Path | Current repo throttle / budget | Source | Notes |
|---|---|---|---|
| CoinGecko onchain discovery | 250 ms between requests | worker/src/lib/rate-limit.ts | Used by discovery crawlers |
| CoinGecko onchain crawl budget | 5 minutes | worker/src/lib/rate-limit.ts | Per-source crawl budget, not full-run deadline |
| CoinGecko backfill throttle | 200 ms between requests | worker/src/lib/rate-limit.ts | Used by CoinGecko backfill/admin flows |
| GeckoTerminal crawl throttle | 2000 ms between requests | worker/src/lib/rate-limit.ts | Conservative crawl pacing |
| GeckoTerminal crawl budget | 3 minutes | worker/src/lib/rate-limit.ts | Per-source crawl budget |
| GeckoTerminal probe budget | 3 minutes per sync-stablecoins run | worker/src/lib/constants.ts | Prevents the serialized soft-source cross-check from consuming the full 8-minute stablecoin sync timeout |
| DexScreener discovery fallback budget | 2 minutes shared fallback window | worker/src/lib/rate-limit.ts | Shared with other late-stage discovery fallbacks |
| Jupiter price fallback | 50 ids/request, 5 s timeout/request, 0 retries; up to 25 low-depth primary augmentation targets/run | worker/src/cron/sync-stablecoins/enrich-prices-jupiter-pass.ts | Solana-only enrichment pass between CMC and DexScreener; can append agreeing Jupiter evidence to low-depth primary prices |
| DexScreener price-enrichment pass | 10 total requests, 5 s timeout/request, 45 s total budget, 0 retries | worker/src/cron/sync-stablecoins/enrich-prices-dexscreener-pass.ts | Best-effort final fallback for missing prices through exact token-address lookups; symbol search is retired |
| Address-price augmentation group | 90 s total budget, 5 s timeout/request, 0 retries | worker/src/lib/address-price-providers/index.ts | Runs during primary pricing for assets with missing prices, low-confidence prices, or previous source depth below 3 |
| DexScreener address augmentation | opt-in; 1 request/run, 30 addresses/request | worker/src/lib/address-price-providers/dexscreener.ts | Exact /tokens/v1/{chain}/{addresses} lane; no symbol search; stops on hard upstream refusal |
| DexPaprika address augmentation | 60 token-detail requests/run | worker/src/lib/address-price-providers/index.ts | Public exact token-detail lookup |
| CoinGecko Onchain address augmentation | 5 requests/run, 30 addresses/request | worker/src/lib/address-price-providers/index.ts | Keyed exact onchain token lookup, separate from the serialized GeckoTerminal pool probe |
| Alchemy Prices address augmentation | 20 requests/run, 25 addresses/request | worker/src/lib/address-price-providers/index.ts | Optional keyed lookup, grouped by Alchemy network id |
| Moralis address augmentation | 3 requests/run, 100 addresses/request | worker/src/lib/address-price-providers/moralis.ts | Optional keyed EVM batch lookup; capped below the free-plan 40k CU/day envelope for the 15-minute sync cadence |
| Birdeye address augmentation | 10 Solana requests/run, 1000 ms between requests | worker/src/lib/address-price-providers/index.ts | Optional keyed Solana-only targeted gap lookup |
| CoinMarketCap fallback | 1 call / hour, 10 s timeout, 0 retries | worker/src/cron/sync-stablecoins/enrich-prices-cmc-pass.ts | Rate-limited through cache key cmc_last_fetch |
| Direct DEX API fetch phase | 2 protocol fetches in parallel, 15 s request timeout, deterministic page caps (50 default) | worker/src/cron/dex-liquidity/direct-api-policy.ts, worker/src/cron/dex-liquidity/direct-api-paginated.ts, protocol fetchers | Runs inside the existing sync-dex-liquidity trigger; page-cap errors include resume markers for large or drifting upstream responses |
| Generic circuit breaker | opens after 3 consecutive failures, probes every 30 minutes | worker/src/lib/circuit-breaker.ts | Used to stop hammering degraded upstreams |
What this means operationally
sync-dex-liquidityno longer owns discovery. It consumes staged output written bysync-dex-discovery.sync-dex-discoveryis deliberately best-effort. Short per-source request timeouts and the 12-minute shared budget are there to force a partialdegradedresult before the platform can hard-kill the invocation. Lower-priority tier-2/tier-3 candidates are deterministically sharded across their cadence windows so one modulo run does not inherit the entire tier queue at once.- Missing-price fallback is intentionally time-bounded so a bad upstream day cannot consume the whole
sync-stablecoinsslot. - Any new provider added to discovery or price enrichment should come with both a throttle and a hard stop budget.
Request Timeouts Worth Preserving
| Area | Current timeout | Source |
|---|---|---|
| CoinMarketCap price fallback | 10_000 ms | worker/src/cron/sync-stablecoins/enrich-prices-cmc-pass.ts |
| Jupiter price fallback | 5_000 ms | worker/src/cron/sync-stablecoins/enrich-prices-jupiter-pass.ts |
| DexScreener price fallback requests | up to 5_000 ms per request | worker/src/cron/sync-stablecoins/enrich-prices-dexscreener-pass.ts |
| Direct DEX API requests | 15_000 ms per request | worker/src/cron/dex-liquidity/direct-api-policy.ts |
| Ops admin proxy reads | 20_000 ms for /api/status and /api/status-history; 45_000 ms for /api/audit-depeg-history | functions/api/admin/[[path]].ts |
| Live reserve adapter attempt | 20_000 ms | worker/src/cron/sync-live-reserves-config.ts |
| Live reserve D1 finalize timeout | 30_000 ms | worker/src/cron/sync-live-reserves-config.ts |
| Blacklist explorer / RPC reads | 15_000 ms | worker/src/lib/fetch-retry.ts (default timeout) |
| Daily digest LLM call (outer) | 12 * 60_000 ms | worker/src/lib/constants.ts |
| Daily digest per-attempt fetch | 11 * 60_000 ms | worker/src/cron/digest/platform.ts (DIGEST_FETCH_PER_ATTEMPT_TIMEOUT_MS) |
Live reserve timeout values are resolved through LiveReserveSyncBudgetConfig. Production uses the checked-in defaults above; tests and operational wrappers can inject smaller or larger positive finite values to validate deferred-tail and D1-finalize behavior without changing cron code.
/api/audit-depeg-history is also hard-capped at 25 items per request (limit default and max) so the CoinGecko-backed admin audit stays page-sized over the Pages proxy.
Anthropic / Digest Runtime
Current digest generation constraints that are actually encoded in repo code:
- model:
claude-opus-4-7 - thinking: adaptive (
thinking.type = "adaptive") - reasoning effort: xhigh (
output_config.effort = "xhigh") — dropped frommaxon 2026-04-18 after runaway-thinking exhaustedmax_tokenstwice (stopReason=max_tokenswith only asignature_deltaat both 16k and 32k).maxhas no constraint on thinking depth on Opus 4.7;xhighis Anthropic's recommended level for complex editorial work and Claude Code's own default. - Anthropic outer timeout:
12 * 60_000 ms(12 min), bound byAbortSignal.timeout(ANTHROPIC_TIMEOUT_MS)inplatform.ts - per-attempt fetch timeout:
11 * 60_000 ms(11 min), local torequestDigestCopy; safety net so a single stalled attempt cannot consume the outer budget - retry depth for the digest Anthropic call:
2(max 3 attempts); the outerAbortSignalcaps total wall time regardless - corrective retry skip: if first-pass elapsed
>= 50%of the outer budget (6 min), the in-process retry after quality failures is skipped; the parse is accepted withqualityIssuesflaggeddegraded - daily cron lease (wrapper timeout):
14 * 60_000 ms(14 min), leaves ~2 min under Cloudflare's 15-min scheduled-event ceiling for D1 persistence, Twitter/Telegram delivery, and cron_runs logging. - daily-digest heartbeat override:
heartbeatSec = 30,maxRenewFailures = 3(seeworker/src/handlers/scheduled/context.ts— default policy unchanged for other jobs) - weekly cron lease:
12 * 60_000 ms(12 min) - max_tokens:
64000daily,64000weekly — Anthropic's documented floor for Opus 4.7 atxhigh/maxeffort with adaptive thinking. Earlier settings of 16k → 32k ateffort: "max"both hitstop_reason=max_tokenswith no text emitted; the root-cause fix on 2026-04-18 lowered effort toxhighand raised the ceiling to 64k in one change. - cadence: daily scheduled run plus deferred manual admin trigger (see "Manual trigger runtime model" below)
- cost envelope (approximate, assuming single-attempt runs): Opus 4.7 input ~$5/Mtok, output ~$25/Mtok. Daily worst-case at 64k tokens ≈ $4.80; weekly worst-case at 64k ≈ $4.80. Annualized ≈ $2000 at cap. Actual usage is typically much lower since most runs don't approach the cap; the ceiling exists to survive adaptive-thinking-heavy runs. The current worker does not persist token-usage telemetry in
digest:last-trigger-resultorcron_runs; use provider-side Anthropic usage logs for exact spend.
Manual trigger runtime model
POST /api/trigger-digest does not execute the digest synchronously. It writes a digest:force-run-request flag into the cache D1 table and returns 202. A dedicated */5 * * * * cron slot (digestTriggerPoll) reads the flag on its next tick and runs the digest under scheduled-event wall-clock (15 min). Outcome is persisted to digest:last-trigger-result for D1 inspection and future ops-UI surfacing; the current admin panel still shows only the enqueue result from the browser session.
This two-step model exists because the repo treats long HTTP-triggered ctx.waitUntil() digest execution as unsafe on Cloudflare Workers. The external platform assumption is that HTTP request tail work can be canceled after a short post-response window, while scheduled events get the full scheduled-event wall-clock; an Opus 4.7 digest run takes 5–10 min, so enqueue + scheduled polling is the repo-verified safe path.
Source: worker/src/api/admin-actions.ts (enqueue-only HTTP handler), worker/src/handlers/scheduled/digest-trigger-poll.ts (polling consumer).
Source: worker/src/lib/constants.ts (Anthropic timeout/retries), worker/src/lib/cron-lease.ts (CRON_TIMEOUT_MS per-job lease budget), worker/src/cron/digest/platform.ts (model/thinking/effort, per-attempt timeout, corrective-retry skip), worker/src/cron/daily-digest.ts and worker/src/cron/weekly-recap.ts (max_tokens), worker/src/handlers/scheduled/context.ts (PER_JOB_LEASE_OPTIONS heartbeat override)
This doc deliberately does not restate Anthropic account-tier RPM / token-plan numbers because those are not repo-enforced.
Design Guidance
Before adding a worker feature that touches external services:
- Pick the trigger slot first. Shared slots are a capacity decision, not just a schedule decision.
- Add explicit throttle constants and an overall time budget before writing the fetch loop.
- Prefer chunked / batched writes and bounded SQL fan-out.
- Add or reuse a circuit breaker when the feature depends on a flaky upstream.
- Run
npm run check:cron-connectionsand document the trigger-slot impact for any new outbound I/O. - Update this doc only with limits the repo actually enforces or depends on architecturally.
If you need current provider-plan quotas, verify them outside the repo before relying on them.