Skip to content

Add isolated connectivity checker that probes the resolved proxy#4

Merged
elielgordondensity merged 6 commits into
mainfrom
claude/add-proxy-connectivity-checker-9pP16
May 13, 2026
Merged

Add isolated connectivity checker that probes the resolved proxy#4
elielgordondensity merged 6 commits into
mainfrom
claude/add-proxy-connectivity-checker-9pP16

Conversation

@elielgordondensity
Copy link
Copy Markdown

VintageNet's per-interface connection property answers "does this
interface have direct internet reachability." On corporate networks
that resolve a proxy via WPAD/PAC, that's the wrong signal to gate
application traffic on — the interface is healthy but direct egress
is firewalled and outbound HTTP only works through the proxy.

VintageNetProxy.Connectivity reports the second signal. It probes
whether the resolved proxy is actually carrying outbound traffic and
publishes the result at ["proxy", "connectivity"] so consumers can
subscribe. The checker is mounted as a sibling of the main supervisor
so a crash in either tree doesn't cascade, and is off by default —
flip on with config :vintage_net_proxy, connectivity: [...].

Probes: TCP-connect for :direct, HTTP CONNECT for HTTP/HTTPS
proxies, :socks_not_supported for SOCKS. :auto is resolved
against the probe URL first, then dispatched. Triggers: startup,
periodic, and any change to the published proxy.

VintageNet's per-interface `connection` property answers "does this
interface have direct internet reachability." On corporate networks
that resolve a proxy via WPAD/PAC, that's the wrong signal to gate
application traffic on — the interface is healthy but direct egress
is firewalled and outbound HTTP only works through the proxy.

`VintageNetProxy.Connectivity` reports the second signal. It probes
whether the resolved proxy is actually carrying outbound traffic and
publishes the result at `["proxy", "connectivity"]` so consumers can
subscribe. The checker is mounted as a sibling of the main supervisor
so a crash in either tree doesn't cascade, and is off by default —
flip on with `config :vintage_net_proxy, connectivity: [...]`.

Probes: TCP-connect for `:direct`, HTTP `CONNECT` for HTTP/HTTPS
proxies, `:socks_not_supported` for SOCKS. `:auto` is resolved
against the probe URL first, then dispatched. Triggers: startup,
periodic, and any change to the published proxy.
@elielgordondensity elielgordondensity force-pushed the claude/add-proxy-connectivity-checker-9pP16 branch from 2f2509e to 617e84d Compare May 13, 2026 02:14
When the active interface re-fetches its PAC script and the body has
changed but the effective URL stayed the same, the published `config`
property goes :auto -> :auto and no event fires. The connectivity
checker would miss the change until the next periodic probe.

Add a `["proxy", "pac_revision"]` tick the Selector publishes when an
iface's pac_script content changes from one non-nil value to a
different non-nil value. nil <-> non-nil transitions already show up on
`config` (as :auto <-> :unset), so this fires only in the narrow case
the existing signal can't observe.

Connectivity subscribes to the tick and re-probes on receipt. No
change to `["proxy", "config"]` semantics or `resolve/1` behavior.
On corporate WPAD networks VintageNet's direct internet probe fails
(the firewall only allows proxy traffic), so the interface stays at
:lan. Gating proxy publication on :internet would never publish on the
exact networks the library is designed for, hence :lan is treated
equivalently to :internet for proxy resolution. The connectivity
checker is the authoritative "outbound works" signal — it routes
through the resolved proxy, not directly.
A single probe target is a SPOF: if it goes down everywhere, every
device reports "offline" even though the proxy is fine. Replace the
scalar `probe_url` config with `probe_urls` (a list) and try each in
order, halting on the first :ok. Default to three captive-portal
probe endpoints across different administrative domains (Google /
Mozilla / Microsoft) so a single-vendor outage doesn't take a fleet
down. Under normal operation only the first URL is probed, so the
fallbacks add no steady-state traffic. The accumulator in
reduce_while is the most recent error, so the surfaced reason is a
concrete failure rather than a synthetic aggregate.
Three cleanups from a review pass:

  * Merge the identical handle_info clauses for `["proxy", "config"]`
    and `["proxy", "pac_revision"]` behind an `in` guard; one body,
    one comment explaining why both feed the same re-probe path.
  * Hoist `Publisher.get()` out of the probe loop — with a fallback
    list we were re-reading the property table per URL. Per-URL
    resolution stays inside the loop because PAC results genuinely
    differ per URL.
  * Drop the unreachable `_ -> :direct` catch-all in `decide/2` and
    the unreachable `:no_probes_attempted` initial accumulator. The
    cases the decider can hit are documented in
    `t:VintageNetProxy.proxy/0`.

Module attributes for `@config_path` and `@pac_revision_path` let
pattern heads reference the same paths used by `VintageNet.subscribe`,
killing a stringly-typed literal duplication inside this module.
@elielgordondensity elielgordondensity merged commit b5d1f88 into main May 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants