api

package
v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2026 License: GPL-3.0 Imports: 20 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultBaseURL = "https://vpn-api.proton.me"
	AppVersion     = "linux-vpn@4.13.1"
	UserAgent      = "ProtonVPN/4.13.1 (Linux; go-pvpn)"
	DefaultTimeout = 30 * time.Second
	MaxRetries     = 3
)
View Source
const (
	ServerFeatureSecureCore = 1
	ServerFeatureTor        = 2
	ServerFeatureP2P        = 4
	ServerFeatureStreaming  = 8
	ServerFeatureIPv6       = 16
)

Server feature bitmask

View Source
const (
	ServerTierFree      = 0
	ServerTierBasic     = 1 // Legacy, treat as Plus
	ServerTierPlus      = 2
	ServerTierVisionary = 3
)

Server tiers

Variables

This section is empty.

Functions

func CountryList

func CountryList(servers []LogicalServer) []string

CountryList returns a sorted list of unique exit countries from the server list.

func GroupServersByCountry

func GroupServersByCountry(servers []LogicalServer) map[string][]LogicalServer

GroupServersByCountry groups servers by their exit country.

func IsAuthError added in v0.1.7

func IsAuthError(err error) bool

IsAuthError checks whether an error represents a permanent auth failure. Returns false for network errors, timeouts, and other transient issues.

func Needs2FA

func Needs2FA(auth *AuthResponse) bool

Needs2FA checks if the auth response requires 2FA to proceed.

Types

type APIError

type APIError struct {
	Code    int         `json:"Code"`
	Error   string      `json:"Error"`
	Details interface{} `json:"Details,omitempty"`
}

func (*APIError) IsSuccess

func (e *APIError) IsSuccess() bool

type Auth2FARequest

type Auth2FARequest struct {
	TwoFactorCode string `json:"TwoFactorCode"`
}

type Auth2FAResponse

type Auth2FAResponse struct {
	Code   int      `json:"Code"`
	Scopes []string `json:"Scopes"`
	Scope  string   `json:"Scope"`
}

type AuthInfoRequest

type AuthInfoRequest struct {
	Username string `json:"Username"`
	Intent   string `json:"Intent"`
}

type AuthInfoResponse

type AuthInfoResponse struct {
	Code            int    `json:"Code"`
	Version         int    `json:"Version"`
	Modulus         string `json:"Modulus"`
	ServerEphemeral string `json:"ServerEphemeral"`
	Salt            string `json:"Salt"`
	SRPSession      string `json:"SRPSession"`
	TwoFA           struct {
		Enabled int `json:"Enabled"`
		TOTP    int `json:"TOTP"`
	} `json:"2FA"`
}

type AuthRequest

type AuthRequest struct {
	Username        string `json:"Username"`
	ClientEphemeral string `json:"ClientEphemeral"`
	ClientProof     string `json:"ClientProof"`
	SRPSession      string `json:"SRPSession"`
}

type AuthResponse

type AuthResponse struct {
	Code         int      `json:"Code"`
	AccessToken  string   `json:"AccessToken"`
	RefreshToken string   `json:"RefreshToken"`
	TokenType    string   `json:"TokenType"`
	UID          string   `json:"UID"`
	UserID       string   `json:"UserID"`
	ServerProof  string   `json:"ServerProof"`
	Scope        string   `json:"Scope"`
	Scopes       []string `json:"Scopes"`
	ExpiresIn    int      `json:"ExpiresIn"`
	TwoFA        struct {
		Enabled int `json:"Enabled"`
		TOTP    int `json:"TOTP"`
	} `json:"2FA"`
}

type CertRefresher

type CertRefresher struct {

	// Called when a new certificate is obtained.
	OnCertRefresh func(cert *CertificateResponse)
	// contains filtered or unexported fields
}

CertRefresher manages automatic certificate rotation in the background.

func NewCertRefresher

func NewCertRefresher(client *Client, kp *KeyPair, features CertificateFeatures) *CertRefresher

NewCertRefresher creates a new certificate refresher.

func (*CertRefresher) CurrentCert

func (r *CertRefresher) CurrentCert() *CertificateResponse

CurrentCert returns the current certificate.

func (*CertRefresher) Start

func (r *CertRefresher) Start(ctx context.Context) error

Start begins the certificate refresh loop. It immediately requests a cert, then refreshes at the API-specified RefreshTime.

func (*CertRefresher) Stop

func (r *CertRefresher) Stop()

Stop halts the refresh loop.

type CertificateFeatures

type CertificateFeatures struct {
	NetShieldLevel int  `json:"NetShieldLevel"`
	RandomNAT      bool `json:"RandomNAT"`
	SplitTCP       bool `json:"SplitTCP"`
	PortForwarding bool `json:"PortForwarding"`
}

type CertificateRequest

type CertificateRequest struct {
	ClientPublicKey     string              `json:"ClientPublicKey"`
	ClientPublicKeyMode string              `json:"ClientPublicKeyMode"`
	Mode                string              `json:"Mode"`
	DeviceName          string              `json:"DeviceName"`
	Duration            string              `json:"Duration"`
	Features            CertificateFeatures `json:"Features"`
}

type CertificateResponse

type CertificateResponse struct {
	Code                 int    `json:"Code"`
	SerialNumber         string `json:"SerialNumber"`
	ClientKeyFingerprint string `json:"ClientKeyFingerprint"`
	ClientKey            string `json:"ClientKey"`
	Certificate          string `json:"Certificate"`
	ExpirationTime       int64  `json:"ExpirationTime"`
	RefreshTime          int64  `json:"RefreshTime"`
	Mode                 string `json:"Mode"`
	DeviceName           string `json:"DeviceName"`
	ServerPublicKeyMode  string `json:"ServerPublicKeyMode"`
	ServerPublicKey      string `json:"ServerPublicKey"`
}

func (*CertificateResponse) ExpiresAt

func (c *CertificateResponse) ExpiresAt() time.Time

func (*CertificateResponse) RefreshAt

func (c *CertificateResponse) RefreshAt() time.Time

type Client

type Client struct {

	// Called when tokens are rotated so the session can be persisted.
	OnTokenRefresh func(uid, accessToken, refreshToken string)
	// contains filtered or unexported fields
}

Client is the Proton VPN API client. It handles authenticated requests, automatic token refresh, and retry logic.

func NewClient

func NewClient(session *Session) *Client

NewClient creates a new API client. If session is non-nil, the client is initialized with existing auth tokens.

func (*Client) BaseURL added in v0.1.2

func (c *Client) BaseURL() string

BaseURL returns the API base URL (needed for kill switch pinhole during reconnection).

func (*Client) GetClientConfig

func (c *Client) GetClientConfig(ctx context.Context) (*ClientConfigResponse, error)

GetClientConfig returns the client configuration.

func (*Client) GetLocation

func (c *Client) GetLocation(ctx context.Context) (*LocationResponse, error)

GetLocation returns the client's current IP and location.

func (*Client) GetServerLoads

func (c *Client) GetServerLoads(ctx context.Context) (*LogicalsResponse, error)

GetServerLoads fetches just the server loads (lighter than full server list).

func (*Client) GetServers

func (c *Client) GetServers(ctx context.Context) (*LogicalsResponse, error)

GetServers returns the full server list.

func (*Client) GetSession

func (c *Client) GetSession() Session

GetSession returns the current session tokens.

func (*Client) GetSessions

func (c *Client) GetSessions(ctx context.Context) (*SessionsResponse, error)

GetSessions returns active VPN sessions.

func (*Client) GetVPNInfo

func (c *Client) GetVPNInfo(ctx context.Context) (*VPNInfoResponse, error)

GetVPNInfo returns the VPN account info.

func (*Client) IsAuthenticated

func (c *Client) IsAuthenticated() bool

IsAuthenticated returns true if the client has auth tokens.

func (*Client) Login

func (c *Client) Login(ctx context.Context, username, password string) (*AuthResponse, error)

Login authenticates with the Proton API using SRP. Returns the auth response. If 2FA is required, the caller must call Submit2FA.

func (*Client) LoginEmail

func (c *Client) LoginEmail() string

LoginEmail returns the email the user logged in with.

func (*Client) Logout

func (c *Client) Logout(ctx context.Context) error

Logout invalidates the current session.

func (*Client) PinnedAPIIPs added in v0.2.1

func (c *Client) PinnedAPIIPs() []string

PinnedAPIIPs returns a snapshot of the currently pinned IPs for the API hostname. Order matches the order given to SetPinnedAPIIPs.

func (*Client) RequestCert

func (c *Client) RequestCert(ctx context.Context, kp *KeyPair, features CertificateFeatures) (*CertificateResponse, error)

RequestCert requests a VPN certificate from the API using the given key pair.

func (*Client) RequestCertificate

func (c *Client) RequestCertificate(ctx context.Context, req *CertificateRequest) (*CertificateResponse, error)

RequestCertificate requests a new VPN certificate.

func (*Client) SetPinnedAPIIPs added in v0.2.1

func (c *Client) SetPinnedAPIIPs(ips []string)

SetPinnedAPIIPs installs a list of pre-resolved IP addresses for the Proton API hostname. Called by the daemon at startup after loading the cached IPs from disk, so the first API request after boot does not require DNS — which would be blocked by the pre-boot kill switch. Safe to call with an empty list (clears pinning).

func (*Client) SetSession

func (c *Client) SetSession(uid, accessToken, refreshToken string)

SetSession updates the client's auth tokens.

func (*Client) Submit2FA

func (c *Client) Submit2FA(ctx context.Context, code string) error

Submit2FA submits a TOTP 2FA code to complete authentication.

type ClientConfigResponse

type ClientConfigResponse struct {
	Code          int           `json:"Code"`
	DefaultPorts  DefaultPorts  `json:"DefaultPorts"`
	HolesIPs      []string      `json:"HolesIPs"`
	FeatureFlags  FeatureFlags  `json:"FeatureFlags"`
	SmartProtocol SmartProtocol `json:"SmartProtocol"`
}

type DefaultPorts

type DefaultPorts struct {
	OpenVPN   ProtocolPorts `json:"OpenVPN"`
	WireGuard ProtocolPorts `json:"WireGuard"`
}

type FeatureFlags

type FeatureFlags struct {
	NetShield      bool `json:"NetShield"`
	PortForwarding bool `json:"PortForwarding"`
	ModerateNAT    bool `json:"ModerateNAT"`
	VpnAccelerator bool `json:"VpnAccelerator"`
	WireGuardTls   bool `json:"WireGuardTls"`
}

type KeyPair

type KeyPair struct {
	// Ed25519 key pair (used for API cert requests and Local Agent mTLS)
	Ed25519 *ed25519.KeyPair

	// X25519 private key in base64 (used as WireGuard private key)
	WireGuardPrivateKey string

	// Ed25519 public key in PEM format (sent to API)
	PublicKeyPEM string
}

KeyPair holds the generated Ed25519/X25519 keys for VPN use.

func GenerateKeyPair

func GenerateKeyPair() (*KeyPair, error)

GenerateKeyPair creates a new Ed25519 key pair and derives the X25519 WireGuard private key from it.

type LocationResponse

type LocationResponse struct {
	Code    int     `json:"Code"`
	IP      string  `json:"IP"`
	Lat     float64 `json:"Lat"`
	Long    float64 `json:"Long"`
	Country string  `json:"Country"`
	ISP     string  `json:"ISP"`
}

type LogicalServer

type LogicalServer struct {
	ID           string           `json:"ID"`
	Name         string           `json:"Name"`
	EntryCountry string           `json:"EntryCountry"`
	ExitCountry  string           `json:"ExitCountry"`
	Domain       string           `json:"Domain"`
	Tier         int              `json:"Tier"`
	Features     int              `json:"Features"`
	Region       string           `json:"Region,omitempty"`
	City         string           `json:"City,omitempty"`
	Score        float64          `json:"Score"`
	Load         int              `json:"Load"`
	Status       int              `json:"Status"`
	HostCountry  string           `json:"HostCountry,omitempty"`
	Location     ServerLocation   `json:"Location"`
	Servers      []PhysicalServer `json:"Servers"`
}

func FilterServers

func FilterServers(servers []LogicalServer, filter ServerFilter, userTier int) []LogicalServer

FilterServers filters and sorts the server list based on the given criteria. Servers are sorted by score (lower is better), then by load (lower is better).

func FindFastestServer

func FindFastestServer(servers []LogicalServer, filter ServerFilter, userTier int) *LogicalServer

FindFastestServer returns the best server matching the filter. "Best" = lowest score, then lowest load.

func FindServerByName

func FindServerByName(servers []LogicalServer, name string) *LogicalServer

FindServerByName finds a server by its name (e.g., "CH#10").

func (*LogicalServer) BestServer

func (s *LogicalServer) BestServer() *PhysicalServer

BestServer returns the first online physical server, or nil.

func (*LogicalServer) EffectiveTier

func (s *LogicalServer) EffectiveTier() int

EffectiveTier returns the tier, treating legacy Basic as Plus.

func (*LogicalServer) HasFeature

func (s *LogicalServer) HasFeature(feature int) bool

func (*LogicalServer) IsOnline

func (s *LogicalServer) IsOnline() bool

func (*LogicalServer) IsP2P

func (s *LogicalServer) IsP2P() bool

func (*LogicalServer) IsSecureCore

func (s *LogicalServer) IsSecureCore() bool

func (*LogicalServer) IsStreaming

func (s *LogicalServer) IsStreaming() bool

func (*LogicalServer) IsTor

func (s *LogicalServer) IsTor() bool

type LogicalsResponse

type LogicalsResponse struct {
	Code             int             `json:"Code"`
	LogicalServers   []LogicalServer `json:"LogicalServers"`
	LastModifiedTime string          `json:"LastModifiedTime,omitempty"`
}

type PhysicalServer

type PhysicalServer struct {
	ID                 string `json:"ID"`
	EntryIP            string `json:"EntryIP"`
	ExitIP             string `json:"ExitIP"`
	Domain             string `json:"Domain"`
	Status             int    `json:"Status"`
	Generation         int    `json:"Generation"`
	Label              string `json:"Label"`
	X25519PublicKey    string `json:"X25519PublicKey"`
	ServicesDownReason string `json:"ServicesDownReason,omitempty"`
}

type ProtocolPorts

type ProtocolPorts struct {
	UDP []int `json:"UDP"`
	TCP []int `json:"TCP"`
}

type RefreshRequest

type RefreshRequest struct {
	ResponseType string `json:"ResponseType"`
	GrantType    string `json:"GrantType"`
	RefreshToken string `json:"RefreshToken"`
	RedirectURI  string `json:"RedirectURI"`
}

type RefreshResponse

type RefreshResponse struct {
	Code         int    `json:"Code"`
	AccessToken  string `json:"AccessToken"`
	RefreshToken string `json:"RefreshToken"`
	TokenType    string `json:"TokenType"`
	ExpiresIn    int    `json:"ExpiresIn"`
	Scope        string `json:"Scope"`
	UID          string `json:"UID"`
}

type RequestError

type RequestError struct {
	HTTPStatus int
	Code       int
	Message    string
}

RequestError represents an API error response.

func (*RequestError) Error

func (e *RequestError) Error() string

func (*RequestError) IsAuthError added in v0.1.7

func (e *RequestError) IsAuthError() bool

IsAuthError returns true if this error indicates the session is permanently dead and the user must re-login (e.g. refresh token revoked, account disabled). Transient errors (network, timeout, 5xx) return false.

type ServerFilter

type ServerFilter struct {
	Country         string // ISO country code (e.g., "US", "CH")
	MinTier         int    // Minimum tier (0=free, 2=plus)
	MaxTier         int    // Maximum tier (0 = no limit)
	Features        int    // Required feature bitmask (OR'd)
	ExcludeFeatures int    // Excluded feature bitmask
	OnlineOnly      bool   // Only include online servers
	FreeOnly        bool   // Only free servers
	SecureCore      bool   // Only Secure Core servers
	P2P             bool   // Only P2P servers
	Tor             bool   // Only Tor servers
	Streaming       bool   // Only streaming servers
	SearchQuery     string // Fuzzy match on name/city/country
	ExcludeName     string // Exclude server by name (for "change server")
}

ServerFilter defines criteria for filtering the server list.

type ServerLocation

type ServerLocation struct {
	Lat  float64 `json:"Lat"`
	Long float64 `json:"Long"`
}

type Session

type Session struct {
	UID          string `json:"uid"`
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	// Ed25519 private key in base64 for certificate re-requests
	PrivateKey string `json:"private_key,omitempty"`
	// Login email/username used for display purposes
	LoginEmail string `json:"login_email,omitempty"`
}

type SessionStore

type SessionStore struct {
	// contains filtered or unexported fields
}

SessionStore handles encrypted persistence of session tokens.

func NewSessionStore

func NewSessionStore(path string) (*SessionStore, error)

NewSessionStore creates a session store at the given path. The encryption key is derived from /etc/machine-id using Argon2id.

func (*SessionStore) Delete

func (s *SessionStore) Delete() error

Delete removes the session file.

func (*SessionStore) Exists

func (s *SessionStore) Exists() bool

Exists returns true if a session file exists.

func (*SessionStore) Load

func (s *SessionStore) Load() (*Session, error)

Load decrypts and reads the session from disk. Returns nil, nil if the file doesn't exist (not an error — just needs login).

func (*SessionStore) Save

func (s *SessionStore) Save(session *Session) error

Save encrypts and persists the session to disk.

type SessionsResponse

type SessionsResponse struct {
	Code     int          `json:"Code"`
	Sessions []VPNSession `json:"Sessions"`
}

type SmartProtocol

type SmartProtocol struct {
	WireGuard    bool `json:"WireGuard"`
	WireGuardTCP bool `json:"WireGuardTCP"`
	WireGuardTLS bool `json:"WireGuardTLS"`
}

type VPNInfo

type VPNInfo struct {
	ExpirationTime int      `json:"ExpirationTime"`
	Name           string   `json:"Name"`
	Password       string   `json:"Password"`
	GroupID        string   `json:"GroupID"`
	Status         int      `json:"Status"`
	PlanName       string   `json:"PlanName"`
	PlanTitle      string   `json:"PlanTitle"`
	MaxTier        int      `json:"MaxTier"`
	MaxConnect     int      `json:"MaxConnect"`
	Groups         []string `json:"Groups"`
}

type VPNInfoResponse

type VPNInfoResponse struct {
	Code int     `json:"Code"`
	VPN  VPNInfo `json:"VPN"`
}

type VPNSession

type VPNSession struct {
	SessionID string `json:"SessionID"`
	ExitIP    string `json:"ExitIP"`
	Protocol  string `json:"Protocol"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL