Where to Find the Client-Side Code for Managing Connection States in Amnezia VPN

The client-side code for managing connection states in the Amnezia VPN client is located primarily in client/core/controllers/connectionController.cpp for high-level state orchestration and client/vpnConnection.cpp for low-level protocol implementation.

The Amnezia VPN client relies on a two-layer architecture to manage VPN connection states, bridging the Qt-based UI with native protocol implementations. Understanding this architecture is essential for developers contributing to the amnezia-vpn/amnezia-client repository or troubleshooting connection issues. This guide maps the specific source files and function calls that handle state transitions, from UI requests to wire-level protocol changes.

High-Level State Orchestration in ConnectionController

The ConnectionController class serves as the primary interface between the UI and the VPN engine. Located in client/core/controllers/connectionController.h and client/core/controllers/connectionController.cpp, this controller exposes Qt-friendly signals including openConnectionRequested, closeConnectionRequested, and connectionStateChanged.

This layer performs platform validation to determine if a connection is allowed before delegating work to the low-level engine. It maintains a clean abstraction so that UI code never directly manipulates protocol objects.

Low-Level Protocol Handling in VpnConnection

The VpnConnection class implements the actual VPN protocols and tracks the definitive connection state. Found in client/vpnConnection.h and client/vpnConnection.cpp, this layer maintains the m_connectionState member of type Vpn::ConnectionState and emits connectionStateChanged signals whenever the underlying protocol reports a transition.

Beyond state tracking, VpnConnection handles routing updates, kill-switch configuration, and DNS flushing when states change. It communicates with platform-specific backends via IpcClient on desktop or native controllers on iOS/macOS.

How the Architecture Propagates State Changes

Signal Flow from Engine to UI

During construction, ConnectionController subscribes to engine signals to propagate state changes upward:

connect(m_vpnConnection, &VpnConnection::connectionStateChanged,
        this, &ConnectionController::connectionStateChanged);

This connection ensures that any state change reported by the VPN engine is instantly broadcast to the UI layer through the controller's connectionStateChanged signal.

Querying Connection Status

The controller provides a convenience method for checking the current state without exposing internal engine details:

bool ConnectionController::isConnected() const {
    return m_vpnConnection &&
           m_vpnConnection->connectionState() == Vpn::ConnectionState::Connected;
}

UI components throughout the application use this isConnected() helper to determine interface states.

Handling Connection Requests

State transitions originate from UI actions bound to the controller's slots:

  • Open connection: openConnectionRequested triggers VpnConnection::connectToVpn
  • Close connection: closeConnectionRequested triggers VpnConnection::disconnectFromVpn
  • Kill-switch toggle: killSwitchModeChangedRequested forwards to VpnConnection::onKillSwitchModeChanged

These connections typically use Qt::QueuedConnection to prevent blocking the UI thread during network operations.

Inside the VpnConnection State Machine

When the underlying protocol reports a change, VpnConnection::onConnectionStateChanged executes platform-specific cleanup and setup operations. This includes flushing the DNS cache, configuring split-tunneling rules, and updating the kill-switch status.

Concrete protocol implementations inherit from the abstract base defined in client/core/protocols/vpnProtocol.h. Specific implementations include:

These protocol objects report their status back to VpnConnection, which then updates the canonical m_connectionState and emits the change notification.

Practical Example: Opening a Connection

To initiate a VPN connection from the UI layer:

QString serverId = "my-server-id";
DockerContainer container = DockerContainer::WireGuard;
QJsonObject config = {/* … VPN config generated by the configurators … */};

emit connectionController->openConnectionRequested(serverId, container, config);

Practical Example: Reacting to State Changes in QML

connect(connectionController, &ConnectionController::connectionStateChanged,
        this, [&](Vpn::ConnectionState state) {
    if (state == Vpn::ConnectionState::Connected) {
        statusLabel->setText("Connected");
    } else if (state == Vpn::ConnectionState::Disconnected) {
        statusLabel->setText("Disconnected");
    }
});

Practical Example: Querying Current Status

bool connected = connectionController->isConnected();
qDebug() << "VPN is currently" << (connected ? "online" : "offline");

Summary

  • ConnectionController (client/core/controllers/connectionController.cpp) provides the high-level Qt API for connection state management and platform validation.
  • VpnConnection (client/vpnConnection.cpp) implements the low-level protocol logic, maintains the canonical Vpn::ConnectionState, and handles network routing.
  • Communication between layers occurs through Qt signals and slots, with connectionStateChanged propagating updates from the engine to the UI.
  • Protocol-specific implementations in client/core/protocols/ inherit from vpnProtocol.h and report status to VpnConnection.
  • The isConnected() convenience method offers a simple boolean check for UI components.

Frequently Asked Questions

Where is the connection state enum defined in the Amnezia VPN client?

The Vpn::ConnectionState enum is defined in the VpnConnection header file within client/vpnConnection.h. This enum represents all possible states including Connected, Disconnected, and intermediate states during connection establishment.

How does the UI receive real-time connection state updates?

The UI receives updates through Qt's signal-slot mechanism. The ConnectionController emits connectionStateChanged(Vpn::ConnectionState) whenever the underlying VpnConnection reports a state change, allowing QML and QWidget interfaces to react immediately without polling.

What handles the kill-switch when the connection state changes?

The VpnConnection class manages kill-switch updates within its onConnectionStateChanged handler. When the state transitions, it invokes platform-specific logic via IpcClient on desktop or native controllers on mobile platforms to ensure the kill-switch rules align with the current connection status.

Why are there separate layers for connection control and VPN implementation?

The separation allows the ConnectionController to focus on UI coordination and platform policy checks while VpnConnection handles the complexity of protocol-specific implementations. This architecture isolates protocol differences (OpenVPN vs. WireGuard) from the user interface, enabling both layers to be tested and maintained independently.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →