PrivateClient is an unofficial macOS client for Private Internet Access (PIA), built with SwiftUI, Network Extension, and Partout.
Implemented today:
- Username/password sign-in against the PIA token API
- Region discovery from PIA server list API
- Transport selection:
WireGuard,OpenVPN UDP,OpenVPN TCP - Connect, disconnect, and in-session server switching
- Latency measurement and latency-based region ordering
- Port forwarding
- Map + sidebar region selection UI
- Session log viewer (app + tunnel log surface)
- Menu Bar extra with status,
Quick Connect, and disconnect - Credential/token persistence in Keychain
Not implemented yet:
- Kill switch / firewall rules
- Dedicated IP workflows
- Favorites and richer connection rules/policies
This project follows:
How this maps in practice:
- PIA semantics:
- Token endpoint:
https://www.privateinternetaccess.com/api/client/v2/token - Server list endpoint:
https://serverlist.piaservers.net/vpninfo/servers/v6 - WireGuard
addKeyflow against selected region server - OpenVPN token split compatibility: first 62 chars username, remainder password
- Token endpoint:
- Partout split:
- App target handles UX, API calls, state, and profile construction
- Tunnel target executes VPN runtime with
NEPTPForwarder/NETunnelController
PrivateClient/: SwiftUI app,AppModel, app lifecycle, UIPrivateClientShared/: PIA API client, models, profile builder, shared configPrivateClientTunnel/:NEPacketTunnelProvider+ Partout registry/runtime wiringPrivateClientTests/: unit tests for decoding, mappings, token/profile rules, latency selector behavior
- Stable profile strategy:
- Uses one fixed profile ID (
PrivateClientConfiguration.tunnelProfileIdentifier) to avoid accumulating VPN profiles.
- Uses one fixed profile ID (
- WireGuard setup:
- Generates key material, calls
addKey, then builds a WireGuard module from handshake values. - Preserves host identity by connecting with
server.cnand routing viaserver.ip.
- Generates key material, calls
- OpenVPN setup:
- Builds config dynamically with PIA CA cert and token-derived credentials.
- DNS safety:
- Filters DNS values to numeric IPs only.
- Falls back to
10.0.0.243when metadata is hostname-based.
- First-permission reliability:
- Connect path retries transient first-time NE config failures once after the allow prompt race.
Both app and tunnel targets must be configured with:
- Network Extension capability (
packet-tunnel-provider) - Shared App Group:
group.uk.tarun.PrivateClient - Matching keychain access group
If connect fails with Network Extension configuration errors, verify entitlement/capability setup first.
Prerequisites:
- Xcode (current project builds/tests on recent Xcode with macOS destination)
- Apple developer signing setup that supports Network Extension for local run
- Optional:
xcodegenif regeneratingPrivateClient.xcodeprojfromproject.yml
Commands:
# Build
xcodebuild -scheme PrivateClient -destination 'platform=macOS' build
# Run tests
xcodebuild -scheme PrivateClient -destination 'platform=macOS' test- Keep PIA-specific protocol/business logic out of SwiftUI views.
- Maintain app/tunnel process boundaries (protocol engines run in tunnel extension, not app process).
- Add/update tests when changing decoding, endpoint mapping, credential shaping, or profile/DNS construction.
Partout is GPLv3. Treat this project as GPL-compatible when distributing derivatives.