Lightway is a modern VPN protocol in Rust, to deliver a VPN experience that’s faster, more secure, and more reliable.
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.
This repository contains multiple crates as follows:
In addition there is:
Protocol and design documentation can be found in the
docs folder.
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.
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"
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.
Lightway-server can be configured using config-file as follows:
lightway-server --config-file './tests/server/server_config.yaml'
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.
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).
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
--passwordoption 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 viaLW_CLIENT_PASSWORDenvironment variable.
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:
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 can also be configured using config-file similar to Lightway-server as follows:
lightway-client --config-file './tests/client/client_config.yaml'
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
--passwordoption 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 viaLW_CLIENT_PASSWORDenvironment 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 providetun_nameconfig and let the system decide the tunnel name.
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
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.
The flake provides multiple build variants using a consistent platform-libc naming convention:
lightway-{client,server}-{arch}-{os}-{libc}[-variant]
# 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
# 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 (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
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
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
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.
We appreciate feedback and contribution to this repository! Before you get started, please see link:
To report security vulnerabilities, please see section on link:
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
$ claude mcp add lightway \
-- python -m otcore.mcp_server <graph>