Pin ProtonVPN sessions to the plain transport #165

Merged
mysticalsoap merged 1 commit from pin-proton-transport into trunk 2026-08-26 14:10:40 -04:00
Owner

Problem

A successful scheduled renewal dumped a full aiohttp traceback and four Task was destroyed but it is pending! errors into the log. proton-core's default AutoTransport races the plain transport against AlternativeRoutingTransport with a 5s head start; the renewal's refresh() ran ~11s, so ART fired its discovery — DoH queries to hardcoded Google (8.8.4.4/8.8.8.8 + v6) and Quad9 resolvers — the v6 probe raised on a v6-less network, and when the primary transport won, the abandoned racer tasks surfaced as ERROR tracebacks. Besides the noise, that's a root service phoning third-party DNS whenever Proton's API is momentarily slow (#164).

Fix

load_proton_core now hands out a session factory that pins session.transport_factory = AiohttpTransport — exactly auto's primary transport, minus the racer. Both the login and renewal paths build sessions through it.

Trade-off, accepted: on a network where Proton's API is blocked, an import fails with the normal network error instead of possibly succeeding via alternative routing's fallback domains. The task leak itself is an upstream python-proton-core bug regardless.

Verification

  • pytest green (370 passed) plus the ruff/compileall gates.
  • New guarded test (skips when python-proton-core isn't installed) builds a real session through the factory and asserts the pinned transport and user agent.
  • Live check after merge: a scheduled renewal that runs long should produce no asyncio/aiohttp tracebacks — and no DNS traffic to 8.8.8.8/Quad9 from the service.

Closes #164

🤖 Generated with Claude Code

## Problem A successful scheduled renewal dumped a full aiohttp traceback and four `Task was destroyed but it is pending!` errors into the log. proton-core's default `AutoTransport` races the plain transport against `AlternativeRoutingTransport` with a 5s head start; the renewal's `refresh()` ran ~11s, so ART fired its discovery — DoH queries to hardcoded Google (8.8.4.4/8.8.8.8 + v6) and Quad9 resolvers — the v6 probe raised on a v6-less network, and when the primary transport won, the abandoned racer tasks surfaced as ERROR tracebacks. Besides the noise, that's a root service phoning third-party DNS whenever Proton's API is momentarily slow (#164). ## Fix `load_proton_core` now hands out a session factory that pins `session.transport_factory = AiohttpTransport` — exactly auto's primary transport, minus the racer. Both the login and renewal paths build sessions through it. Trade-off, accepted: on a network where Proton's API is blocked, an import fails with the normal network error instead of possibly succeeding via alternative routing's fallback domains. The task leak itself is an upstream python-proton-core bug regardless. ## Verification - `pytest` green (370 passed) plus the ruff/compileall gates. - New guarded test (skips when python-proton-core isn't installed) builds a real session through the factory and asserts the pinned transport and user agent. - Live check after merge: a scheduled renewal that runs long should produce no asyncio/aiohttp tracebacks — and no DNS traffic to 8.8.8.8/Quad9 from the service. Closes #164 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: pin ProtonVPN sessions to the plain transport
Some checks failed
ci / test (pull_request) Successful in 27s
ci / test (push) Has been cancelled
ef9fd3be01
proton-core's default AutoTransport races AiohttpTransport against
AlternativeRoutingTransport with a 5-second head start, so any API
call that runs long fires ART's discovery: DoH queries to hardcoded
Google and Quad9 resolvers -- third-party traffic nothing in aqomui
asked a root service to send. When the primary transport then wins,
the abandoned racer tasks surface through asyncio's exception handler
as ERROR tracebacks, which is how a successful scheduled renewal
still managed to dump a page of aiohttp errors into the log.

Pinning AiohttpTransport keeps exactly auto's primary path and drops
the racer. The cost is alternative routing's censorship rescue: on a
network where Proton's API is blocked, an import now fails with the
normal network error instead of possibly succeeding through a
fallback domain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mysticalsoap deleted branch pin-proton-transport 2026-08-26 14:10:40 -04:00
Sign in to join this conversation.
No description provided.