LPM CLI

lpm tunnel

Expose a local HTTP service through a public HTTPS URL, with request capture and replay.

lpm tunnel gives a public HTTPS URL to a local HTTP service.

Use it for webhook development, API callbacks, remote-device tests, and temporary previews.

For a repeatable project workflow, set a claimed domain in lpm.json and run lpm dev.

When a service already runs or you manage capture history, use standalone lpm tunnel.

lpm tunnel [<port>|<action>] [target] [flags]

Quickstart with lpm.json

When a project needs the same public URL across development sessions, use lpm.json.

First, claim the domain once:

lpm login
lpm tunnel claim acme-api.lpm.llc

Then add the domain to lpm.json, next to package.json:

lpm.json
{
  "$schema": "https://cli.lpm.dev/schemas/lpm.json",
  "tunnel": {
    "domain": "acme-api.lpm.llc"
  }
}

Start the project:

lpm dev

The domain field enables the tunnel automatically. You do not need to add --tunnel.

lpm dev starts the project and waits for its final HTTP endpoint. Then it opens the tunnel to that endpoint.

If services is absent, lpm dev runs the dev script from package.json.

Use this setup for stable webhook URLs, callback URLs, and preview links.

The tunnel value is an object, not a boolean value. The object currently accepts only domain.

For an assigned domain instead of a saved domain, omit the tunnel object:

lpm dev --tunnel

Free users receive a random lpm.fyi domain. A claimed domain requires a Pro or Org plan.

Override or disable the project tunnel

The lpm dev flags override lpm.json for one run:

lpm dev --domain preview-api.lpm.llc # use another claimed domain and enable the tunnel
lpm dev --no-tunnel                 # disable the configured tunnel for this run

The --domain flag belongs to lpm dev. Standalone lpm tunnel accepts the domain as a positional argument.

Tunnel access and inspector flags also work with lpm dev:

lpm dev --tunnel-auth               # protect the public URL with a session token
lpm dev --inspect-port 4500         # use a fixed browser-inspector port
lpm dev --no-inspect                # keep capture but skip the browser inspector

Multi-service projects

If lpm.json contains one service, LPM CLI uses that service as the tunnel target.

If it contains multiple services, mark exactly one service as primary:

lpm.json
{
  "$schema": "https://cli.lpm.dev/schemas/lpm.json",
  "tunnel": {
    "domain": "acme-api.lpm.llc"
  },
  "services": {
    "web": {
      "command": "vite",
      "port": 5173,
      "primary": true
    },
    "api": {
      "command": "node api.js",
      "port": 4000
    }
  }
}
lpm dev

The public URL routes to the final endpoint of web. The api service remains available to the project on its local endpoint.

See the lpm.json tunnel reference for the field definition.

One-time tunnel

Start your local HTTP service on port 3000. Then open the tunnel:

lpm login
lpm tunnel 3000

The command prints a public URL and forwards requests to http://127.0.0.1:3000/.

While you use the URL, keep the command open. Press q or Ctrl+C to stop the tunnel.

Examples

lpm dev                                   # use tunnel.domain from lpm.json
lpm dev --tunnel                          # use an assigned domain for this run
lpm dev --no-tunnel                       # skip the lpm.json tunnel for this run
lpm tunnel                                # tunnel the only active lpm dev endpoint
lpm tunnel 4000                           # tunnel an explicit local port
lpm tunnel start acme-api.lpm.llc         # tunnel the active dev endpoint with a claimed domain
lpm tunnel 4000 acme-api.lpm.llc          # use a claimed domain
lpm tunnel claim acme-api.lpm.llc         # claim a personal domain
lpm tunnel claim staging.lpm.fyi --org acme # claim an organization domain
lpm tunnel unclaim acme-api.lpm.llc       # release a claimed domain
lpm tunnel list                           # list personal domain claims
lpm tunnel list --org acme                # list organization domain claims
lpm tunnel domains                        # list available base domains
lpm tunnel inspect --last 10              # show recent captured requests
lpm tunnel inspect --ui                   # open the browser inspector
lpm tunnel replay --last --port 4000      # replay the newest capture
lpm tunnel log --status 5xx               # show failed requests

Choose a command

GoalCommand
Start the tunnel from lpm.jsonlpm dev
Start the project with an assigned domainlpm dev --tunnel
Disable the lpm.json tunnel for one runlpm dev --no-tunnel
Tunnel the only active lpm dev endpointlpm tunnel
Tunnel an explicit portlpm tunnel 3000
Tunnel the active endpoint with a claimed domainlpm tunnel start acme-api.lpm.llc
Tunnel with a claimed domainlpm tunnel 3000 acme-api.lpm.llc
Claim a stable domainlpm tunnel claim <domain>
Release a claimed domainlpm tunnel unclaim <domain>
List claimed domainslpm tunnel list
List available base domainslpm tunnel domains
Show captured requestslpm tunnel inspect
Replay a captured requestlpm tunnel replay <number>
Browse or clear capture historylpm tunnel log

The CLI also accepts release, ls, and logs as aliases.

Authentication

The remote actions require a saved login session from lpm login.

This requirement applies to tunnel startup, domain claims, domain lists, and base-domain lists.

lpm login
lpm tunnel 3000

The remote actions do not accept --token, LPM_TOKEN, or CI-only tokens.

When you revoke a CLI session in the registry dashboard, its active tunnels close after the registry processes the revocation. Run lpm login again before restarting the tunnel.

LPM CLI renews the saved session credential before it expires. Renewal keeps the same public URL, active connections, and original Free-session deadline. If renewal fails until the credential expires, the connection closes. A later reconnection can receive a new Free URL. Older relay versions refresh credentials through reconnection, which can close active requests and WebSockets.

The inspect, replay, and log actions use local files. These actions do not require a login.

Select the local service

Use an explicit port

If you know the local listener, pass its port:

lpm tunnel 3000

An explicit port always targets http://127.0.0.1:<port>/. Valid ports are from 1 through 65535.

The local service must use plain HTTP. The public tunnel URL still uses HTTPS.

If your framework uses HTTPS, disable framework HTTPS. Use lpm dev --https for trusted local browser TLS.

Use an active lpm dev endpoint

Run these commands in different terminals:

# Terminal 1
lpm dev

# Terminal 2
lpm tunnel

Without a port, LPM CLI selects the only active lpm dev endpoint. It preserves the endpoint address, port, and base path.

If no active endpoint exists, pass a port. If multiple endpoints exist, pass the intended port.

Use lpm dev --tunnel to start the dev server and tunnel from one command.

The tunnel forwards HTTP requests and WebSocket connections. Empty WebSocket close frames remain empty, and normal close codes and reasons pass through.

Recipes

Receive webhooks on localhost

Run your webhook service and tunnel in different terminals:

# Terminal 1
lpm run dev

# Terminal 2
lpm tunnel 3000 --session stripe-test

Add the webhook path to the printed URL. For example, use https://<domain>/webhooks/stripe for a /webhooks/stripe route.

LPM CLI saves each request and local response in the current directory. The session name helps you find related requests later.

Replay a failed webhook

List recent captures. Then inspect and replay the selected entry:

lpm tunnel inspect --last 10
lpm tunnel inspect --detail 3
lpm tunnel replay 3 --port 3000

Capture numbers use the newest-first list and start at 1.

Replay sends the original method, path, headers, and body to the selected local service.

If one lpm dev endpoint is active, you can omit the replay port:

lpm tunnel replay --last

Keep a webhook endpoint active

When the tunnel must accept requests without the local service, use --auto-ack:

lpm tunnel 3000 --auto-ack

When LPM CLI cannot reach the local service, it returns 200 OK. It still saves the request for later inspection and replay.

CAUTION: The provider records the request as successful, although your local service did not process it.

Protect a preview URL

Pro and Org tunnels can require a per-session access token:

lpm tunnel 3000 acme-api.lpm.llc --tunnel-auth

The startup output prints an X-Tunnel-Auth header and a browser URL. Treat the token and browser URL as secrets.

Use a fixed inspector port

The browser inspector starts with each tunnel and selects an available local port.

Use a fixed port for a stable local bookmark:

lpm tunnel 3000 --inspect-port 4500

If port 4500 is busy, the command fails. Without this flag, an inspector startup error does not stop the tunnel.

To run without the browser inspector, use:

lpm tunnel 3000 --no-inspect

When --no-inspect is active, capture continues.

Stable domains

Free tunnels receive a random lpm.fyi domain for each session. Stable domain claims require a Pro or Org plan.

List the enabled base domains before you select a name:

lpm tunnel domains

Current base domains are:

  • lpm.fyi for Free, Pro, and Org tunnels.
  • lpm.llc for Pro and Org tunnels.

A Free tunnel can use lpm.fyi for its random domain. A stable claim on either base still requires Pro or Org.

Claim and use a personal domain:

lpm tunnel claim acme-api.lpm.llc
lpm tunnel list
lpm tunnel 3000 acme-api.lpm.llc

Organization owners and administrators can manage organization domains:

lpm tunnel claim staging.lpm.fyi --org acme
lpm tunnel list --org acme
lpm tunnel 3000 staging.lpm.fyi

The subdomain must contain from 3 through 32 lowercase letters, numbers, or hyphens. It must start and end with a letter or number.

When you no longer need the claim, release the domain:

lpm tunnel unclaim acme-api.lpm.llc
lpm tunnel unclaim staging.lpm.fyi --org acme

The release is immediate and does not stop for confirmation.

Plans and limits

Tunnel typePublic domainSession limitConcurrent tunnelsStable claimsTunnel auth
Free personalRandom lpm.fyi1 hour10No
Pro personalAccount or claimed domainNo fixed limit33Yes
OrgClaimed organization domainNo fixed limit1010Yes

The startup output shows the current plan, expiry, active limits, and request usage.

Request allowances and rates apply across the account, not to each tunnel. See Request usage and overage.

Capture history

LPM CLI stores captured requests, responses, and sessions in .lpm/inspector.db under the current directory.

Captured data can include request bodies, response bodies, cookies, authorization headers, and signature headers.

Add .lpm/inspector.db* to .gitignore so that Git does not track the database or its temporary files.

Run tunnel history commands from the same directory that started the tunnel. This keeps each project history separate.

Inspect captures

lpm tunnel inspect                       # show the newest 20 captures
lpm tunnel inspect --last 10
lpm tunnel inspect --detail 3            # show request and response details
lpm tunnel inspect --filter stripe       # filter by detected provider
lpm tunnel inspect --status 4xx          # filter by HTTP status class
lpm tunnel inspect --status 404          # filter by exact status

The status filter accepts 2xx, 3xx, 4xx, 5xx, error, err, or an HTTP status from 100 through 599.

Open the browser inspector

lpm tunnel inspect --ui
lpm tunnel inspect --ui --inspect-port 4500

This action opens the history for the current directory. Press Ctrl+C to stop the inspector.

When a tunnel is active, press o in its terminal to open the same inspector.

Browse or clear the log

lpm tunnel log                           # show the newest 50 captures
lpm tunnel log --last 10
lpm tunnel log --filter github
lpm tunnel log --status 5xx
lpm tunnel log --clear

CAUTION: lpm tunnel log --clear deletes captured requests and completed sessions for the current directory.

An active session remains available for new captures after a clear operation.

JSON and scripts

Use --json with local history actions or domain management:

lpm tunnel list --json
lpm tunnel inspect --last 10 --json
lpm tunnel replay --last --port 3000 --json

Local history actions work in non-interactive scripts without a login.

A remote tunnel still requires a saved interactive login session. The tunnel process also remains open until you stop it.

With --json, tunnel startup emits a JSON object after each successful connection. A temporary connection failure emits a retry object before the delay:

{
  "schema_version": 1,
  "success": false,
  "event": "retry",
  "error_code": "tunnel_retry",
  "error": "disconnected, retrying in 2s... (relay unavailable)",
  "retrying": true
}

Read this output as a sequence of JSON objects. A retry event does not mean that the process exited. After a permanent failure or exhausted retries, the command emits its final error and exits with a nonzero status.

Flags

FlagApplies toEffect
--org <SLUG>claim, unclaim, listManage domains for an organization.
--tunnel-authStartRequire the generated access token. Pro and Org only.
--auto-ackStartProvide a 200 OK fallback for an unavailable local service.
--session <NAME>StartName the capture session.
--no-inspectStartDo not start the browser inspector. Capture continues.
--inspect-port <PORT>Start, inspect --uiBind the inspector to an exact port.
--uiinspectOpen the browser inspector.
--last <N>, -n <N>inspect, logShow the newest N captures.
--last, -nreplayReplay the newest capture.
--detail <N>, -d <N>inspectShow one capture by its 1-based list number.
--filter <PROVIDER>inspect, logShow captures from one detected provider.
--status <STATUS>inspect, logShow captures with one status or status class.
--clearlogDelete capture history for the current directory.
--port <PORT>, -p <PORT>replaySend the replay to this local port.
--jsonAll actionsWrite machine-readable output. This is a global flag.

See also

  • lpm dev --tunnel — start a tunnel as part of the dev pipeline
  • Tunneling — learn about domains, plans, request usage, and tunnel behavior
  • lpm cert — use trusted HTTPS for local development without a public tunnel