English | Русский | فارسی | العربية | 中文
Fast, censorship-resistant VPN built on QUIC. Written in Rust.
Status: MVP / early alpha. Core tunneling works and is tested on macOS/Linux, but expect rough edges, breaking changes, and missing features. See Roadmap for what's planned.
Tunnels raw IP packets over QUIC DATAGRAM frames (RFC 9221) on UDP:443, TLS 1.3. The outer QUIC uses a no-op congestion controller (fixed 16 MB window) since inner TCP already handles CC. Supports multiple transport modes for different network conditions.
add-user / remove-user CLI)/metrics)up / down / status commands with IPC| Role | OS | Arch |
|---|---|---|
| Server | Linux | x86_64, arm64 |
| Client | macOS, Linux, Windows | arm64, x86_64 |
Note: The Windows client compiles and includes full wintun/routing/firewall support, but has not been tested on real hardware yet. macOS and Linux clients are fully tested.
git clone https://github.com/redpill-vpn/redpill.git
cd redpill
cargo build --release
Binaries appear in target/release/:
- redpill-server - VPN server
- redpill-client - VPN client
Optional build features:
cargo build --release --features xdp # XDP conntrack bypass (Linux server only)
cargo build --release --features acme # ACME/Let's Encrypt stub
openssl rand -hex 32
# Example output: a3f7b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0
Save this 64-character hex string - you will need it for both server and client.
# Create config directory
sudo mkdir -p /etc/redpill
# Copy example config
sudo cp config/server.example.toml /etc/redpill/server.toml
# Save the PSK
echo "YOUR_64_CHAR_HEX_PSK" | sudo tee /etc/redpill/psk
# Install binary
sudo cp target/release/redpill-server /usr/local/bin/
# Start the server
sudo redpill-server -c /etc/redpill/server.toml
Important: Edit /etc/redpill/server.toml and set nat.interface to your server's WAN interface (e.g. ens1, eth0). Find it with ip route show default.
On first run the server auto-generates a self-signed TLS certificate at the paths specified in the config (cert_file / key_file). Copy the certificate file to the client machine.
sudo redpill-client \
--server YOUR_SERVER_IP:443 \
--cert path/to/cert.pem \
--psk YOUR_64_CHAR_HEX_PSK
sudo cp config/client.example.toml ~/client.toml
# Edit ~/client.toml - set server address, cert path, and PSK
sudo redpill-client --config ~/client.toml
sudo redpill-client --server YOUR_SERVER_IP --cert cert.pem --psk <hex> --test-mode
In test mode the tunnel is established but no system routes or DNS are changed. Useful for iperf3 benchmarks or debugging.
# Should show your server's IP
curl -s https://api.ipify.org
# Ping through the tunnel
ping 10.0.1.1
Instead of a single shared PSK, you can give each user their own key.
Add users_dir to the server config:
users_dir = "/etc/redpill/users"
# Generate a random PSK for the user
openssl rand -hex 32 | sudo tee /etc/redpill/users/alice.key
Give alice her PSK and the server certificate. She connects with:
sudo redpill-client --server YOUR_SERVER_IP --cert cert.pem --psk $(cat alice.key)
sudo rm /etc/redpill/users/alice.key
sudo kill -HUP $(pidof redpill-server) # Reload without restart
ls /etc/redpill/users/
# alice.key bob.key charlie.key
Each .key file is a 64-character hex PSK. The filename (without .key) is the username shown in logs and metrics.
Backward compatibility: If
psk_fileis also set, its key is accepted as a fallback (shown as userlegacyin logs). To migrate: copy the old PSK intousers_dir/legacy.keyand removepsk_file.
RedpillVPN supports 5 transport modes. Choose the one that fits your network:
| Mode | Protocol | When to use |
|---|---|---|
quic |
Direct QUIC DATAGRAM over UDP:443 | Default. Best performance on unrestricted networks |
quic-camouflaged |
QUIC + SNI rotation + padding + browser fingerprint | Networks with light DPI (SNI-based filtering) |
tcp-reality |
TLS-over-TCP with active probe deflection | QUIC is blocked, TCP+TLS still works |
websocket |
WebSocket binary frames through a CDN | Only CDN-fronted access works |
auto |
Probes all transports, picks the best one | Recommended for censored or unknown networks |
Transport selection is done in the client config file ([transport] section):
[server]
address = "1.2.3.4:443"
cert = "cert.pem"
psk = "your-psk-hex"
[transport]
mode = "quic"
Makes your QUIC traffic look like regular browser HTTPS:
[transport]
mode = "quic-camouflaged"
[camouflage]
# Domains to rotate through (SNI field in ClientHello)
sni_pool = ["dl.google.com", "www.google.com", "fonts.gstatic.com", "www.youtube.com"]
# Pad packets to standard HTTP/3 sizes
padding = true
# Mimic a real browser's TLS fingerprint
chrome_fingerprint = true
# Browser to mimic: "chrome", "firefox", "safari", or "random"
browser_profile = "chrome"
When QUIC/UDP is completely blocked. The server accepts TCP+TLS on a separate port and deflects non-VPN probes to a real website:
Server config:
[reality]
enabled = true
listen = "0.0.0.0:8443"
target = "www.google.com:443" # Probes see this real website
Client config:
[transport]
mode = "tcp-reality"
[reality]
target = "www.google.com:443"
address = "1.2.3.4:8443" # Server's Reality port
Route traffic through a CDN (e.g. Cloudflare) to hide the server IP:
Server config:
[websocket]
enabled = true
listen = "127.0.0.1:8080" # Behind CDN reverse proxy
Client config:
[transport]
mode = "websocket"
[websocket]
url = "wss://cdn.example.com/ws"
host = "cdn.example.com"
Probes transports in priority order and picks the first one that works:
[transport]
mode = "auto"
[camouflage]
sni_pool = ["dl.google.com", "www.google.com"]
padding = true
browser_profile = "chrome"
[reality]
target = "www.google.com:443"
address = "1.2.3.4:8443"
The client tries: QUIC → QUIC Camouflaged → TCP Reality → WebSocket. If the active transport degrades, the health monitor triggers a switch.
Run the client as a background service:
# Start VPN in background
sudo redpill-client --config client.toml up
# Check status
redpill-client status
# Output:
# Redpill VPN Client
# Status: connected
# Server: 1.2.3.4:443
# Transport: QuicRaw
# Client IP: 10.0.1.2
# Uptime: 3600s
# TX: 150.3 MB (125000 pkts)
# RX: 1200.5 MB (1000000 pkts)
# Stop VPN
sudo redpill-client down
Logs are written to /tmp/redpill-client.log. The PID file is at /tmp/redpill-client.pid.
Note: Daemon mode is not available on Windows. Use
redpill-client connect(foreground) instead.
Create /etc/systemd/system/redpill-quic.service:
[Unit]
Description=RedpillVPN Server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/redpill-server -c /etc/redpill/server.toml
Restart=on-failure
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable redpill-quic
sudo systemctl start redpill-quic
# View logs
journalctl -u redpill-quic -f
Reload configuration without restarting:
sudo kill -HUP $(pidof redpill-server)
Reloadable: PSK, user keys, max_connections, decoy page, log level. Not reloadable (requires restart): listen address, TUN config, certificates, NAT interface.
curl http://127.0.0.1:9093/metrics
Available metrics:
| Metric | Type | Description |
|---|---|---|
redpill_active_sessions |
gauge | Currently connected clients |
redpill_sessions_by_user{user} |
gauge | Sessions per user |
redpill_bytes_in / _out |
counter | Total bytes received / sent |
redpill_datagrams_in / _out |
counter | Total datagrams received / sent |
redpill_handshakes_total |
counter | Total handshake attempts |
redpill_handshakes_failed |
counter | Failed handshakes (bad PSK) |
redpill_drops_backpressure |
counter | Packets dropped by backpressure |
redpill_drops_rate_limit |
counter | Packets dropped by rate limiter |
redpill_drops_stale |
counter | Stale realtime packets dropped |
redpill_rtt_ms |
histogram | Round-trip time distribution |
Build with the xdp feature for conntrack bypass on high-throughput servers:
cargo build --release --features xdp
This adds iptables -t raw -j NOTRACK rules for UDP:443, skipping connection tracking in the kernel. The rules are automatically cleaned up on graceful shutdown.
XDP also tunes socket buffers (8 MB) and disables UDP GRO for consistent latency.
Full example: config/server.example.toml
| Key | Default | Description |
|---|---|---|
listen |
0.0.0.0:443 |
Listen address (UDP) |
tun_name |
redpill1 |
TUN device name |
tun_address |
10.0.1.1 |
Server tunnel IP |
tun_prefix_len |
24 |
Subnet prefix length |
mtu |
1200 |
Initial TUN MTU (auto-updated via PMTU) |
max_connections |
64 |
Max simultaneous clients |
max_bandwidth_mbps |
0 |
Per-client bandwidth cap (0 = unlimited) |
metrics_listen |
127.0.0.1:9093 |
Prometheus metrics address |
psk_file |
- | Path to shared PSK file (single-user mode) |
users_dir |
- | Directory with per-user .key files (multi-user mode) |
cert_file |
cert.pem |
TLS certificate path |
key_file |
key.pem |
TLS private key path |
dns |
1.1.1.1 |
DNS server pushed to clients |
log_level |
info |
Log level (trace, debug, info, warn, error) |
nat.enabled |
true |
Enable NAT masquerade |
nat.interface |
ens1 |
WAN interface for NAT |
decoy.enabled |
true |
Enable HTTP/3 decoy for probe resistance |
decoy.page |
- | Path to HTML file served as decoy |
reality.enabled |
false |
Enable TCP Reality listener |
reality.listen |
0.0.0.0:8443 |
TCP Reality listen address |
reality.target |
www.google.com:443 |
Real website for probe deflection |
websocket.enabled |
false |
Enable WebSocket listener |
websocket.listen |
127.0.0.1:8080 |
WebSocket listen address |
Full example: config/client.example.toml
| Section | Key | Default | Description |
|---|---|---|---|
[server] |
address |
- | Server IP:port |
cert |
- | Path to server certificate | |
psk |
- | 64-char hex PSK | |
domain |
- | Domain for WebPKI verification (instead of cert pinning) | |
[transport] |
mode |
auto |
Transport mode (see above) |
[camouflage] |
sni_pool |
Google domains | SNI domains to rotate |
padding |
true |
Pad packets to standard size |
$ claude mcp add redpill \
-- python -m otcore.mcp_server <graph>