---
title: "Quick Start"
description: "Share a local file, directory, or HTTP service with another tailnet device"
url: "https://tslink.md/docs/quickstart"
locale: "en"
product_version: "0.1.1"
source: "https://github.com/anydoor7/tslink/blob/v0.1.1/docs/getting-started.md"
---

> Documentation index: https://tslink.md/llms.txt · Installed binary is authoritative: `tslink manifest`.

Scenario guides: [Use your coding agent's web UI from your phone](https://tslink.md/docs/agent-ui-phone.md).

## Install

Install TSLink with `brew install --cask anydoor7/tap/tslink` on macOS or Linux, or follow [Installation](https://tslink.md/docs/installation.md) for archives, Linux packages, Windows zip and source builds. Receiving devices need [Tailscale](https://tailscale.com/download) 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](https://tslink.md/docs/configuration.md) 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](https://tslink.md/docs/installation.md#credential-storage) 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](https://tslink.md/docs/mcp-server.md) cover service operations; daemon lifecycle, installation, login/logout, and configuration stay CLI-only.

## Next steps

* [Task recipes](https://tslink.md/docs/use-cases.md): report sharing, local web previews, remote MCP, and TCP access
* [Command Reference](https://tslink.md/docs/commands.md): command-specific flags, outputs, and errors
* [Daemon Mode](https://tslink.md/docs/daemon.md): background operation and logs
* [Configuration](https://tslink.md/docs/configuration.md): registry, credentials, and policy mutation
* [Troubleshooting](https://tslink.md/docs/troubleshooting.md): enrollment and exact URL readiness
