Skip to content

Create a tunnel

The CLI is the primary way to run a tunnel — it both creates the local↔public forwarding session and prints your URL.

Terminal window
npx @maxoperf/tunnel http 3000
✔ guest tunnel online https://tunnel-swift-otter-7f3.maxoperf.com → http://127.0.0.1:3000
↑ sign up for a named subdomain, more tunnels, access controls & capture: https://maxoperf.com/signup
12:00:01 GET /api/users 200 12ms 1.2kB

No token, no account. A durable guest identity is saved to ~/.maxoperf/tunnel.json (file mode 0600), so re-running the command reuses the same URL.

Flag / envPurpose
--token <mptt_…> / MAXOPERF_TUNNEL_TOKENTunnel client token. Omit for a guest tunnel.
--name <name>Preferred subdomain label (informational — the URL is set server-side at create time).
--server <host:port> / MAXOPERF_TUNNEL_SERVERGateway address (default app.maxoperf.com:443, TLS auto-detected).
--api-url <url> / MAXOPERF_API_URLPlatform API origin, used for guest tunnels (default https://app.maxoperf.com).
--local-host <host>Local host to forward to (default 127.0.0.1).

Credential resolution order: --tokenMAXOPERF_TUNNEL_TOKEN → the saved config-file account token → the saved config-file guest token → mint a brand-new guest. Ctrl-C closes the session cleanly so the tunnel flips offline immediately — it does not linger as a zombie session. The CLI never prints or logs your token.

  1. Open Tunnels in the left navigation and click Create tunnel.
  2. Enter a name — the DNS label that becomes tunnel-<name>.maxoperf.com (lowercase letters, digits, hyphens; must be globally unique).
  3. Optionally configure access controls (below), then save. The tunnel is created offline — it goes live the moment a client (the CLI, or your own gRPC client) connects with its token.
  4. Reveal the tunnel’s connection token from the tunnel detail page and run the CLI (or your client) with it.
📷 Screenshot to capture — tunnels-list

What to show:MaxoPerf Tunnels list page showing tunnel rows with name, URL, online status, and a Create tunnel button

Tunnels list — each row shows the tunnel's URL and whether a client is currently connected (online).

📷 Screenshot to capture — tunnel-detail-overview

What to show:MaxoPerf tunnel detail page Overview tab showing the tunnel URL, online/offline status, and traffic statistics chart

Tunnel detail — Overview tab: status, URL, and live traffic statistics.

Terminal window
curl -X POST https://app.maxoperf.com/v1/tunnels \
-H "Authorization: Bearer mpak_example_key" \
-H "X-Account-Id: acn-1234567890" \
-H "Content-Type: application/json" \
-d '{
"name": "myapp",
"authMode": "basic",
"basicAuth": { "username": "demo", "password": "s3cret" },
"allowIps": ["203.0.113.0/24"],
"rateLimitRpm": 600,
"maxBodyBytes": 10485760,
"idleTimeoutSeconds": 3600,
"captureMode": "headers"
}'

The 201 response returns the tunnel’s URL and a one-time clientToken (mptt_…) — it is never shown again, so copy it immediately. Other routes: GET /v1/tunnels, GET /v1/tunnels/:id, PATCH /v1/tunnels/:id (any config field except name), POST /v1/tunnels/:id/stop, POST /v1/tunnels/:id/start, DELETE /v1/tunnels/:id (stopped only), POST /v1/tunnels/:id/rotate-token.

Configure these at create time or later via PATCH, from the tunnel’s Settings tab in the console:

ControlWhat it does
Auth modenone / basic / bearerRequire HTTP basic auth or a bearer token before a request reaches your local server.
Allow / deny IPsCIDR lists. An IP not in allowIps (when set) is blocked; an IP in denyIps is always blocked.
Rate limit (rateLimitRpm)Requests per minute before the tunnel starts responding 429.
Max body size (maxBodyBytes)Caps request/response body size the tunnel will proxy.
Idle timeout (idleTimeoutSeconds)How long the tunnel can sit with no traffic before it auto-stops. Default 3600s (1 hour).
Expiry (expiresAt)Optional absolute ISO-8601 timestamp after which the tunnel stops permanently.
  • Idle or TTL auto-stop reactivates on reconnect. If a tunnel stopped itself because it went idle or hit its expiry, a client reconnecting with a valid token brings it straight back online, on the same URL — no separate “start” step needed. This is what makes the guest URL genuinely stable across restarts.
  • A manual or plan-driven stop does not. If you (or MaxoPerf, for a plan-limit reason) explicitly stopped a tunnel, it stays stopped even if a client keeps retrying — you have to call POST /v1/tunnels/:id/start (or use the console) to bring it back. This distinction exists specifically to stop a client’s automatic reconnect from silently bypassing a deliberate stop or a plan cap.
  • Dropped network ≠ stopped tunnel. If your local network blips, the CLI reconnects automatically with backoff and the tunnel’s public URL keeps working once it’s back — this isn’t a “stop” at all.

Every account tunnel’s Overview tab shows a live traffic timeline: request counts, 4xx/5xx error rates, blocked-request counts, bytes in/out, and p50/p95/p99 latency. The Requests tab lists individual request events (when captureMode is headers or body) with the ability to replay a captured request straight from the console.

captureModeWhat’s recorded
none (guest default)Only aggregate stats — no per-request detail.
headersPer-request metadata + headers, but not the body.
bodyFull per-request capture including body (subject to the maxBodyBytes cap).

Signed up after starting with guest mode? Attach your running guest tunnel to your account without losing its URL:

Terminal window
curl -X POST https://app.maxoperf.com/v1/tunnels/<id>/claim \
-H "Authorization: Bearer mpak_example_key" \
-H "X-Account-Id: acn-1234567890" \
-H "Content-Type: application/json" \
-d '{ "guestToken": "mptt_g_example_guest_token" }'

The tunnel keeps its URL, moves from the guest cap to your plan’s tunnel limit, and immediately unlocks named-subdomain-style configuration (access controls, capture, idle-timeout tuning).

  • Tunnels overview — guest vs. account tunnels, what every tunnel gets.
  • Virtual services — expose a mocked dependency the same way a tunnel exposes a local port.