Skip to content

Proxy blocklist race after network change with local PAC file #183

Description

@derbauer97

Problem

When switching from a network where the corporate proxy is unreachable (e.g. mobile hotspot) back to the corporate network, alpaca gets stuck in DIRECT mode for up to 5 minutes despite the proxy being reachable again.

Root Cause

The sequence of events:

  1. On hotspot: proxy unreachable → blockProxy() adds it to blocklist (5 min TTL) → requests fall through to DIRECT (from ; DIRECT in PAC)
  2. Switch back to corporate network → new IP assigned
  3. Next request triggers checkForUpdates()addrsChanged() returns true → download() returns PAC bytes → blocklist is reset via pf.blocked = newBlocklist()
  4. But: a concurrent request (from system apps like Defender, Edge etc.) hits handleConnect() / proxyRequest() at the same moment. The TCP handshake to the proxy fails because the network is not fully stable yet → blockProxy() immediately re-adds the proxy to the blocklist
  5. From this point: addrsChanged() returns false (addresses have not changed since step 3) → blocklist is never cleared again → proxy stays blocked for the full 5 minutes

Environment

  • macOS (Apple Silicon)
  • Alpaca v2.0.13
  • Local PAC file (file://) with "PROXY corporate-proxy:8080; DIRECT"
  • LaunchAgent with KeepAlive=true
  • System proxy pointing at alpaca (so GUI apps like Edge also route through it)

Suggested Fix

Add a grace period after addrsChanged() returns true, during which blockProxy() is suppressed. This would give the network time to stabilize before alpaca starts evaluating proxy reachability. Similar to Chrome's kDelayAfterNetworkChangesMs concept.

Alternatively, reduce the blocklist TTL when a recent network change was detected (e.g. 5 seconds instead of 5 minutes).

Workaround

Restart alpaca after network switch:

pkill -9 alpaca  # launchd restarts it via KeepAlive

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions