tnl.dev :: docs

teams and domains.

select teams, manage domains, and understand route ownership.

Every route belongs to a team. Your selected team determines which domains, member namespace, hostnames, and administrative permissions a tnl publish or tnl dev command can use.

understand ownership

Every identity has a permanent personal team and may also belong to organization teams. A membership joins an identity to one team with a role and immutable member slug.

A member route belongs to one membership and stays within that membership's namespace. A shared route belongs to the team collectively and sits outside individual member namespaces.

select a team

Show the selected team and your role:

show the selected team
tnl team current

List every current membership. The selected team is marked with *:

list team memberships
tnl team list

Select a team by ID or an unambiguous display name:

select a team
tnl team use acme

The selection is stored in local client state and reused by future commands for the same tnl server. A project's team field can select a team for every named service without changing the stored default:

tnl.config.ts
export default {
  team: "Acme Engineering",
  services: {
    web: {
      directory: ".",
      publish: { target: 3000 },
      dev: { command: ["npm", "run", "dev"] },
    },
  },
};

A named service team has highest project precedence, followed by the root project team and the stored selection. If a stored membership is no longer valid, tnl falls back to your personal team. An unavailable explicit project team fails instead of silently publishing under a different team.

Create an organization team with your immutable member slug:

create an organization team
tnl team create "Acme Engineering" --slug chase

The new team is selected immediately and its creator becomes an owner.

team roles

team roles: The publishing and management permissions associated with member, admin, and owner roles.
rolepermissions
memberpublish in own namespaces
adminmember access, invites, domains, shared routes
owneradmin access, owner changes, domain release

Admins and owners can manage ordinary member routes, but cannot publish inside another member's namespace. Teams always retain at least one owner, and only an owner can grant or remove owner status.

member namespaces

A team has access to the managed deployment domain and may have claimed domains. Member namespaces are derived differently for each kind:

member namespaces: How managed and claimed domains combine with membership labels to produce member namespaces.
domainnamespace formexample
managed<managed-label>.<domain>busy-toast.tnl.dev
claimed<member-slug>.<domain>chase.dev.example.com

Managed labels are allocated by the server. Member slugs are chosen when a team is created or an invitation is issued, and stay fixed while the membership is active.

For the member namespace chase.dev.example.com:

route scopes: Examples of member and shared routes under a claimed domain.
hostnamescope
chase.dev.example.commember
api.chase.dev.example.commember
api.dev.example.comshared
dev.example.comshared

--subdomain api always creates the one-label member route. Use --host for an exact member or shared hostname. Shared routes require an admin or owner. See publish for all hostname rules.

claim a domain

An admin or owner can assign a domain exclusively to the selected team:

start a domain claim
tnl domain claim dev.example.com --default

The command prints the DNS records required to prove authority. Add every record at your DNS provider, then inspect the claim until its state is ready:

check domain state
tnl domain list

With --default, the domain becomes the team's default after verification. Without it, select a ready domain explicitly:

change the default domain
tnl domain default dev.example.com

Changing the default affects future implicit hostnames and --subdomain routes. Existing routes keep their complete hostnames.

Only an owner can release a claimed domain:

release a claimed domain
tnl domain release dev.example.com

The default domain cannot be released. A release may also need to wait for retained routes, active route sessions, certificate work, or certificate reuse deadlines. Delete routes you no longer need before releasing their domain.

invite members

Create an expiring invitation from the selected team:

invite a member
tnl team invite create \
  --slug alex \
  --role member \
  --email alex@example.com \
  --expires-in 168h

The command returns an invitation ID and one-time secret. Send the secret through a protected channel; the server stores only its hash. --email is optional and, when present, restricts acceptance to an identity with that verified email.

The recipient accepts the invitation and selects the joined team:

accept an invitation
tnl team join <invitation-secret>

List or revoke pending invitations:

manage invitations
tnl team invite list
tnl team invite revoke <invitation-id>

Expired and revoked invitations cannot be accepted.

manage members

List memberships and their IDs:

list team members
tnl team members

Owners can change owner or admin status; admins can manage ordinary members:

change a team role
tnl team member set-role <membership-id> --role admin

Remove a membership when it should no longer create route sessions:

remove a team member
tnl team member remove <membership-id>

Removal blocks new authorization and closes that membership's active route sessions. Delete member routes that must release their hostnames before removing the membership that owns them.

manage durable routes

Routes remain after a publisher disconnects unless the tunnel was started with --ephemeral. List the selected team's routes to inspect hostname, scope, lifecycle state, route version, and ID:

list team routes
tnl route list

Delete a route by ID when the hostname should no longer belong to that route:

delete a route
tnl route delete route_0123456789abcdef0123456789abcdef

A member can delete routes bound to their current membership. Admins and owners can also delete ordinary member routes and shared team routes. Deleting an active route closes its route session and removes public route DNS managed by tnl.

Normal tnl dev and tnl publish runs reconcile a persistent owned route before creating a new route session. If a named service changes its target or IP policy, tnl requests an authorized route update instead of requiring deletion. Hostname, team, route scope, and route owner bindings still have to match; reconciliation never transfers ownership.