tunnelctl
CLI

CLI overview

The tunnelctl command tree, the HTTP/TCP/UDP tunnel model, and where state is stored.

tunnelctl is a single binary that authenticates you, manages tunnel reservations on the server, and runs an embedded FRP client to forward traffic. Tunnels come in three protocols — HTTP, TCP, and UDP — and the protocol is chosen by subcommand (up http, up tcp, up udp). Tunnels run in the foreground by default, or as a per-tunnel background daemon with -d.

Command tree

tunnelctl
├── login            Authenticate via your identity provider (OIDC)
│   └── --auth-flow    auto (default) | pkce-url-open | pkce-url-print | device
├── whoami           Show sign-in status & token details
│   └── --verify       Verify tokens end-to-end (IdP + API)
├── env              Show the active profile (endpoints, public domain, env overrides)

├── up               Start a tunnel (pick a protocol)
│   ├── http <slug> [target]          HTTP / HTTPS
│   │   ├── -d, --detach
│   │   ├── --host-header-rewrite STR
│   │   └── --pool-count N
│   ├── tcp <remote-port> [target]    raw TCP   [-d, --detach]
│   └── udp <remote-port> [target]    raw UDP   [-d, --detach]

├── down             Stop a tunnel
│   ├── http <slug>            │  tcp <remote-port>  │  udp <remote-port>
│   └── --all                  Stop every tunnel on this host

├── status           Tunnels running on this host
│   ├── --all          Include tunnels on other hosts (server roster)
│   └── -v, --verbose  Extra columns (mode, pid, uptime, note, version)

├── logs             View a tunnel's daemon log (pick a protocol)
│   ├── http <slug> │ tcp <remote-port> │ udp <remote-port>
│   └── -n, --tail N   |  -f, --follow

├── ports            L4 port-pool helpers
│   ├── range <tcp|udp>          Allowed range + your current usage
│   ├── check <tcp|udp> <port>   Is a port free? (exit 0/1)
│   └── pick  <tcp|udp>          Print a free port to stdout

└── slugs
    └── check <name>  Is an HTTP slug available? (exit 0/1)

Protocol is now a subcommand

Since 0.10, you always pick a protocol: tunnelctl up http myapp 8080 (not the old bare up myapp). HTTP tunnels are addressed by slug; TCP/UDP tunnels by remote port. The old tunnels list command is gone — use status --all.

Commands by area

Where state lives

tunnelctl follows platform conventions (XDG on Linux):

PathPurpose
~/.config/tunnelctl/oidc.jsonOIDC tokens (access + refresh), mode 0600
~/.config/tunnelctl/tunnels/<name>.jsonPer-tunnel metadata (connection token, serial, target, remote port)
~/.local/state/tunnelctl/<name>.logDaemon log for a detached tunnel
~/.local/state/tunnelctl/<name>.{lock,sock,pid}Daemon lock, IPC socket, PID

The <name> is the slug for HTTP tunnels, and tcp-<port> / udp-<port> for L4 tunnels.

One daemon per tunnel

Each detached tunnel is its own process with its own log and IPC socket — a crash in one tunnel never takes the others down.

On this page