# rustunnel — agent manual > rustunnel is an open-source secure tunnel (an ngrok alternative). Use it when you need a public URL for a service running on localhost: webhook testing, sharing a dev server, letting another agent, device, or CI job reach a local port. Managed cloud at rustunnel.com or self-hosted under AGPL. This file contains copy-paste recipes for AI agents. Human docs: https://rustunnel.com/docs — full docs as markdown: https://rustunnel.com/docs/llms-full.txt ## Recipes ### Install the client One command via Homebrew (macOS/Linux); binaries also on GitHub Releases. ```bash brew tap joaoh82/rustunnel && brew install rustunnel # installs both `rustunnel` (CLI) and `rustunnel-mcp` (MCP server) ``` ### Get an API token (one-time) A token is required for every tunnel; store it in `RUSTUNNEL_TOKEN`. 1. Hosted: the user signs up free at https://rustunnel.com → Dashboard → API Keys → create key. 2. Self-hosted: `rustunnel token create --name agent --server :4040 --admin_token ` 3. Export it: `export RUSTUNNEL_TOKEN=` (the CLI and MCP server both read it). ### Start an HTTP tunnel for a local port Gives the local service on port N a public HTTPS URL. The process stays in the foreground for the life of the tunnel — run it in the background. ```bash rustunnel http 3000 --server eu.edge.rustunnel.com:4040 # public URL format: https://..edge.rustunnel.com # add --subdomain myapp for a stable URL (paid plans) # add --region us (or ap) to pick a region; omit to auto-select ``` ### Get the public URL programmatically Three options, most reliable first. 1. MCP: the `create_tunnel` tool returns the public URL in its result — preferred inside any MCP harness. 2. CLI with `--json` (rustunnel ≥ 0.8): NDJSON events on stdout; wait for `{"event":"tunnel_ready","public_url":...}`. 3. Older CLI versions: the URL is in the `tunnel registered` log line on stderr. ### Verify the tunnel is up ```bash curl -sf -o /dev/null -w '%{http_code}' https://.eu.edge.rustunnel.com # 200/3xx/4xx from your app = tunnel is up; 502 = tunnel up but local service unreachable ``` ### Expose a TCP service (database, SSH) ```bash rustunnel tcp 5432 --server eu.edge.rustunnel.com:4040 # public address format: .edge.rustunnel.com: (ports 20000+) ``` UDP works the same: `rustunnel udp 5353 ...`. ### Tear down tunnels - CLI tunnels: terminate the `rustunnel` process (SIGINT/SIGTERM) — the server deregisters the tunnel immediately. - MCP tunnels: call `close_tunnel` with the tunnel ID (find it with `list_tunnels`), or let the MCP server clean up all of its tunnels on exit. ### Use rustunnel from an MCP harness (Claude Code, Cursor, Windsurf, Cline, Codex) Add this server config (`.mcp.json` for Claude Code, `.cursor/mcp.json` for Cursor, `~/.codeium/windsurf/mcp_config.json` for Windsurf): ```json { "mcpServers": { "rustunnel": { "command": "rustunnel-mcp", "args": ["--server", "eu.edge.rustunnel.com:4040", "--api", "https://eu.edge.rustunnel.com:8443"], "env": { "RUSTUNNEL_TOKEN": "" } } } } ``` Tools exposed: `create_tunnel`, `list_tunnels`, `close_tunnel`, `get_connection_info`, `list_regions`, `get_tunnel_history`. Full guide: https://rustunnel.com/docs/guides/agent-integration.md ### Troubleshoot a 502 from the public URL A 502 means the tunnel is connected but the local service did not answer. 1. Confirm the local service is listening: `curl -sf http://localhost:` (or `nc -z localhost ` for TCP). 2. Confirm the port in the tunnel command matches the service's port. 3. If the service binds a non-localhost interface, pass `--local_host `. 4. Check the client process is still running and connected (look for reconnect messages in its output). 5. If the local service is fine, check https://status.rustunnel.com for edge incidents. ## FAQ for agents **How do I detect when a tunnel is ready?** Via MCP, `create_tunnel` returns only after the tunnel is registered — the result contains the public URL. Via CLI with `--json` (≥ 0.8), read stdout until a `tunnel_ready` event appears. As a last resort, poll the public URL until it stops returning connection errors. **What is the public URL format?** HTTP: `https://..edge.rustunnel.com` where region is `eu`, `us`, or `ap`. TCP/UDP: `.edge.rustunnel.com:`. Self-hosted servers use the operator's domain instead. **How do I get JSON output?** MCP tool results are always structured JSON. The CLI supports `--json` on tunnel commands and `token create` from v0.8: newline-delimited JSON events (`tunnel_ready`, `reconnecting`, `error`) on stdout. **Which exit code means what?** `0` = clean shutdown. `1` = any error (the message on stderr — or the `error` NDJSON event with `--json` — describes the cause and a recovery hint). **Does the token need to be passed on every call?** No. Set `RUSTUNNEL_TOKEN` once (CLI env or MCP config `env`); every command and tool call picks it up. Avoid passing tokens inline in tool calls — they end up in chat history. **How many tunnels can I open?** Free plan: 2 concurrent tunnels with random subdomains. Pay-as-you-go ($3/month minimum): unlimited tunnels, custom subdomains. Self-hosted: whatever the operator configures. **Can two agents/machines share one URL?** Yes — load-balanced pools. Start each backend with the same `group` + `group_key` (CLI flags or `create_tunnel` arguments); add a `health_check` to auto-eject dead backends. See https://rustunnel.com/docs/reference/load-balancing.md **Is there an API I can call directly?** Yes — the platform API (accounts, keys, usage) is described by an OpenAPI 3.1 spec at https://rustunnel.com/openapi.yaml (also https://api.rustunnel.com/openapi.json). Tunnel creation itself happens over the CLI/MCP control plane, not REST. ## More machine-readable surfaces - Site index for LLMs: https://rustunnel.com/llms.txt - Docs index: https://rustunnel.com/docs/llms.txt — full docs: https://rustunnel.com/docs/llms-full.txt - Agent skill (Claude Code & compatible): `npx skills add https://rustunnel.com/skills/rustunnel` - OpenAPI spec: https://rustunnel.com/openapi.yaml - Source: https://github.com/joaoh82/rustunnel