Quick Start
Share a local file, directory, or HTTP service with another tailnet device
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.
tslink share ./report.html --name previewNo 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:
tslink share ./report.html --name preview --jsonA successful enrollment handoff looks like this (additional fields may be present):
{"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:
tslink url preview --waitOpen 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.
tslink status
tslink listAn 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:
tslink add my-app --proxy localhost:3000
tslink url my-app --waitadd 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:
tslink add my-app --proxy localhost:3000 --no-daemon-install
tslink serveshare 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:
tslink remove previewLocal removal and remote tailnet device cleanup are reported separately; remote cleanup requires exact ownership proof and an API client.
More tasks
# 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 installPublic 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:
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 --jsonRead 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
- Task recipes: report sharing, local web previews, remote MCP, and TCP access
- Command Reference: command-specific flags, outputs, and errors
- Daemon Mode: background operation and logs
- Configuration: registry, credentials, and policy mutation
- Troubleshooting: enrollment and exact URL readiness