MCPcopy Index your code
hub / github.com/expressvpn/lightway

github.com/expressvpn/lightway @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
1,535 symbols 4,610 edges 125 files 437 documented · 28%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Lightway

Lightway is a modern VPN protocol in Rust, to deliver a VPN experience that’s faster, more secure, and more reliable.

Quick Start

Want to try Lightway VPN quickly? Download pre-built binaries from our nightly releases and follow the Quick Start Guide for step-by-step setup instructions.

Structure

This repository contains multiple crates as follows:

  • lightway-core - Core VPN protocol library
  • lightway-client - Client application
  • lightway-server - Server application

In addition there is:

  • tests - dev and e2e test infrastructure

Documentation

Protocol and design documentation can be found in the docs folder.

Supported platforms

Lightway Rust implementation currently supports Linux OS and macOS. Both x86_64 and arm64 platforms are supported and built as part of CI.

Support for other platforms will be added soon.

Development steps

Lightway core and reference applications can be built using Earthly without setting up the complete development environment locally.

Refer to Earthly documentation on how to install earthly.

earthly +build

For running unit-tests,

earthly +test

To format code:

cargo fmt

For more information about the different Earthly targets available, run:

earthly doc

Note: Lightway can also be built using standard cargo tools. To use the same cross-compilation defaults as Earthly, write this into .cargo/config.toml:

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
runner = "qemu-aarch64-static"

[target.riscv64gc-unknown-linux-gnu]
linker = "riscv64-linux-gnu-gcc"
runner = "qemu-riscv64-static -L /usr/riscv64-linux-gnu -cpu rv64"

Mobile

The android mobile library(.aar) can build by cargo make build-android or nix develop .#android -c build under project root. We rely on cargo and gradlew, so the cargo clean is not enough to clean all the artifacts, the extra gradlew clean is needed. All the artifacts (including desktop) can be clean by cargo make clean or nix develop .#android -c clean. If you want to set up the environment not from nix, you can set up cargo-make, Java, Android SDK and NDK on any Linux distro. If you need specific versions in used please check here. Besides, we are using UniFFI, and it is still possible with other approach.

Configuration

Lightway-server

Lightway-server can be configured using config-file as follows:

lightway-server --config-file './tests/server/server_config.yaml'

Example config file:

We can also override configs (except config-file), either by using env variables or by using cli arguments. Env variables should have the prefix LW_SERVER_. Cli arguments has the highest priority.

Authentication

Users are authenticated either using username and password or JWT tokens. Both schemes may be used simultaneously but each connection is authorized by one or the other (chosen by the client).

User / Password

A user database may be provided via the user_db option. This is in a format compatible with Apache htpasswd files and so can be managed using [htpasswd(1)][] or the tool of your choice.

The format is one user per line with username and password hash separated by a colon, e.g.: username:hash(password).

$ htpasswd -B -c lwpasswd my_user
New password:
Re-type new password:
Adding password for user my_user
$ cat lwpasswd
my_user:$2y$05$V6da9E.ys3QUnhgAfSGm6eM5dhkHa6Oc90kKNpb8bmcCgPsreU7Sa

Note that only the hashes supported by the [pwhash][] crate are supported and that this notably excludes the custom Apache MD5 hash which htpasswd(1) uses by default. Use an option such as -B, -2 or -5 to pick a different algorithm.

[!CAUTION] The widely used but basic htpasswd(1) username / password database format was chosen here to provide an easy-to-setup reference implementation of Lightway. Users are encouraged to modify this implementation with more advanced username / password authentication mechanisms and their own choice of password hashing algorithms to suit their security needs.

Please note that when providing env variables it should be in upper case and using "_" as a word separator, while using as cli config, it should be in lower case with "-" as the word separator.

[!CAUTION] Passing the --password option on the CLI will expose your password to other users on the system. It is recommended to provide the password via the configuration file or via LW_CLIENT_PASSWORD environment variable.

JWT Token

An RSA public key may be provided using the token_rsa_pub_key_pem option. Any JWT (see RFC 7519) with algorithm type "RS256" signed with the corresponding private key will be authorized to connect.

The token's header must consist of at least:

{
  "alg": "RS256",
  "typ": "JWT"
}

The token's payload must contain at least a valid (in the future) "exp" claim:

{
  "exp": 123456789,
}

The keys and tokens can be generated by any RSA and JWT tooling.

For example:

  • https://mkjwk.org/ can generate suitable key. Using "Show X.509" presents the public and private key in PEM format as required.
  • https://jwt.io/ can then be used to generate individual tokens. (Note that by default the "alg" field is "HS256" not "RS256" as required here.

Example:

LW_SERVER_LOG_LEVEL=trace lightway-server --config-file './tests/server/server_config.yaml'

The above command loads the config file and then overrides the log_level from env variable to trace level.

LW_SERVER_LOG_LEVEL=trace lightway-server --config-file './tests/server/server_config.yaml' --log-level=off

The above command loads the config file and then overrides the log_level from cli args to off level. Since cli arguments have the highest priority, env variable config will be ignored.

Lightway-client

Lightway-client can also be configured using config-file similar to Lightway-server as follows:

lightway-client --config-file './tests/client/client_config.yaml'

Example config file:

Lightway-client also supports overriding the config using either env variables or cli arguments. Env variables should have the prefix LW_CLIENT_.

By default the client will use the existing MTU on the tunnel device, this can be overridden with the --inside-mtu option but note that this requires additional privileges, specifically the CAP_SYS_ADMIN capability.

Running the client on linux platforms with dns_config_mode: default will require CAP_DAC_OVERRIDE and CAP_FOWNER permissions, to properly modify resolv.conf.

[!CAUTION] Passing the --password option on the CLI will expose your password to other users on the system. It is recommended to provide the password via the configuration file or via LW_CLIENT_PASSWORD environment variable.

[!Note] macOs has restrictions on tunnel name. It will only allow the format utun[0-9]+. To avoid guessing the available number, do not provide tun_name config and let the system decide the tunnel name.

E2E Testing

To run all e2e tests:

earthly --allow-privileged +e2e

Or to run a single e2e test:

earthly --allow-privileged ./tests+run-tcp-test

Check tests/Earthfile or earthly doc ./tests for other run-*-test targets.

To start the stack for your own testing:

earthly -P ./tests/+save-all-test-containers  && SERVER_ARGS="--config-file server_config.yaml" CLIENT_ARGS="--config-file client_config.yaml" docker compose -f tests/e2e/docker-compose.yml up

Then you can use e.g.

docker compose -f tests/e2e/docker-compose.yml exec client bash

To run things within the containers

Nix flake

Lightway can be built and tested using Nix commands:

nix build .#lightway-server
nix build .#lightway-client

To run the server or client:

nix run .#lightway-server -- -c config.yaml
nix run .#lightway-client -- -c config.yaml

Both Linux and macOS are supported.

Available Build Targets

The flake provides multiple build variants using a consistent platform-libc naming convention: lightway-{client,server}-{arch}-{os}-{libc}[-variant]

Convenience Aliases (recommended for most users)

# Short names that automatically select the right package for your platform
nix build .#lightway-client        # Native build for current platform
nix build .#lightway-server        # Native build for current platform
nix build .#lightway-client-msrv   # MSRV build for current platform
nix build .#lightway-server-msrv   # MSRV build for current platform

Native Linux Builds (x86_64-linux and aarch64-linux)

# Dynamic glibc builds (latest Rust)
nix build .#lightway-client-x86_64-linux-gnu
nix build .#lightway-server-x86_64-linux-gnu
nix build .#lightway-client-aarch64-linux-gnu
nix build .#lightway-server-aarch64-linux-gnu

# MSRV builds
nix build .#lightway-client-x86_64-linux-gnu-msrv
nix build .#lightway-server-aarch64-linux-gnu-msrv

# Static musl builds (no external dependencies)
nix build .#lightway-client-x86_64-linux-musl
nix build .#lightway-server-x86_64-linux-musl
nix build .#lightway-client-aarch64-linux-musl
nix build .#lightway-server-aarch64-linux-musl

Native macOS Builds (aarch64-darwin / Apple Silicon)

# Native macOS builds (latest Rust)
nix build .#lightway-client-aarch64-darwin
nix build .#lightway-server-aarch64-darwin

# MSRV builds
nix build .#lightway-client-aarch64-darwin-msrv
nix build .#lightway-server-aarch64-darwin-msrv

Cross-Compilation Targets

Linux Cross-Compilation

All platforms can cross-compile to Linux targets. These use Rust target triple naming (e.g., aarch64-unknown-linux-musl):

# From any platform, build x86_64 Linux targets
nix build .#lightway-client-x86_64-linux-gnu    # dynamic glibc
nix build .#lightway-client-x86_64-linux-musl   # static musl

# From any platform, build aarch64 Linux targets
nix build .#lightway-client-aarch64-linux-gnu   # dynamic glibc
nix build .#lightway-client-aarch64-linux-musl  # static musl
Darwin Cross-Compilation (aarch64-darwin only)

Apple Silicon Macs can cross-compile to Intel Mac (x86_64-darwin):

# From Apple Silicon, build Intel Mac binaries
nix build .#lightway-client-x86_64-darwin
nix build .#lightway-server-x86_64-darwin

Use cases for cross-compilation: - CI/CD pipelines building for multiple architectures from a single runner - Building ARM binaries on x86_64 hardware (and vice versa) - Creating static binaries for containerized deployments on macOS - Testing builds for different architectures without native hardware

Note: All -musl builds produce static-pie linked binaries with no external dependencies

Development environment

Set up a local development environment using the Nix flake:

nix develop
cargo build --bin lightway-server

This installs all necessary tools required to develop Lightway.

Development shells are available for different use cases:

# Default development shell (stable Rust)
nix develop

# Nightly Rust toolchain (needed for fuzzing)
nix develop .#nightly

# MSRV toolchain for compatibility testing
nix develop .#msrv

# Musl development shell (Linux only, for static builds)
nix develop .#musl

The musl development shell automatically configures the build environment for static musl compilation.

Contributing

We appreciate feedback and contribution to this repository! Before you get started, please see link:

CONTRIBUTING

Reporting a vulnerability

To report security vulnerabilities, please see section on link:

Reporting a vulnerability

Dev-Testing

For running both client and server in the same machine and test end to end, follow these steps:

export LW_DANGEROUSLY_DISABLE_PERMISSIONS_CHECKS=1

Configuration files must be trustworthy (per [fs-mistrust's definition][]) but many path elements are owned by the user while the tests are run as root via sudo. For dev test we disable those checks.

Then:

sudo ./tests/setup.sh

The above script by default creates four network namespaces: - lightway-server - lightway-middle - lightway-client - lightway-remote

The lightway-remote namespace simulates "The Internet". Run any services which you'd like the client to access over the tunnel here.

The lightway-middle namespace facilitates a multi-hop network path: client <-> middle <-> server. Settings can be modified in the middle namespace to simulate interesting network conditions (e.g. lower path MTU, see below)

Start server using this command,

cargo build --bin lightway-server && sudo -E ip netns exec lightway-server ./target/debug/lightway-server --config-file './tests/server/server_config.yaml'

Start client using this command,

cargo build --bin lightway-client && sudo -E ip netns exec lightway-client ./target/debug/lightway-client --config-file './tests/client/client_config.yaml'

Then enter into lightway-client namespace and trying pinging google.com

sudo ip netns exec lightway-client bash
ping google.com -c 3

Verify wan interface in lightway-remote namespace receiving the packet and replying: ```bash sudo ip netns exec lightway-remote bash tcpdump -i wan

Extension points exported contracts — how you extend this code

DplpmtudTickable (Interface)
Get a suitable `lightway_core::Connection` on which to call `pmtud_tick`. [6 implementers]
lightway-app-utils/src/dplpmtud_timer.rs
OutsideIOSendCallback (Interface)
Application provided callback used to send outside data. [7 implementers]
lightway-core/src/io.rs
DnsBackend (Interface)
Trait implemented by each DNS backend. [3 implementers]
lightway-client/src/platform/linux/dns_manager.rs
ServerBatchRecvSyscall (Interface)
Platform-specific batch receive syscall. [2 implementers]
lightway-server/src/io/outside/udp/batch_receive.rs
TunAdapter (Interface)
(no doc) [3 implementers]
lightway-app-utils/examples/udprelay.rs
TestSock (Interface)
(no doc) [2 implementers]
lightway-core/tests/connection.rs
Tickable (Interface)
Get a suitable `lightway_core::Connection` on which to call `tick`. [8 implementers]
lightway-app-utils/src/connection_ticker.rs
Plugin (Interface)
Lightway Plugin trait for inside and outside packets These are the hooks which will be installed at the ingress and egr [4 …
lightway-core/src/plugin.rs

Core symbols most depended-on inside this repo

unwrap
called by 472
lightway-core/src/connection/fragment_map.rs
len
called by 109
lightway-core/src/borrowed_bytesmut.rs
as_ref
called by 71
lightway-server/src/io/outside/udp/cmsg.rs
as_borrowed_bytesmut
called by 51
lightway-core/src/borrowed_bytesmut.rs
iter
called by 49
lightway-server/src/io/outside/udp/cmsg.rs
expect_pending
called by 41
lightway-core/src/connection/dplpmtud.rs
len
called by 36
lightway-client/src/config.rs
put
called by 35
lightway-core/src/connection/io_adapter.rs

Shape

Method 678
Function 570
Class 173
Enum 77
Interface 37

Languages

Rust100%

Modules by API surface

lightway-core/src/connection.rs79 symbols
lightway-core/src/connection/dplpmtud.rs65 symbols
lightway-core/src/wire/expresslane_data.rs53 symbols
lightway-core/src/connection/io_adapter.rs53 symbols
lightway-core/tests/connection.rs48 symbols
lightway-client/src/lib.rs45 symbols
lightway-server/src/metrics.rs41 symbols
lightway-client/src/route_manager.rs41 symbols
lightway-client/src/keepalive.rs36 symbols
lightway-server/src/connection_manager.rs31 symbols
lightway-core/src/gso.rs30 symbols
lightway-core/src/context.rs30 symbols

For agents

$ claude mcp add lightway \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact