The tunnel CLI and its QUIC data-plane engine drive the tunnel modality. There is no typed Tunnel SDK class today.
A programmatic client, which embeds a tunnel in your own process, is Preview. See Programmatic API (Preview) at the bottom. For shipping work, use the CLI documented here.

Install

The CLI is a pure-static binary for Windows, macOS, and Linux (amd64 / arm64). It starts a separate QUIC engine for the data path. The engine ships next to the CLI, or you point at it with an env var (see Config).

Save a key

A tunnel uses an API key (rtk_…). Create the key in the Tunnel console on the API keys page, then save it on the machine:
The key is the only credential that the CLI keeps on the machine. A tunnel does not need a sign-in. For CI or a service, set TUNNEL_AUTHTOKEN instead. The CLI also reads the old RNP_AUTHTOKEN variable. tunnel login opens the browser to sign in. Use it only for key and domain commands. The CLI does not save the sign-in session.
The CLI was named rnp before. The CLI still runs when you start it as rnp, and it shows one warning line.

Command surface

The commands claim, devices, topup and logout are removed. Use tunnel domains add and tunnel keys.

http — expose a local service

The command dials the nearest edge PoP over QUIC. It forwards inbound public traffic to localhost:<local-port>. It prints the public URL and runs until you interrupt it.
int
required
The local TCP port to expose (e.g. 3000).
string
The public hostname, for example acme.rnp.sh or your own domain (tunnel.example.com). Add the domain on the Domains page of the Tunnel console, or with tunnel domains add, before you use it.

tcp, udp and connect — private raw services

tunnel tcp and tunnel udp share a raw service privately under a name. tunnel connect reaches it from another machine. The other machine uses a key of the same organization, or a key that is bound to the name.
WebSocket upgrades, server-sent events, and chunked/streaming responses pass through unchanged. Each public connection is its own QUIC stream.

p2p — direct peer-to-peer

Two peers run with the same pair-id. They discover each other through the control-plane rendezvous and connect directly. The edge brokers candidates but never carries bytes. Across NATs, set the TUNNEL_STUN env var to a reflector, so each side can gather its public candidate.
string
required
A shared secret that both peers know. It gates the direct tunnel and keys the rendezvous.
int
(share) The local service port to expose to the peer.
int
(connect) A local port to listen on. The CLI pipes connections to it through to the peer’s shared service.
string
default:"tcp"
(share) tcp, udp, or http.
string
The STUN reflector host:port for reflexive candidate gathering. You can set TUNNEL_STUN instead.

When the punch fails

Two subscribers of one carrier NAT often cannot reach each other directly. The connect side can then route through the relay:
The direct dial has a 5 second bound. After it, the CLI proxies each local connection to --relay over plain TCP. That port is the sharer’s ordinary raw TCP tunnel on the PoP.
The --relay fallback is a plain TCP proxy to a public port on the PoP. That port has no pair-token gate. Only the shared service’s own authentication protects it. Use it for a service that authenticates by itself, such as SSH. Do not use it for a policy server or a control loop. For those, use the SDK path, which keeps the QUIC connection, its certificate pin and its pair token on the relay path. See P2P on QUIC.
Endpoint-independent (cone) NATs punch through directly. Symmetric NATs fall back to the relayed path.

status

status uses the saved key. It does not need a sign-in.

Config

The CLI keeps its config file in your user config directory (mode 0600). The file holds the API key (authtoken), the control-plane base URL, and optional named tunnels for tunnel start. A flag wins over an environment variable, and an environment variable wins over the file.

Environment variables

Each variable has the TUNNEL_ prefix. The CLI also reads the old RNP_ name of each variable when the TUNNEL_ name is not set.

Data-plane engine flags

tunnel http execs the QUIC engine (tunnel-engine) with the resolved arguments. You rarely call the engine directly, but the surface is:

Remote desktop on the same transport

A remote-desktop client (a fork of an open-source remote-desktop project) runs its network transport over this same raw-QUIC relay instead of its own rendezvous/relay. To enable it, point the client’s quic-relay-server option at a host:port that runs the tunnel module:
  • The controlled box registers its device id as a slug ("proto":"quick") and parks bidi work streams.
  • The controller dials the box by slug. The edge splices the two QUIC streams.
  • The remote-desktop client’s own message framing and end-to-end encryption ride unchanged on top. The edge only secures the QUIC hop to the PoP. It never sees cleartext.
This is the remote-desktop tunnel type in the details page. You need no extra CLI beyond the relay endpoint.

Programmatic API (Preview)

Preview / forward-looking. The shipping SDK does not contain a typed Tunnel client that embeds a tunnel inside your own process. Ship with the CLI today. The shape below is an illustration, and it can change.