Skip to main content

Persistent Tunnels

Swarm can reuse the same public tunnel hostname for local tests instead of creating a new temporary URL on every run. Persistent tunnels are useful when your app depends on callback URLs, redirect URLs, or OAuth flows that break when the public tunnel URL changes.
Persistent tunnels are provisioned automatically after swarm login or useswarm-mcp login when your Swarm workspace has the feature enabled. If provisioning is unavailable, local tests keep using Cloudflare Quick Tunnels automatically.

How it works

When persistent tunneling is enabled, Swarm assigns one stable hostname to your user and organization:
For every local test, the CLI or MCP server:
  1. Starts Swarm’s local proxy in front of your app.
  2. Opens a Cloudflare named tunnel to that proxy.
  3. Reuses your stable hostname for the test URL.
  4. Runs Swarm’s cloud agents against that hostname.
  5. Closes the local tunnel process when the test is done.
The local proxy behavior is the same as normal local testing: split frontend/backend routing, auth-origin rewriting, and localhost URL rewriting still work.

CLI usage

Log in normally:
If your account can provision a persistent tunnel, login prints the hostname:
Run local tests the same way you already do:
When the stored persistent tunnel is available, Swarm uses it automatically.

Manual retry or revoke

Retry provisioning:
Revoke the current tunnel and clear local tunnel credentials:
After revocation, local tests fall back to temporary tunnels until you provision again.

MCP usage

The MCP package uses the same persistent tunnel flow.
Inside Claude Code, Cursor, or another MCP host, dev_test automatically uses the persistent hostname when testing localhost.

OAuth and localhost callbacks

OAuth providers often receive localhost callback URLs during development:
That URL is valid on your machine, but many providers require the exact callback origin to be allowlisted. When Swarm runs through a persistent tunnel, the browser agent rewrites common redirect params such as redirect_uri, callbackURL, redirectTo, and returnTo to your stable tunnel hostname before the provider receives the request:
Swarm also rewrites provider redirect responses that point back to localhost:
to:
The rewrite preserves the callback path, query string, and hash. Your app still receives the request through the tunnel and local proxy. If your OAuth provider requires HTTPS callback URLs and does not allow localhost, add the persistent HTTPS callback URL to the provider allowlist once:
Because the hostname is stable, you do not need to update the allowlist for every test run.
Use test OAuth apps and test accounts for Swarm runs. Do not use production credentials for automated development testing.

Split frontend/backend apps

Persistent tunnels still support split frontend/backend setups:
Swarm opens one tunnel to the local proxy. The proxy routes API paths to the backend and everything else to the frontend. Default backend paths: Use --backend-paths for custom backend prefixes.

One active session

Only one active test session can use a persistent tunnel at a time. If a second local test starts while the first still holds the tunnel, Swarm returns a clear conflict message. If a client crashes, Swarm automatically treats the lease as stale after missed heartbeats and lets the next run recover.

Troubleshooting

Login says persistent tunnel was not provisioned

Your test can still run. Swarm falls back to temporary Cloudflare Quick Tunnels. Try again later:

A second test says the tunnel is already in use

Wait for the first run to finish, call dev_close in your MCP host, or revoke and re-provision:

OAuth redirects still fail

Check which callback URL your provider receives in redirect_uri or sends back in Location. Localhost callback values are rewritten automatically. HTTPS provider allowlists still need to include the stable tunnel callback if the provider does not allow localhost redirect URIs.

Operator setup

Self-hosted or internal deployments need Cloudflare credentials on the Swarm API before users can provision persistent tunnels. See the repository operator runbook at docs/persistent-tunnels.md.