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.
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.
+--[ team ownership ]------------------------------------------+||| team || |-- domains || | |-- managed deployment domain || | \-- claimed domains || |-- memberships || | \-- one member namespace per domain || \-- routes || |-- member routes || \-- shared routes |||+--------------------------------------------------------------+
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.
+--show the selected team----------------------------------------------------------------------------------------------------------------------------------------------------------------+
List every current membership. The selected team is marked with *:
+--list team memberships----------------------------------------------------------------------------------------------------------------------------------------------------------------+
Select a team by ID or an unambiguous display name:
+--select a team----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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:
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----------------------------------------------------------------------------------------------------------------------------------------------------------------+
+--[ team roles ]-----------------------------------------+| role | permissions |+--------+------------------------------------------------+| member | publish in own namespaces || admin | member access, invites, domains, shared routes || owner | admin access, owner changes, domain release |+--------+------------------------------------------------+
team roles: The publishing and management permissions associated with member, admin, and owner roles.
role
permissions
member
publish in own namespaces
admin
member access, invites, domains, shared routes
owner
admin 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.
A team has access to the managed deployment domain and may have claimed domains. Member namespaces
are derived differently for each kind:
+--[ member namespaces ]-------------------------------------+| domain | namespace form | example |+---------+--------------------------+-----------------------+| managed | <managed-label>.<domain> | busy-toast.tnl.dev || claimed | <member-slug>.<domain> | chase.dev.example.com |+---------+--------------------------+-----------------------+
member namespaces: How managed and claimed domains combine with membership labels to produce member namespaces.
domain
namespace form
example
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.
route scopes: Examples of member and shared routes under a claimed domain.
hostname
scope
chase.dev.example.com
member
api.chase.dev.example.com
member
api.dev.example.com
shared
dev.example.com
shared
--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.
An admin or owner can assign a domain exclusively to the selected team:
+--start a domain claim----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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:
With --default, the domain becomes the team's default after verification. Without it, select a
ready domain explicitly:
+--change the default domain----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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.
Create an expiring invitation from the selected team:
+--invite a member----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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----------------------------------------------------------------------------------------------------------------------------------------------------------------+
+--list team members----------------------------------------------------------------------------------------------------------------------------------------------------------------+
Owners can change owner or admin status; admins can manage ordinary members:
+--change a team role----------------------------------------------------------------------------------------------------------------------------------------------------------------+
Remove a membership when it should no longer create route sessions:
+--remove a team member----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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.
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----------------------------------------------------------------------------------------------------------------------------------------------------------------+
Delete a route by ID when the hostname should no longer belong to that route:
+--delete a route----------------------------------------------------------------------------------------------------------------------------------------------------------------+
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.