Repository navigation
fix(api): a target refusing the egress IP is not a healthy proxy - #10654
Merged
diegosouzapw merged 1 commit intoAug 20, 2026
Conversation
The reachability probe classified every response under 500 as "ok", so a 401/403/429 from the probe target — the shape a destination uses to refuse a banned or rate-limited IP — was reported as a healthy proxy. The proxy did relay, so it is not failing; but it is not serving that destination either, and "ok" hid exactly that. Add a "blocked" outcome for 401/403/429, produced by a shared, pure classifyProbeStatus() so the sweep and the "Test All" endpoint stop deriving the same rule twice. Behaviour is deliberately unchanged: - "blocked" is neutral in decideProxyHealthAction, like "inconclusive": no failure count, no status write. One target refusing an IP does not make the proxy dead, and the removal policy stays operator-owned (cheaper-inference#6246 policy C/D). - On /api/settings/proxies/auto-test, `alive` keeps its exact prior meaning (any status under 500). The refusal is surfaced as a separate optional `blockedByTarget` field, so nothing changes for the opt-in status write behind PROXY_HEALTH_AUTO_DEACTIVATE. The sweep log gains a "blocked by target" count. No API contract change beyond one optional field, no UI string, no migration.
This was referenced Aug 18, 2026
Collaborator
|
Verified locally against release/v3.8.50: |
diegosouzapw
merged commit Aug 20, 2026
b43ad73
into
cheaper-inference:release/v3.8.50
5 checks passed
diegosouzapw
pushed a commit
that referenced
this pull request
Aug 20, 2026
…ic target (#10664) Merged — locally validated (16/16 focused tests, typecheck:core clean, file-size/changelog gates green) after resolving base-drift against #10654/#10657 (both landed earlier today, same file chain — combined additively, no logical conflict). Nice chain of proxy-probe-target fixes, thanks!
This was referenced Sep 13, 2026
diegosouzapw
added a commit
to maxmad64bis/OmniRouteFork
that referenced
this pull request
Sep 15, 2026
…ag (default off) Registers the switch for the proxy health policy change of cheaper-inference#13608 as a regular feature flag (health category, defaultValue "false"), so a target-refused probe keeps the deliberate cheaper-inference#10654 neutral policy unless an operator opts in. The environment variable of the same name works through the normal flag resolution (DB override > env > default). Adds the catalog row in FEATURE_FLAGS.md, the description i18n key (vi and pt-BR translated, other locales deferred with the __MISSING__ marker) and bumps the flag-count assertions from 55 to 56.
diegosouzapw
added a commit
to maxmad64bis/OmniRouteFork
that referenced
this pull request
Sep 15, 2026
… only behind PROXY_HEALTH_BLOCKED_RESETS_STREAK cheaper-inference#13608 made every target-refused probe (401/403/429) reset the consecutive-failure streak, reversing the deliberate cheaper-inference#10654 policy for all installs. Keep the idea as an opt-in: - decideProxyHealthAction() takes an optional blockedResetsStreak input (default false = blocked stays neutral like inconclusive, exactly as on the base); the sweep reads the PROXY_HEALTH_BLOCKED_RESETS_STREAK feature flag once per sweep and passes it in. With the flag on a refusal resets the streak but still never counts, sets a status or removes a proxy. - Fix the comments: decision.ts policy E no longer claims that any relayed HTTP response resets the streak (a relayed 5xx stays inconclusive and keeps it), and the stale testOneProxy JSDoc in scheduler.ts describes both modes. - Restore the existing policy-E assertions of proxy-health-blocked-outcome.test.ts to the base contract and add flag-on cases next to them. - Replace the source-grep check of the sweep summary with a behavioral test through forceProxyHealthSweep(): a local relay answering 403 drives fail -> blocked -> fail with PROXY_AUTO_DISABLE and a threshold of 2 (flag off: the proxy ends dead; flag on: it keeps its status), and the captured summary line pins the refusal tally.
diegosouzapw
pushed a commit
that referenced
this pull request
Sep 15, 2026
…ure streak (#13608) Behind the new `PROXY_HEALTH_BLOCKED_RESETS_STREAK` flag (default off), a probe the target refuses (401/403/429) resets the proxy's consecutive-failure streak, so a proxy that clearly relays is not marked dead by spaced-out real failures. Maintainer rework before merge (kept the idea, no default behavior change): - The original reversed the deliberate #10654 policy for everyone; with the flag off a refusal stays neutral, and the existing assertions are restored. The stale JSDoc and the wrong "any relayed response resets" comment are fixed (5xx stays inconclusive). - The source-grep test became a real sweep test: a local relay answering 403 drives fail → blocked → fail with auto-disable, in both flag modes. Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests. Thanks @maxmad64bis!
4 of 5 tasks
5 tasks
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…aper-inference#10654) Merged — locally validated in a combined batch worktree with the related proxy-health-probe PRs (typecheck:core clean, gates green). Thanks!
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
… checks (cheaper-inference#10657) Merged — locally validated (15/15 focused tests, typecheck:core clean, file-size/changelog gates green) after resolving base-drift against cheaper-inference#10654 (both landed today, same file — combined import block, no logical conflict). Thanks!
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ic target (cheaper-inference#10664) Merged — locally validated (16/16 focused tests, typecheck:core clean, file-size/changelog gates green) after resolving base-drift against cheaper-inference#10654/cheaper-inference#10657 (both landed earlier today, same file chain — combined additively, no logical conflict). Nice chain of proxy-probe-target fixes, thanks!
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ure streak (cheaper-inference#13608) Behind the new `PROXY_HEALTH_BLOCKED_RESETS_STREAK` flag (default off), a probe the target refuses (401/403/429) resets the proxy's consecutive-failure streak, so a proxy that clearly relays is not marked dead by spaced-out real failures. Maintainer rework before merge (kept the idea, no default behavior change): - The original reversed the deliberate cheaper-inference#10654 policy for everyone; with the flag off a refusal stays neutral, and the existing assertions are restored. The stale JSDoc and the wrong "any relayed response resets" comment are fixed (5xx stays inconclusive). - The source-grep test became a real sweep test: a local relay answering 403 drives fail → blocked → fail with auto-disable, in both flag modes. Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 d61b804: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests. Thanks @maxmad64bis!
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.
Summary
ok(
src/lib/proxyHealth/scheduler.ts). A 401/403/429 from the probe target — the shape adestination uses to refuse a banned or rate-limited egress IP — was therefore reported as a
healthy proxy. The proxy did relay, so it is not failing; but it is not serving that
destination either, and
okhid exactly that.blockedoutcome for 401/403/429, produced by a shared pureclassifyProbeStatus().The sweep and the "Test All" endpoint (
/api/settings/proxies/auto-test) were deriving thesame
status < 500rule independently; they now share one definition.blocked by targetcount next toalive/inconclusive.Behaviour is deliberately unchanged.
blockedis neutral indecideProxyHealthAction, exactly likeinconclusive: no failurecount, no status write. One target refusing an IP does not make the proxy dead, and the
removal policy stays operator-owned ([BUG] Massive issue with proxies since 3.8.44 - IP leak #6246 policy C/D).
/api/settings/proxies/auto-test,alivekeeps its exact prior meaning (any status under500). The refusal is surfaced as a separate optional
blockedByTargetfield, so nothingchanges for the opt-in status write behind
PROXY_HEALTH_AUTO_DEACTIVATE.No migration, no feature flag, no UI string, no i18n churn.
Related Issues
Validation
npm run lintThe single typecheck error is already tracked on the base and lives in a file this PR does not
touch.
Tests Added Or Updated
tests/unit/proxy-health-blocked-outcome.test.ts(new, 8 tests):classifyProbeStatusmaps 401/403/429 →blocked, 200/204/301/400/404 →ok,500/502/503 →
inconclusive;[401, 403, 429]are flaggedblocked, andalive(ok || blocked) equals the previousstatus < 500for every status — theregression guard for the "Test All" endpoint keeping its behaviour;
decideProxyHealthActiononblockedtouches neither count nor status, including at theremoval threshold with both
autoRemoveandautoDisableenabled — the destructive path isproven unreachable from
blocked;blockeddoes not reset a failure streak the wayokdoes.Coverage Notes
src/:classifyProbeStatus()(pure, every branch covered), theblockedbranch ofdecideProxyHealthAction(covered, including the destructive-path case), and the two callsites that now consume the shared classifier.
scheduler.tsloses an inline ternary to a tested function.Reviewer Notes
ProxyProbeOutcomewidens from three values to four. Both consumers live in this PR(
scheduler.ts,decision.ts), and the union is closed, so TypeScript enforces that the newcase is handled — that exhaustiveness is the point.
407as a response — it throws,both on an HTTPS
CONNECTtunnel and on a plain HTTP request — so a proxy demandingauthentication already lands in the
catchand is classifiedfail. Verified against a localproxy answering 407 to everything; no change was warranted.
to everyone, every proxy is reported blocked for that sweep. Because
blockedis neutral, theworst case is one misleading log line — no proxy is disabled or removed. A guard would need
pool-wide aggregation in the sweep for a strictly cosmetic gain.
blockedByTargetis optional and only present when true, so existing consumers of the"Test All" response are unaffected.
blockedintoalivewas considered and rejected: underPROXY_HEALTH_AUTO_DEACTIVATE=trueit would start deactivating proxies that are currently leftactive, which is a policy change rather than an observability fix.