tnl.dev :: docs

quickstart.

install one package, initialize a project, and run tnl dev.

tnl gives a local HTTP service a public HTTPS URL. The project workflow uses one package, one initializer, and one command whether the application runs on Vite, Next.js, or a fixed loopback port.

install one package

Add the project-local client and framework integrations as one development dependency:

install tnl
npm install --save-dev @tnldotdev/tnl@next

The package provides the native tnl command, configuration types, and the @tnldotdev/tnl/vite and @tnldotdev/tnl/next integration subpaths. There are no separate Vite or Next.js packages to install. Node.js 22.18 or newer is required.

Homebrew and verified release archives remain available when a project-local npm installation is not appropriate:

install a native client
brew install tnldotdev/tap/tnl

Follow the release verification guide before running a downloaded archive.

initialize the project

Run the initializer from the project root:

initialize tnl
npm exec -- tnl init

tnl init detects the package manager, supported framework, package scripts, and existing config. It installs the consolidated dependency, creates a named-service config when one is absent, and creates a framework config when one is absent. Existing Next.js, Vite, and TypeScript configuration is preserved, and the result reports the exact integration actions still needed. Review the selected team and named service before the first run.

The generated configuration names each service. That name keeps status output understandable and participates in the default hostname when a project publishes more than one service.

start public development

Start the configured development workflow:

run the development services
npm exec -- tnl dev

On the first command that needs an account, tnl starts browser authentication automatically. Finish sign-in in the opened browser and return to the terminal; there is no separate login step in the normal hosted workflow. If a browser cannot be opened, the same frame prints the exact URL and the command to retry. Saved authentication is reused and refreshed by later commands.

With one configured service, tnl selects it automatically, waits for its actual loopback port, reconciles its route, and prints the URL when it is ready. Select by name in a multi-service project:

run one named service
npm exec -- tnl dev api

The Next.js and Vite integrations preserve framework defaults and explicit host or port settings. They register the actual target after the server binds, so an occupied-port retry cannot tunnel an unrelated process still using the preferred port. tnl dev --port instead requires that exact port. A user host setting can independently expose the development server on the LAN; tnl does not enable or disable that access.

Each tunnel uses deterministic lifecycle states such as starting, provisioning, ready, and draining; repeated status output describes current state rather than an estimated progress bar. After two minutes in one route version's provisioning state, tnl emits one actionable warning and keeps retrying.

understand the defaults

With no explicit hostname, tnl derives a DNS-safe label from the current Git worktree. This lets the main checkout and feature worktrees run concurrently without sharing a route. A configured service name prefixes that worktree label to distinguish named services. Its short hash is stable for the current client state but differs on another installation. The resulting hostname remains beneath the selected membership's namespace on the selected team's default domain.

Hostname values resolve from highest to lowest precedence:

Routes are safe for the current network by default. The control API adds the public IP it observes to the route's IP policy. Use --public only when every visitor address should be accepted:

share with any visitor
npm exec -- tnl dev --public

Denied visitor connections are counted and reported as aggregate warnings. tnl does not print a line containing source information for every denied connection.

Routes are durable by default, so a later run reuses and reconciles the same hostname. Use --ephemeral for a preview route that should be deleted when the tunnel ends:

run an ephemeral preview
npm exec -- tnl dev --ephemeral

inspect and recover

Run tnl status from another terminal for tunnels in the current worktree project. Each entry shows the named service, target, hostname, route version, and lifecycle state when available; tnl status --all adds each worktree project root. Errors include a stable diagnostic code and a relevant help URL when tnl can offer a specific recovery path.

Continue with development workflow for reconciliation and troubleshooting, project configuration for TypeScript, YAML, and JSON examples, or publish for an already-running local service.