Repository navigation
Portal gateway mode: a person outside your tailnet accepts one device share, of the portal node, and you decide which apps they can use, on this host or on others, without sending more invitations. Gateway mode is opt-in; per-app sharing is unchanged and remains the default.
Upgrade
- Homebrew:
brew upgrade --cask anydoor7/tap/tslink. Scoop:scoop update; scoop update tslink. Other installs: download the new archive or package from this release. - If TSLink runs as a background service, run
tslink installagain after upgrading so the service starts the new binary. - Existing registries keep their schema version until gateway mode is configured; read Compatibility below before turning it on.
Added
tslink portal enable --gateway: a persistent portal node with one stable device identity, the private dashboard on port 443, and one HTTPS port per routed app (tslink portal route add|remove|list). Each connection is identified before TLS, each request is checked against people grants, and non-navigation requests from other origins, including other gateway ports, are refused unless a route opts out with--allow-cross-origin.- Portal-only apps (
tslink add --portal-only) with no node of their own, and apps on other hosts (tslink add --remote https://<node>.<tailnet>.ts.net). Remote apps are reached only through the tagged gateway node's own netstack, using the peer address from the gateway's netmap, never through the host network or the host resolver. The remote host authenticates the gateway's tag; visitor identity is not forwarded. - One gateway device invite per person (
tslink people add|update --invite); later grant changes need no new invite. An invite left behind by a gateway identity change is reported as stale and can be retired explicitly. - Periodic health checks for portal-only apps; gateway fields in
status,list,urlanddoctor; access-log reason codes and connection-drop counts; MCP toolsportal_route_add,portal_route_removeandportal_route_list, and gateway options forportal_enableandadd. tslink health set <app>and the owner MCP toolhealth_setchange only an app's health check (path, status range, body, timeout, interval, or--reset) in one registry transaction, keeping access, tags, people scope, routes and the enrolled node. Use it instead of re-runningadd, which replaces the whole service.- App health reports the observed HTTP
status_code.doctorreports a backend that answers outside its health settings asapp_probe_unexpected_response(warning) with the exacttslink health setcommand to fix the check. - Access-log events for real backend failures add
error_class(connection_refused,connection_reset,tls,timeoutorother).
Changed
- People-scoped apps now report
allow.mode: peoplewherever an app's access summary is printed (list,status,add), including when gateway mode is off. - HTTP health checks without an explicit status range accept 200-399. Redirects are still not followed; a redirect proves the app answers. 401 and 403 need an explicit range. A single explicit bound keeps its previous meaning, and registries are not rewritten.
- A health check that gets an unexpected status or body is
degraded, neverdown, and sends noapp_downalert. Only unreachable backends (refused, timed out, reset) becomedownafter three consecutive failures.
Fixed
tslink invite listno longer fails when Tailscale returns an empty user-invite list as JSONnull; device invitations stay visible. Device list completeness checks are unchanged.- Apps that redirect their root page, require authentication, or have no root route were reported
downand triggered falseapp_downalerts, and the portal showed them as Down to visitors. - A client that cancels a request while TSLink identifies it, on an app node, a gateway route or the remote MCP control plane, is no longer recorded as an access denial or logged as an identification error, and the request is still never served. On app nodes and gateway routes, a client that cancels during proxying before the response starts is recorded as allowed with status 499 and
client_canceled, instead of a 502 backend failure; once the response has started, the event keeps the status the client received. Requests that TSLink cancels itself, when it tears down a gateway route, when an app node's graceful shutdown runs out of time, or when a guest-gated app stops during a valid guest's request, are recorded as allowed withroute_closedornode_closed, not as client cancellations, guest denials or backend failures: with status 503 before the response starts, and with the forwarded status once it has started. A guest grant that expires or is revoked during a request is still recorded as a denial.
Compatibility
Gateway configuration uses registry schema 3, which older releases refuse. Before downgrading, follow the steps in docs/portal.md: stop the daemon, back up the registry and portal state, remove portal-only and remote apps and all routes, and turn gateway mode off with this version. Recorded gateway invites keep the registry at schema 3 even with gateway mode off; if any remain, restore a registry backup taken before gateway mode was enabled (people and invite changes made since then are lost). Replacing only the binary is not a downgrade.