tnl

module
v0.1.0-rc.32 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 28, 2026 License: MIT

README

tnl

tnl gives your local app an end-to-end encrypted HTTPS URL. By default, every Git worktree gets its own unique URL, so you can easily work on multiple development tracks in parallel.

                           browser
                              |
                            HTTPS
                              v
                 tnld (hosted or self-hosted)
                              |
                      encrypted traffic
                              |
+-----------------------------v-----------------------------+
|                       your machine                        |
|                                                           |
|  main repo                    perf worktree               |
|  web URL -> :5173             web URL -> :5174            |
|  api URL -> :3001             api URL -> :3002            |
|                                                           |
+-----------------------------------------------------------+

start developing

First, install the NPM package:

npm install -D @tnldotdev/tnl@next

Then, initialize with the helper:

npx tnl init

tnl init walks you through creating a tnl.config.ts with the command that starts your app. For example, in a Vite project:

// tnl.config.ts
import { defineConfig } from "@tnldotdev/tnl/config";

export default defineConfig({
  services: {
    app: {
      directory: ".",
      dev: { command: ["vite", "dev"] },
    },
  },
});

Then run:

npx tnl dev

Sign in to GitHub when prompted. Your app will start on the next available port, and tnl publishes to its unique URL, with only your current IP whitelisted:

+--[ tnl dev ]-- ready ----------------------------------------+
|                                                              |
|  https://app-example-abcd1234.ecstatic-penguin.tnl.dev       |
|     |                                                        |
|     v                                                        |
|    tnl                                                       |
|     |                                                        |
|     v                                                        |
|  http://127.0.0.1:5173                                       |
|                                                              |
|  framework                 vite                              |
|  automatically allowed IP  122.151.3.101                     |
|                                                              |
+-- ctrl+c to stop --------------------------------------------+

Hot reloading works automatically, as well as SSE/websockets/streaming responses.

You can allow reviewers or webhooks when you need them.

Follow the quickstart or read about framework setup.

give your services their own urls

If your project has multiple separate services, you can put both startup commands in tnl.config.ts:

// tnl.config.ts
import { defineConfig } from "@tnldotdev/tnl/config";

export default defineConfig({
  services: {
    web: {
      directory: "apps/web",
      dev: { command: ["vite", "dev"] },
    },
    api: {
      directory: "apps/api",
      dev: { command: ["pnpm", "dev"] },
    },
  },
});

Start both services either together or individually:

npx tnl dev
# or
npx tnl dev web
npx tnl dev api

These will have separate URLs:

service URL
web https://web-example-abcd1234.ecstatic-penguin.tnl.dev
api https://api-example-abcd1234.ecstatic-penguin.tnl.dev

Use the API URL assigned to this project in the frontend:

import { tnl } from "@tnldotdev/tnl";

const apiURL = tnl.services?.api?.url;
// https://api-example-abcd1234.ecstatic-penguin.tnl.dev

Read about project configuration and framework metadata. For a Node or Bun API, use tnl.port and tnl.register(server) so worktrees can listen on different ports. See API server setup.

open changes side by side

Create another Git worktree. The project configuration comes with it:

git worktree add -b perf ../perf

Then start the two services:

npx tnl dev
# or
npx tnl dev web
npx tnl dev api

The same frontend is now running from two checkouts:

worktree frontend URL
main https://web-example-abcd1234.ecstatic-penguin.tnl.dev
perf https://web-checkout-e5f6a7b8.ecstatic-penguin.tnl.dev

For Next.js and Vite, there is no port bookkeeping. If another worktree already uses the preferred port, the framework can choose another and tnl follows the listener it actually opens. Restarting the same worktree reuses its URL.

See what is running across your local worktrees from any of them:

npx tnl status --all

custom domain

Claim a development subdomain and make it the default for new public URLs:

npx tnl domain claim dev.example.com --default
npx tnl domain list

Delegate the subdomain with the NS records. Then, dev URLs will look like:

https://web-example-abcd1234.chase.dev.example.com

Use a custom domain.

self-host

After deploying the tnl server, update your config with it:

export default defineConfig({
  server: "https://control.example.com",
  services: {
    // ...
  },
});

The same config works with hosted tnl.dev or your own server.

Run your own server.

not using typescript/javascript, or don't want config?

You can use Homebrew to install the tnl client. Publish an HTTP app already listening on port 3000 without any configuration:

brew install tnldotdev/tap/tnl
tnl publish 3000

This works very similarly to tnl dev. Read about tnl publish.

how much does it cost?

tnl.dev is free for now. Teams have a soft limit of 50 GiB of transfer per month across their public URLs, counting traffic in both directions.

Accounts and teams created while it is free will keep a free plan if paid options arrive.

license

The client and server are MIT-licensed. See the dependency notices, browse the command reference, or contribute.

Directories

Path Synopsis
cmd
tnl command
Command tnl is the client entry point for tnl.
Command tnl is the client entry point for tnl.
tnlbench command
Command tnlbench measures the complete tnl public URL path.
Command tnlbench measures the complete tnl public URL path.
tnld command
Command tnld runs the tnl server daemon.
Command tnld runs the tnl server daemon.
internal
authorityapi
Package authorityapi serves the built-in authority HTTP API.
Package authorityapi serves the built-in authority HTTP API.
authorityclient
Package authorityclient calls the authority API.
Package authorityclient calls the authority API.
authorization
Package authorization authorizes control operations and builds stable request hashes.
Package authorization authorizes control operations and builds stable request hashes.
benchworkload
Package benchworkload contains the publisher and visitor workloads shared by local runtime tests and deployed benchmarks.
Package benchworkload contains the publisher and visitor workloads shared by local runtime tests and deployed benchmarks.
certificates
Package certificates uses ACME to issue route and relay transport certificates.
Package certificates uses ACME to issue route and relay transport certificates.
clientauth
Package clientauth handles CLI login and coordinates control-session refresh.
Package clientauth handles CLI login and coordinates control-session refresh.
clientstate
Package clientstate stores local client data and tunnel snapshots.
Package clientstate stores local client data and tunnel snapshots.
clioutput
Package clioutput renders stable human-readable CLI diagrams.
Package clioutput renders stable human-readable CLI diagrams.
config
Package config defines the versioned YAML and JSON configuration format.
Package config defines the versioned YAML and JSON configuration format.
controlapi
Package controlapi serves the control API and authorizes route operations.
Package controlapi serves the control API and authorizes route operations.
controlclient
Package controlclient calls the tnl control API.
Package controlclient calls the tnl control API.
controlstate
Package controlstate stores persistent control state in PostgreSQL and changes that state in transactions.
Package controlstate stores persistent control state in PostgreSQL and changes that state in transactions.
controltls
Package controltls configures automatic public TLS for the control API.
Package controltls configures automatic public TLS for the control API.
credentials
Package credentials creates and validates typed credentials.
Package credentials creates and validates typed credentials.
diagnostic
Package diagnostic defines stable, user-facing diagnostics for tnl failures.
Package diagnostic defines stable, user-facing diagnostics for tnl failures.
dnscontroller
Package dnscontroller applies persistent DNS changes requested by control.
Package dnscontroller applies persistent DNS changes requested by control.
filelock
Package filelock acquires user-owned, mode-0600 regular lock files on Unix.
Package filelock acquires user-owned, mode-0600 regular lock files on Unix.
httpclient
Package httpclient contains shared HTTP client policy for internal clients.
Package httpclient contains shared HTTP client policy for internal clients.
httpjson
Package httpjson reads and writes size-limited JSON.
Package httpjson reads and writes size-limited JSON.
ingress
Package ingress routes TLS connections by exact SNI hostname without terminating their TLS.
Package ingress routes TLS connections by exact SNI hostname without terminating their TLS.
ingressapi
Package ingressapi serves control's private API for ingress processes.
Package ingressapi serves control's private API for ingress processes.
localproxy
Package localproxy forwards one public hostname to one local HTTP target.
Package localproxy forwards one public hostname to one local HTTP target.
muxsession
Package muxsession carries multiplexed byte streams over supported transports.
Package muxsession carries multiplexed byte streams over supported transports.
naming
Package naming defines public-hostname rules used by control and ingress.
Package naming defines public-hostname rules used by control and ingress.
observability
Package observability provides process and service metrics without depending on one metrics provider.
Package observability provides process and service metrics without depending on one metrics provider.
oidcauth
Package oidcauth implements OIDC authentication and CLI login flows.
Package oidcauth implements OIDC authentication and CLI login flows.
opaqueid
Package opaqueid creates and validates opaque 128-bit identifiers.
Package opaqueid creates and validates opaque 128-bit identifiers.
projectconfig
Package projectconfig finds, loads, and validates project configuration.
Package projectconfig finds, loads, and validates project configuration.
projectmeta
Package projectmeta validates and writes browser-safe project metadata.
Package projectmeta validates and writes browser-safe project metadata.
proxyproto
Package proxyproto implements tnl's strict PROXY protocol v2 boundary.
Package proxyproto implements tnl's strict PROXY protocol v2 boundary.
publicurlusage
Package publicurlusage defines public URL usage data that can be combined across reports.
Package publicurlusage defines public URL usage data that can be combined across reports.
publicurlusageworker
Package publicurlusageworker finishes and sends stored public URL usage reports.
Package publicurlusageworker finishes and sends stored public URL usage reports.
publisher
Package publisher publishes one local service through a public URL.
Package publisher publishes one local service through a public URL.
relayapi
Package relayapi serves control's private API for relay processes.
Package relayapi serves control's private API for relay processes.
routebackend
Package routebackend defines how ingress opens a visitor stream to a publisher.
Package routebackend defines how ingress opens a visitor stream to a publisher.
router
Package router reads a size-limited TLS ClientHello and preserves every byte so the connection can replay it.
Package router reads a size-limited TLS ClientHello and preserves every byte so the connection can replay it.
serviceapi
Package serviceapi provides HTTP helpers for the private service APIs.
Package serviceapi provides HTTP helpers for the private service APIs.
sqlite
Package sqlite manages client SQLite connections and migrations.
Package sqlite manages client SQLite connections and migrations.
storagekey
Package storagekey encrypts recoverable secrets stored by control.
Package storagekey encrypts recoverable secrets stored by control.
streamcopy
Package streamcopy copies bytes in both directions and preserves half-closes.
Package streamcopy copies bytes in both directions and preserves half-closes.
tlschallenge
Package tlschallenge manages publisher-local TLS challenge keys and certificates.
Package tlschallenge manages publisher-local TLS challenge keys and certificates.
tnldconfig
Package tnldconfig loads and validates tnld process configuration.
Package tnldconfig loads and validates tnld process configuration.
tunnel
Package tunnel runs tunnelv1 sessions over supported transports.
Package tunnel runs tunnelv1 sessions over supported transports.
pkg
api/authorityv1
Package authorityv1 provides primitives to interact with the openapi HTTP API.
Package authorityv1 provides primitives to interact with the openapi HTTP API.
api/controlv1
Package controlv1 provides primitives to interact with the openapi HTTP API.
Package controlv1 provides primitives to interact with the openapi HTTP API.
api/ingressv1
Package ingressv1 provides primitives to interact with the openapi HTTP API.
Package ingressv1 provides primitives to interact with the openapi HTTP API.
api/publicurlusagev1
Package publicurlusagev1 provides primitives to interact with the openapi HTTP API.
Package publicurlusagev1 provides primitives to interact with the openapi HTTP API.
api/relayv1
Package relayv1 provides primitives to interact with the openapi HTTP API.
Package relayv1 provides primitives to interact with the openapi HTTP API.
protocol/tunnelv1
Package tunnelv1 defines messages for publisher connections and visitor streams.
Package tunnelv1 defines messages for publisher connections and visitor streams.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL