tnl.dev :: docs

how tnl works.

follow public HTTPS traffic from ingress to the local service.

tnl carries a visitor's HTTPS connection from the ingress address to a local service on the publisher's loopback interface. Route TLS stays end-to-end through the tnl data plane and terminates inside the publisher.

the deployed services

A production tnl server separates durable coordination from the stateless data plane:

service responsibilities: The durable and runtime responsibilities of control, ingress, and relay services.
serviceresponsibility
controlstate, placement, certificates, administration
ingressvisitor policy, relay selection, forwarding
relaypublisher connections and visitor streams
PostgreSQLauthoritative durable runtime state

Only control connects to PostgreSQL. Ingress and relay processes are stateless, keep short-lived leases, and fail closed when their authorization or routing information expires.

routes, sessions, and tunnels

publishing lifecycle: The relationship between durable routes, publishing sessions, route versions, and local tunnels.
termlifetime
routedurable hostname, ownership, and policy
route sessionpersisted lifecycle of one publisher on a route
route versionone continuous publishing run
tunnelone local tnl publish or tnl dev process

Stopping a tunnel ends its route session but does not delete the route. Publishing the route again creates a new session and increments its route version. Only one unexpired route session can attach to a route at a time.

publisher startup

When a publisher starts, it resolves the selected team, named service, worktree hostname, target, and IP policy. It authenticates through the browser when necessary, reconciles that desired state with the durable route, and opens a new route session.

Each route session owns exactly two connection slots assigned to different relay services. The publisher prefers QUIC. After a short delay it races a TLS/TCP connection using yamux as a fallback; whichever transport completes the authenticated handshake first wins.

Initial routability requires the certificate and both publisher connections. After that first transition, one ready publisher connection is enough to keep serving new visitors while the publisher replenishes toward two. With zero ready connections, the route version becomes temporarily unroutable without ending the session or changing its version.

visitor request path

Public route DNS points every route hostname to the ingress address, never to a relay. For each new visitor TCP connection:

Ingress creates one visitor connection ID and exactly one PROXY v2 metadata header containing the original source and destination. The relay preserves that metadata and all route TLS bytes unchanged. The publisher removes untrusted forwarding headers before proxying a validated HTTP request to the target.

Ingress applies the route's IP policy before forwarding. The publisher receives bounded denial counts and renders aggregate warnings over an interval; individual denied connections and source addresses are not written to the development log.

the retry boundary

Ingress can try another connected relay only while setting up a visitor stream. The first visitor byte sent to a relay is the retry boundary:

After the boundary, a process or transport failure closes that visitor connection. tnl never replays application bytes and never migrates an established visitor stream.

Continue with self-hosting for deployment topology, DNS, PostgreSQL, cluster authentication, and storage keys.