Skip to main content
The VPN layer is built around an abstract VpnBackend class that each protocol implementation extends. A provider selects and prepares the right backend based on the user’s protocol preference.

The VpnBackend abstract class

VpnBackend (vpn/VpnBackend.kt) is the common contract for all protocol implementations. It:
  • Manages a local agent connection (certificate-based mTLS channel to the VPN server)
  • Tracks the tunnel’s VpnState via a MutableStateFlow
  • Handles certificate refresh and key rotation
  • Surfaces NetShield statistics and exit IP information

Local agent

Every connected backend opens a local agent channel — a TLS connection to the VPN server’s local agent endpoint. The agent:
  • Authenticates the session with the client certificate
  • Sends feature flags (NetShield level, randomised NAT, VPN Accelerator)
  • Delivers NetShield statistics and connectionDetails (exit IP)
  • Reports certificate errors and policy violations
The inner VpnAgentClient class implements the NativeClient interface from the gopenpgp/localAgent Go library and translates callbacks into VpnState updates on the main coroutine scope.

VPN state

Protocol state is tracked in two places: VpnConnectionManager observes selfStateFlow and forwards it to VpnStateMonitor.

Protocol implementations

WireguardBackend (vpn/wireguard/WireguardBackend.kt) wraps the WireGuard Android library (com.wireguard.android.backend.GoBackend).Supported transmission protocols: UDP (default), TCP, TLS
Connection flow:
  1. prepareForConnection scans available ports on the server.
  2. connect builds a WireGuard tunnel config from ConnectionParamsWireguard (including the X25519 key from CertificateRepository) and calls backend.setState(Tunnel.State.UP, config, transmissionStr, serverNameStrategy).
  3. A monitoring coroutine (startMonitoringJob) polls backend.state in a loop and maps the raw integer state to VpnState:
Android VPN service: WireguardWrapperService is the VpnService subclass for WireGuard. References to the service are held weakly by the backend via serviceCreated / serviceDestroyed callbacks.
On Android 10+, the backend waits for the tunnel’s NET_CAPABILITY_VALIDATED network capability before opening the local agent connection. This prevents TLS failures on newer OS versions.

ProtonVpnBackendProvider

ProtonVpnBackendProvider (vpn/ProtonVpnBackendProvider.kt) implements VpnBackendProvider and decides which backend to use for a given connection request.
It is constructed in AppModule.kt:

Protocol selection logic

prepareConnection maps a ProtocolSelection to the right backend: Smart protocol tries backends in order of preference and returns the first one that responds:
getSmartEnabledBackends checks AppConfig.getSmartProtocolConfig() to determine which protocols are enabled (WireGuard UDP/TCP/TLS, ProTun). Feature flag wireguardTlsEnabled gates TCP and TLS transmission modes.

Fallback pinging (pingAll)

When VpnConnectionErrorHandler needs to find an alternative server after a failure, it calls pingAll. This method concurrently probes a list of candidate servers across all smart-enabled backends and returns the first server that responds:
Each server is probed with up to PING_ALL_MAX_PORTS = 3 ports unless it is designated as the fullScanServer, in which case all ports are scanned.

Certificate management

CertificateRepository (vpn/CertificateRepository.kt) manages the Ed25519 keypair and the short-lived VPN certificate issued by the Proton API.

Key lifecycle

  1. Generate — CertificateKeyProvider.generateCertInfo() creates a new Ed25519 keypair using the gopenpgp Go library. The result is stored in CertificateStorage (encrypted with Android Keystore via CertStorageCrypto).
  2. Fetch — updateCertificateFromBackend calls api.getCertificate(sessionId, publicKeyPem) and stores the returned PEM certificate alongside the keys.
  3. Refresh — Certificates are refreshed proactively via PeriodicUpdateManager. The next refresh time is derived from the API response (refreshAt) or set to halfway between now and expiry on error.
  4. Revoke — If the local agent reports errorCodeBadCertSignature or errorCodeCertificateRevoked, the backend calls revokeCertificateAndReconnect, which generates a new keypair and forces an immediate certificate fetch before reconnecting.
The currentCertUpdateFlow shared flow notifies active backends when a new certificate is available, triggering an agent reconnection without tearing down the VPN tunnel.
Certificate keys are session-scoped. If a user has multiple active sessions, each session maintains its own keypair and certificate stored under its SessionId.

Error types

ErrorType (defined in VpnState.kt) enumerates all terminal and recoverable errors that backends can emit: Recoverable errors trigger VpnConnectionErrorHandler, which attempts to find an alternative server via pingAll before surfacing a failure to the user.