End-to-End Encryption Security
NFLTR provides two end-to-end encrypted tracks, TLS passthrough and P2P direct, plus a WireGuard VPN that is end to end between client and exit. Tunnel commands run in verified end-to-end encryption by default. Inspectable mode is available only as an explicit opt-in via --e2ee=false.
Tutorials: End-to-end encryption with pairing keys · Reach a private LAN over WireGuard
Encrypted Tracks
🔐 TLS Passthrough (default)
Tunnel commands run in verified mode by default. The agent generates or loads a TLS certificate and terminates TLS itself. The server routes by SNI hostname without decrypting and sees only the TLS ClientHello.
Protocols: HTTPS, gRPCS, any TLS-wrapped protocol
Cert options: Auto-generated ECDSA P-256, user-provided cert/key, or ACME/Let's Encrypt
🛡️ WireGuard VPN (nfltr wg)
Full L3 VPN tunnel using WireGuard, end to end between the client and the exit: they are WireGuard peers of each other, and the relay only forwards their sealed packets. Use the same pairing key (--e2ee-key-file) on both ends so a relay in the middle cannot swap in its own WireGuard key. Each client reaches only the exit it named in its own account.
Protocols: TCP, UDP, ICMP — any IP protocol
Features: Userspace (no root), SOCKS5 proxy, CGNAT address allocation
✦ P2P Direct (nfltr p2p)
Data flows directly between peers after HMAC-authenticated handshake. AES-256-CTR encryption with per-session keys. The server handles signaling only — data never touches the relay.
Features: File transfer, text chat, audio/video calls
NAT traversal: STUN-assisted, falls back to encrypted relay
TLS Passthrough for Tunnel Commands
Tunnel commands run with agent-terminated TLS by default. Use --e2ee=false only when you need inspectable mode for debugging.
Supported Commands
| Command | Default behavior |
|---|---|
nfltr http | Agent terminates HTTPS; share URL serves over agent's TLS cert |
nfltr tcp | TCP tunnel with agent-terminated TLS wrapping |
nfltr terminal | Browser terminal via agent's HTTPS server (xterm.js) |
nfltr shell | Embedded SSH over agent-terminated TLS |
nfltr command | Command-as-a-service over agent's HTTPS |
nfltr tail | Log tail viewer over agent's HTTPS |
nfltr watch | Live command output over agent's HTTPS |
nfltr share | Ephemeral content share over agent's HTTPS |
nfltr ai | AI session (Claude, etc.) over agent's HTTPS |
nfltr a2a | Agent-to-agent with payload encryption (default on) |
Certificate Options
# Auto-generated self-signed ECDSA P-256 cert (default verified mode)
nfltr http 3000
# User-provided certificate and key
nfltr http 3000 --e2ee-cert server.crt --e2ee-key server.key
# Let's Encrypt certificate for the share's E2EE host name
nfltr http 3000 --share
# Explicit inspectable mode for debugging
nfltr http 3000 --e2ee=false
WireGuard VPN
Full Layer 3 VPN tunnel using the WireGuard protocol. Runs in userspace (no root required) using gvisor netstack.
What the relay sees: sealed WireGuard packets, and which client connects to which exit, when, and how much. WireGuard is end to end: the client and the exit are peers of each other, and the relay forwards their packets without holding a WireGuard key, so it cannot read addresses, ports or payloads inside the tunnel. The relay does pass each side the other's public key, so a compromised relay could swap in its own key and sit in the middle; the same pairing key on both ends (--e2ee-key-file) stops that, because a relay without the secret cannot complete a WireGuard handshake. Accounts are isolated: a client reaches only the exit it named in its own account.
🔑 Curve25519 Key Exchange
The client and the exit exchange WireGuard public keys through the relay, in gRPC metadata on their authenticated sessions. The relay allocates a CGNAT IP (100.64.0.0/10) and holds no WireGuard key; with the same pairing key (--e2ee-key-file) on both ends it cannot swap in one of its own.
🌐 SOCKS5 Proxy
Connect via nfltr wg socks5 — routes TCP traffic through the WireGuard tunnel. Access remote services as if you were on the same LAN.
🚧 Allow-listed Destinations
nfltr wg serve forwards only to the destinations you pass with --allow (CIDRs, IPs or hosts, each optionally with a port) and refuses and logs everything else. --allow all is an explicit opt-in, and still excludes the exit's own loopback (127.0.0.1, ::1) unless you also list 127.0.0.1: or --allow all is an explicit opt-in.lt;port--allow all is an explicit opt-in.gt;localhost:. The agent side and --allow all is an explicit opt-in.lt;port--allow all is an explicit opt-in.gt;nfltr wg socks5 run in userspace: no sudo, no /dev/tun.
# Start WireGuard server on the agent; it forwards only to --allow
nfltr wg serve --name my-network --allow 192.168.1.0/24
# Connect from another machine via SOCKS5
nfltr wg socks5 my-network --listen :1080
# Access remote services through the tunnel
curl --proxy socks5://localhost:1080 http://192.168.1.100:8080
P2P Direct Transfer
Data flows directly between peers after an HMAC-authenticated handshake. AES-256-CTR encryption with per-session key derivation. The server handles signaling only — payload never touches the relay.
Cryptography Summary
🔐 AES-256-GCM / CTR
P2P transfers use AES-256-CTR stream encryption. A2A messaging uses AES-256-GCM authenticated encryption. Both provide confidentiality and integrity.
🔑 Per-Session Keys
Fresh keys derived for every session — X25519 ECDH for A2A, HMAC-based KDF for P2P, Curve25519 for WireGuard (end to end between client and exit). Compromising one session doesn't affect others.
🤝 HMAC Mutual Auth
P2P connections authenticate both parties via HMAC challenge-response before any data flows. Prevents man-in-the-middle attacks during handshake.
🌐 STUN NAT Traversal
P2P connections bypass the server entirely after handshake. STUN-assisted NAT traversal works across LAN, CGNAT, and hairpin NAT. Falls back to encrypted relay if direct connection fails.
🚫 No Server Session Keys
On TLS passthrough and P2P, the server never possesses session keys or plaintext data. TLS passthrough reads only SNI; P2P data bypasses the server entirely. WireGuard packets pass through the relay sealed, and it holds no WireGuard key; with a pairing key on both ends it cannot swap in one of its own. A2A messaging is covered below, with its limits.
What's Encrypted
| Track | What's E2EE | What the server sees |
|---|---|---|
| TLS Passthrough | All HTTP/TCP traffic (agent terminates TLS) | SNI hostname only — opaque TLS bytes |
| WireGuard VPN | All tunnel traffic, end to end between client and exit (against a relay that swaps keys, with a pairing key) | Sealed WireGuard packets; which client connects to which exit, timing and sizes |
| P2P Direct | File contents, chat messages, call media | Only handshake signaling — no data passes through |
| A2A Messaging | Payload bytes (AES-256-GCM by default) | Routing headers (agent IDs), timing and sizes — payload is ciphertext |
A2A messaging: what it protects, and what it does not
Agent-to-agent frames (nfltr a2a, and the hub, nodes and workers of nfltr orch) are encrypted with X25519 key agreement, a SHA-256 key derivation and AES-256-GCM, on by default (--e2ee, NFLTR_A2A_E2EE).
- A relay that only watches sees ciphertext.
- An active relay: the ephemeral keys are not signed, so a relay that swapped them could read the traffic. A pairing key (
--e2ee-key-file,NFLTR_A2A_E2EE_KEY_FILE), a random secret held only by your machines, closes this: each end proves it holds the key over both public keys before anything is sent, and a relay that swaps keys is refused. It proves the peer is one of your machines, not which one. Your account API key cannot do this job: the relay sees it on every request. - No downgrade: an end with E2EE on refuses unencrypted requests, with an error naming the flag, and never falls back itself. Exceptions, by design: messages to the relay's own agent, and relay-originated work that a worker accepts (a task POSTed to the relay's task API, a label-selector fleet invocation), which the relay sees anyway. With a pairing key, workers refuse relay-originated work too.
- Not covered: metadata: agent and machine ids, labels, timing and message sizes. A hub refuses every task request from the relay's own agent and sends the relay no summary of its runs, so the dashboard shows none; runs stay on the hub's machine (
nfltr orch hub runs; see What the relay sees).
Trust Model
NFLTR uses a trust-the-server-for-routing-only model:
- Agents authenticate to the server (API key / fleet token / mTLS)
- The server performs routing: SNI-based for TLS passthrough, IP allocation for WireGuard, signaling relay for P2P
- Subsequent data is encrypted end-to-end — the server relays opaque bytes it cannot decrypt (for A2A and WireGuard, against an active relay only with a pairing key; see above)
Tunnel commands run in end-to-end encrypted verified mode by default, where the NFLTR relay cannot read your traffic. Use --e2ee=false only when you intentionally need inspectable mode for debugging. If a CDN or reverse proxy terminates TLS in front of the relay, that edge is outside this guarantee.
Private by design
Two E2EE tracks - TLS passthrough and P2P direct - plus a WireGuard VPN, end to end between client and exit. Verified mode is the default for tunnel commands.
Install the CLI Agent-to-Agent →