feat(daemon): per-site navigation pacing and security-block circuit breaker - #2464
Open
yisiliu wants to merge 1 commit into
Open
feat(daemon): per-site navigation pacing and security-block circuit breaker#2464yisiliu wants to merge 1 commit into
yisiliu wants to merge 1 commit into
Conversation
…reaker Sites with velocity-based risk control (xiaohongshu, weibo) soft-block accounts that navigate faster than a human. Adapters already jitter their own settle waits, but nothing spaced navigations across separate CLI invocations - a batch caller fires them back-to-back, which is exactly the pattern the xiaohongshu pitfalls doc warns escalates toward account restrictions. The daemon is the one local process that sees every CLI client (same argument as session-lease.ts), so it now enforces two rules centrally, via a pure injectable-clock module (src/site-pacing.ts): - Adapter `navigate` dispatches for a paced (contextId, site) are spaced by a jittered per-site interval (xiaohongshu 1.5-3s, weibo 1-2s). Concurrent CLI processes serialize into consecutive slots without locks; navigations arriving after the interval pass with zero delay. Only `navigate` is paced - evaluates against a warm tab load no pages. - Two SECURITY_BLOCK outcomes within 10 minutes open a randomized 5-10 min cooldown during which paced navigations fail fast with a machine-readable `security_cooldown` error carrying retryAfterMs, instead of hammering a hot risk state. Any successful command resets the counter. execution.ts reports outcomes best-effort on command settle (SECURITY_BLOCK errors and successes only - unrelated failures say nothing about the site's risk state). Pacing events surface in the daemon's /logs ring buffer (its own stdio is discarded). OPENCLI_PACING=off at daemon startup disables the layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
xiaohongshu's own pitfalls doc (
sitemaps/xiaohongshu/pitfalls.md) documents that risk control triggers on velocity and asks callers to keep 1–2s between requests and to back off 60s after a安全限制— but nothing enforces any of it. Adapters jitter their own settle waits, yet navigations from separate CLI invocations land back-to-back: a batch caller turns one hiccup into a burst of rapid page loads, which is the documented escalation path toward account restrictions (#842, #677). The soft-block retry helper explicitly notes cross-invocation throttling as a follow-up (risk-control.js: "does NOT cap request velocity across separate CLI invocations (that needs session-level throttling, tracked as a follow-up)") — this PR is that follow-up.Design
Same architecture argument as
session-lease.ts: the daemon is the one local process that sees every CLI client, so it arbitrates centrally, with the decision logic in a pure, injectable-clock module (src/site-pacing.ts) and only thin wiring in the daemon handler.Navigation slots — adapter
navigatedispatches for a paced(contextId, site)are spaced by a jittered per-site interval (xiaohongshu1.5–3s,weibo1–2s; all other sites untouched). Slot assignment ismax(now, lastSlot + interval), so concurrent CLI processes serialize into consecutive slots without locks, and a navigation arriving after the interval passes with zero delay. Onlynavigateis paced — evaluates against a warm tab load no pages, and pacing them would slow everything for nothing.Circuit breaker —
execution.tsreports command outcomes best-effort on settle:SECURITY_BLOCKfailures and successes only (unrelated errors say nothing about the site's risk state, unknown-outcome endings report nothing). Two blocks inside a 10-minute window open a randomized 5–10 min cooldown; while open, paced navigations fail fast with a machine-readablesecurity_cooldown(HTTP 429) carryingretryAfterMs, so schedulers can re-queue instead of being silently held for minutes — and the hot risk state is never hammered. Any success closes the loop.Observability — the daemon's own stdio is discarded (
stdio: 'ignore'), so pacing events also land in the/logsring buffer:[pacing] spacing xiaohongshu navigate by 1881ms/[pacing] xiaohongshu navigate refused — security cooldown, retry in 312s.Kill switch —
OPENCLI_PACING=offat daemon startup disables the layer.Verification
site-pacing.test.ts(20) covers slot serialization, jitter bounds, zero-delay after idle, contextId partitioning, breaker open/close/reset/window-pruning, the classify predicate, the 429 failure shape, and locks the shipped defaults to the pitfall-doc bounds.execution.test.ts(4) covers the report contract: ok on success, security_block on SECURITY_BLOCK, nothing on unrelated failures or non-browser commands.noteinvocations completed in a clean staircase (4.8s / 7.3s / 10.3s) with/logsshowingspacing xiaohongshu navigate by 1881msand3647ms— consecutive slots, jitter in bounds. Sequential invocations correctly pass undelayed (their natural spacing exceeds the interval). The breaker path is deliberately not live-tested — intentionally triggering real security blocks escalates account risk; it is unit-covered.Part of the persistent-session/risk-control series: weibo #2442 → xiaohongshu #2460/#2461 → this.
🤖 Generated with Claude Code
Part of the xiaohongshu risk-control/persistence arc — umbrella issue with full motivation and cross-PR context: #2470