Repository navigation
v0.1.0
First public release. TSLink gives each app on your computer or server, whether a web app, a folder or file, or a TCP port, its own private address on your Tailscale network, so you can open it from your own devices. You decide who else can reach it: people you name, browser guests with an expiring link, or, only when you ask for it, the public internet. Run it yourself from the CLI or through an AI agent over MCP. TSLink needs a Tailscale account and is an independent project, not made or endorsed by Tailscale.
Install
- Prebuilt archives for macOS, Linux and Windows (amd64 and arm64),
.deband.rpmpackages for Linux, and a Homebrew cask for macOS and Linux:brew install --cask anydoor7/tap/tslink. - macOS binaries are signed with a Developer ID certificate and notarized by Apple. Windows archives are not code-signed.
checksums.txtand the SBOMs carry Sigstore signatures, and every release asset has a GitHub build provenance attestation. See Verify a release. Building from source needs Go 1.26.6 or newer.
Share your apps
tslink share 3000,tslink share ./photosortslink share ./report.htmlgives a web app, folder or single file its own HTTPS address in your tailnet.tslink add <name> --tcp <port>does the same for a TCP service. Each app runs as its own embedded Tailscale node inside one background daemon.tslink installstarts the daemon at login: a LaunchAgent on macOS, a systemd user service on Linux and a per-user scheduled task on Windows (--startupuses the Startup folder instead).- Recipes for 15 self-hosted apps, including Home Assistant, Jellyfin, Immich, Nextcloud, Open WebUI and Ollama, plus a generic web recipe.
tslink apps detectfinds apps listening on this machine, andtslink apps share <recipe>previews a registration that--yesapplies. - Per-app request limits (
--max-request-bodyand timeouts) for large or slow uploads. The default body limit is 32 MiB.
People, guest links and public access
- People:
tslink people add alice@example.com --apps photos,finance --for 7dlets that Tailscale login open the chosen web and file apps until the deadline.people update,extendandpeople removechange or revoke access.--invite --print-linksadds device invitations for someone outside your tailnet (this needs a user-owned API token), and--qrprints a QR code for phone setup. - Guest links:
tslink guest create photos --for 3d --public --print-linkcreates an expiring browser link, optionally with a PIN, for a web app. Guests need no Tailscale account. The link goes through Tailscale Funnel behind a mandatory guest gate. Anyone holding the link can use it; TSLink stores only its hash. - Public access: an open Funnel needs
--funnel --public, works for web apps only and always expires, after 24 hours by default and at most 7 days unless you raisedurations.public_max. - Durations: every share and access lifetime uses one grammar, such as
90m,36h,3d,1w,1d12horuntil 2030-06-01. The minimum is one hour. Only tailnet members can be givennever, and only with--ack-never.
Portal and access requests
tslink portal enable --owner you@example.comstarts a private home page on its own tailnet node. Each visitor sees the apps they may open, with their health and deadlines.- People in your tailnet can ask for an app, or for more time, from the portal. Only apps registered with
--requestableare offered.tslink requests approve <id> --for 3dortslink requests deny <id>decides, and a new request can trigger an alert.
Health, alerts and access history
- The daemon checks each app's backend (HTTP status and an optional body match, or a TCP connection) and reports
healthy,degraded,downorunknowninstatus,list --verbose,doctorand the portal.statusanddoctorwarn 14 days and 3 days before a node key expires. - Optional alerts for an app going down or recovering, an approaching expiry and new access requests run a command or call a webhook configured in
alerts.json. tslink access logshows who opened which app and when, including denied requests, with summaries per person and per app. By default only the first path segment is recorded;tslink access pathchanges that per app.
Agents and automation
- Every command in the published manifest except
tslink mcpaccepts--jsonand returns a versionedtslink.resultenvelope; Cobra'shelpandcompletionare not in the manifest and print plain text.tslink manifest --jsonlists those commands with their flags, exit codes and error codes. tslink mcpis an MCP server over stdio.tslink serve --mcpadds a remote MCP endpoint on its own tailnet-only node; it is never published through Funnel.- Scoped roles
viewer,app-operatorandpeople-managerlimit an agent to listed apps, a fixed set of tools and a maximum grant duration, whileownerkeeps full control. Usetslink mcp --scopelocally, ormcp.bindingsfor remote callers, who are identified by Tailscale login or tag.tslink mcp-auditreads the journal of changes made through MCP.
Security defaults
- Apps stay private to your tailnet unless you create a guest link or publish through Funnel. TCP services are never public.
- Registration refuses link-local and cloud metadata targets (
link_local_target_refused) and file shares that would expose TSLink's config directory (path_exposes_config_dir). - The proxy removes client-supplied
Tailscale-*andX-Tailscale-*identity headers before adding the caller's identity from Tailscale. tslink removeand cleanup delete a tailnet device only when TSLink recorded that exact device as its own.- On macOS and Linux, daemon logs are owner-only (mode 0600). Guest PINs are checked against salted PBKDF2 hashes, with attempt limits per link and per source address.
- Over MCP, invitations with a role above member, or that allow exit-node use, need the owner's
mcp.allow_elevated_invitesopt-in.
Not in this release
- Multi-host inventory is planned. Today each host runs its own TSLink, and the portal lists that host's apps.
- There is no admin dashboard or REST API, Docker image, custom domain support, middleware or Prometheus endpoint. See the roadmap.
Compatibility (0.x)
For later 0.x releases, these are the public automation and data contracts:
--jsonkeeps thetslink.resultenvelope and itstype,ok,schema_version,command,code,data, anderrorfields. Optionaldata,error, anderror.nextstay optional; fields may be added.- Documented command
datafields keep their meaning; fields and warnings may be added. Public viewdata.schema_versionstays an integer. - Published error codes keep their meanings and exit-code mapping. Codes may be added; exit classes 0, 1, 2, 3, 4, 5, 64, and 65 stay as documented.
tslink manifest --jsonderiveserror_codesfrom this mapping. registry.jsonschemas 1 and 2 and knownconfig.jsonkeys remain readable; fields may be added. Runregistry checkbefore an upgrade.- MCP tool names and output schemas remain available with additive fields; tools and optional fields may be added. Refusals remain tool results with
isError: true, one JSON text failure object (code,message,next, optionaldata), and nostructuredContent; schema argument errors useusage_error. The four behavior annotations remain present on every tool. - Human output, error messages, and logs are not parsing interfaces. Use
--jsoninstead.runtime.jsonand other daemon/CLI state files are private. - Go packages are not a library API. A future incompatible 0.x change needs a migration note; private pre-release behavior is not a baseline.