TSLinkTSLink Docs

Quick Start

Share a local file, directory, or HTTP service with another tailnet device

View as Markdown

Scenario guides: Use your coding agent's web UI from your phone.

Install

Install TSLink with brew install --cask anydoor7/tap/tslink on macOS or Linux, or follow Installation for archives, Linux packages, Windows zip and source builds. Receiving devices need Tailscale and permission to reach the new node under your tailnet policy. The TSLink host does not need a separate Tailscale daemon.

First share

Share a report you have already created. A single-file target exposes that file; a directory target exposes its browsable contents.

bash
tslink share ./report.html --name preview

No API token, OAuth client secret, or tslink login is required for this default path. share registers the service and ensures the background gateway is running. On first enrollment it may return a Tailscale authorization URL instead of a ready service URL. Enrollment and approval remain subject to your tailnet policy.

For an agent or script, use the same command with --json:

bash
tslink share ./report.html --name preview --json

A successful enrollment handoff looks like this (additional fields may be present):

json
{"type":"tslink.result","ok":true,"schema_version":1,"command":"share","code":0,"data":{"status":"needs_login","auth_url":"https://login.tailscale.com/a/..."}}

needs_login is success with a human action pending. Read data.auth_url; do not treat it as a usable service URL or retry forever without authorization. When already ready, read data.url instead.

Authorize and get the URL

Open the returned authorization URL, approve the node if your policy permits, then ask for its exact runtime URL:

bash
tslink url preview --wait

Open that returned HTTPS link from your phone or another device on the tailnet. The hostname comes from runtime evidence; do not construct it from the requested name.

bash
tslink status
tslink list

An enrollment handoff, a running daemon, and an exact URL are separate states. Tailscale network policy still controls which devices can connect. For proxy/file HTTP services, an unset --allow permits callers allowed by the tailnet; use an explicit allow-list when you need an additional TSLink identity check.

Keep a named service

For an existing local web application, use add:

bash
tslink add my-app --proxy localhost:3000
tslink url my-app --wait

add also ensures the gateway is running by default. If it reports needs_login, complete the same enrollment handoff. Use serve directly only for a deliberate foreground workflow after registering with --no-daemon-install, with no other gateway running:

bash
tslink add my-app --proxy localhost:3000 --no-daemon-install
tslink serve

share defaults to an ephemeral tailnet node; add defaults to a persistent node. Ephemeral node cleanup does not remove the local registry entry. Remove the entry when finished:

bash
tslink remove preview

Local removal and remote tailnet device cleanup are reported separately; remote cleanup requires exact ownership proof and an API client.

More tasks

bash
# Share a directory read-only
tslink share ./build --name docs

# Restrict an HTTP service to specific tailnet identities
tslink add internal --proxy localhost:9090 --allow you@example.com,tag:ops

# Forward raw TCP; protect it with tailnet policy and backend authentication
tslink add mydb --tcp localhost:5432

# Enable login-time autostart
tslink install

Public exposure is a separate choice. Proxy services can use --funnel --public; Funnel traffic is public and has no TSLink caller identity enforcement. It rejects --allow. Automatic Funnel policy provisioning is enabled by default for this explicitly public flow; see Configuration before using it.

Optional stored credential

tslink login is a credential-entry menu, not a browser OAuth callback. Add a stored API token or OAuth client secret only when you need tagged-node automation or API operations. For scripts, choose one explicit stdin source:

bash
printf %s "$TSLINK_API_KEY" | tslink login --api-key-stdin --json
# Or use the OAuth slot, with the secret injected by your secret manager
printf %s "$TSLINK_CLIENT_SECRET" | tslink login --client-secret-stdin --json

Read credential storage and its conditional fallback before using a headless machine. OAuth API operations depend on its scopes. Do not put secrets in argv or MCP client configuration.

Programmatic access

CLI commands accept the versioned --json envelope, except tslink mcp, whose stdout is reserved for MCP frames and rejects --json. The 44 owner MCP tools, with fewer in reduced sessions cover service operations; daemon lifecycle, installation, login/logout, and configuration stay CLI-only.

Next steps

Table of Contents