Skip to content

jaxxstorm/tscli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

194 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

tscli

tscli is a fast, single-binary CLI for the Tailscale HTTP API. From your terminal you can manage devices, users, auth keys, webhooks, posture integrations, tailnet-wide settings, and even hit raw endpoints when the SDK hasn’t caught up yet.

πŸš€ Quick Start

Authenticate and run your first command:

export TAILSCALE_API_KEY=tskey-xxx
export TAILSCALE_TAILNET=example.com
tscli list devices

Create a reusable, preauthorized ephemeral auth key:

tscli create key \
  --type authkey \
  --description "ci-runner" \
  --expiry 24h \
  --reusable \
  --ephemeral \
  --preauthorized

Set up a named profile for multi-tailnet use:

tscli config profiles upsert _lbr_sandbox --api-key tskey-abc123
tscli config profiles set-active _lbr_sandbox
tscli config profiles list

Explore available commands:

tscli --help
tscli get --help
tscli create key --help

✨ Highlights

Area What you can do
Devices List, get, (de)authorize, rename, force IPv4, enable subnet routes, expire, set / delete posture attributes
Keys List & get existing keys; create auth-keys or OAuth clients (with full scope/tag validation)
Users List (filter by type / role), get, suspend / restore / approve, manage invites
Tailnet settings Get & patch booleans + key-expiry with a single command (tscli set settings …)
Policy file (ACL) Fetch as raw HUJSON or canonical JSON
Webhooks List, get, delete, create (generic / Slack) with subscription & provider validation
Posture integrations List, get, create, patch existing integrations
Invites List / delete device- or user-invites
Contacts Get & update contact emails
Debug switch --debug or TSCLI_DEBUG=1 prints full HTTP requests / responses to stderr
Config precedence flags β†’ env β†’ ~/.tscli.yaml (or local ./.tscli.yaml)

πŸ”§ Installation

macOS / Linux (Homebrew)

brew tap jaxxstorm/tap
brew install tscli          # upgrades via β€˜brew upgrade’

Windows (Scoop)

scoop bucket add jaxxstorm https://github.com/jaxxstorm/scoop-bucket.git
scoop install tscli

Nix

nix shell github:jaxxstorm/tscli

Manual download

Pre-built archives for macOS, Linux, Windows (x86-64 / arm64) are published on every release:

# install the newest tscli (Linux/macOS, amd64/arm64)
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case $ARCH in
  x86_64) ARCH=amd64 ;;
  aarch64|arm64) ARCH=arm64 ;;
esac

curl -sSL "$(curl -sSL \
  https://api.github.com/repos/jaxxstorm/tscli/releases/latest \
  | grep -oE "https.*tscli_.*_${OS}_${ARCH}\.tar\.gz" \
  | head -n1)" \
| sudo tar -xz -C /usr/local/bin tscli

Go install (always builds from HEAD)

go install github.com/jaxxstorm/tscli@latest

After any method, confirm:

tscli --version

πŸ“š Documentation

The primary docs live in the in-repo Docsify site under docs/.

  • Site entrypoint: docs/index.html
  • Start page: docs/README.md
  • Generated command docs: docs/commands/

Most useful pages:

  • docs/getting-started.md: install, first command, and flags
  • docs/configuration.md: config keys, profiles, and precedence
  • docs/authentication.md: API-key auth methods and security guidance
  • docs/command-reference.md: generated command reference and workflow

Docs workflows:

make docs-generate   # regenerate docs/commands from Cobra
make docs-check      # fail if generated docs are stale/missing
make docs-serve      # serve docs locally with docsify

βš™οΈ Configuration

Option Flag / Env var YAML key Default
Tailscale API key --api-key, -k / TAILSCALE_API_KEY api-key β€”
Tailnet name --tailnet, -n / TAILSCALE_TAILNET tailnet -
Active profile β€” active-tailnet β€”
Profile list β€” tailnets []
# ~/.tscli.yaml
output: pretty # other options are: human, json or yaml
active-tailnet: _lbr_sandbox
tailnets:
  - name: _lbr_sandbox
    api-key: tskey-abc123
  - name: example.com
    api-key: tskey-def456

# Legacy keys are still supported for backward compatibility
tailnet: example.com
api-key: tskey-def456

Profile management commands:

tscli config profiles list
tscli config profiles upsert _lbr_sandbox --api-key tskey-abc123
tscli config profiles set-active _lbr_sandbox
tscli config profiles delete example.com

πŸš€ Usage

tscli <noun> <verb> [flags]

Canonical verb taxonomy:

  • create creates resources
  • set updates or mutates resources
  • get fetches a single resource
  • list fetches collections
  • delete removes resources

Compatibility aliases remain available for legacy paths such as get dns ns, set dns prefs, set dns splitdns, and get webhook webhook.

Common workflows

# Get a single resource
tscli get device --device node-abc123

# List a collection
tscli list users

# Update settings
tscli set settings --devices-approval=true

# Delete a resource
tscli delete key --key key-id

Global flags

-k, --api-key string   Tailscale API key
-n, --tailnet string   Tailnet (default "-")
-d, --debug            Dump raw HTTP traffic to stderr

πŸ“œ Coverage

API Area / Action Status tscli Command
Devices
List tailnet devices βœ… tscli list devices
Get a device βœ… tscli get device --device <device>
Delete a device βœ… tscli delete device --device <device>
Expire a device key βœ… tscli set device expiry --device <device>
List device routes βœ… tscli list routes --device <device>
Set device routes βœ… tscli set device routes --device <device> --route <cidr>
Authorize / de-authorize device βœ… tscli set device authorization --device <device> --approve=<bool>
Set device name βœ… tscli set device name --device <device> --name <hostname>
Set device tags βœ… tscli set device tags --device <device> --tag tag:<tag>
Update a device key βœ… tscli set device key --device <device> --disable-expiry|--enable-expiry
Set device IPv4 address βœ… tscli set device ip --device <device> --ip <ip>
Get posture attributes βœ… tscli get device posture --device <device>
Set custom posture attributes βœ… tscli set device posture --device <device> --key custom:x --value <v>
Delete custom posture attributes βœ… tscli delete device posture --device <device> --key custom:x
Batch update posture attributes βœ… tscli set device attributes --file <payload.json>
Policy File
Get policy file βœ… tscli get policy [--json]
Set policy file βœ… tscli set policy --file <acl.hujson>
Preview rule matches βœ… tscli get policy preview --type user|ipport --value … [--current|--file F]
Validate / test policy βœ… tscli get policy validate --file <policy.json>
Keys
List tailnet keys βœ… tscli list keys
Create auth-key / OAuth client βœ… tscli create key --type authkey --oauthclient …
Get key βœ… tscli get key --key <id>
Delete / revoke key βœ… tscli delete key --key <key-id>
Update key metadata/scopes βœ… tscli set key --key <id> --body '{"description":"new"}'
Create a token βœ… tscli create token --client-id <oauth-client-id> --client-secret <oauth-client-secret>
DNS
List DNS nameservers βœ… tscli list nameservers
Get DNS configuration βœ… tscli get dns configuration
Set DNS configuration βœ… tscli set dns configuration --body '<json>'
Set DNS nameservers βœ… tscli set dns nameservers --nameserver <ip> …
Get DNS preferences βœ… tscli get dns preferences
Set DNS preferences βœ… tscli set dns preferences --magicdns=<bool>
List DNS search paths βœ… tscli get dns searchpaths
Set DNS search paths βœ… tscli set dns searchpaths --searchpath <domain> …
Get split-DNS map βœ… tscli get dns split-dns
Update split-DNS βœ… tscli set dns split-dns --entry <d>=<ip> …
Set split-DNS βœ… tscli set dns split-dns --replace --entry <d>=<ip>
Logging
List configuration audit logs βœ… tscli list logs config --start <t> [--end <t>]
List network flow logs βœ… tscli list logs network --start <t> [--end <t>]
Get log-streaming status βœ… `tscli get logs stream --type {configuration
Get log-streaming configuration βœ… `tscli get logs stream --type {configuration
Set log-streaming configuration βœ… `tscli set logs stream --type {configuration
Disable log-streaming βœ… `tscli delete logs stream --type {configuration
Create or get AWS external id. βœ… tscli get logs aws
Validate external ID integraton with IAM role trust policy βœ… tscli get logs aws validate --external-id <id> --role-arn <arn>
Users
List users βœ… tscli list users [--type …] [--role …]
Get a user βœ… tscli get user --user <id>
Update user role βœ… tscli set user role --user <id> --role <role>
Approve / suspend / restore user βœ… tscli set user access --user <id> --approve|--suspend|--restore
Delete a user βœ… tscli delete user --user <id>
User Invites
List user invites βœ… tscli list invites user [--state …]
Create user invite βœ… tscli create invite user --email <email> [--role <role>]
Get a user invite βœ… tscli get user invite --id <invite-id>
Delete a user invite βœ… tscli delete user invite --id <invite-id>
Resend user invite βœ… tscli set user invite --id <invite-id> --resend
Device Invites
List device invites βœ… tscli list invites device --device <device>
Create device invite βœ… tscli create invite device --device <device> --email <email>
Get a device invite βœ… tscli get device invite --id <invite-id>
Delete a device invite βœ… tscli delete device invite --id <invite-id>
Resend / accept device invite βœ… tscli set device invite --id <invite-id> --status <resend|accept>
Posture Integrations
List integrations βœ… tscli list posture-integrations
Create integration βœ… tscli create posture-integration --provider <p> …
Get integration βœ… tscli get posture-integration --id <id>
Update integration βœ… tscli set posture-integration --id <id> …
Delete integration βœ… tscli delete posture-integration --id <id>
Contacts
Get contacts βœ… tscli get contacts
Update contact βœ… tscli set contacts --contact <id> --email <e@x>
Resend verification βœ… tscli set contact --type <type> --resend
Webhooks
List webhooks βœ… tscli list webhooks
Create webhook βœ… tscli create webhook --url <endpoint> …
Get webhook βœ… tscli get webhook --id <id>
Update webhook subscriptions βœ… tscli set webhook --id <id> --subscription <event>
Delete webhook βœ… tscli delete webhook --id <id>
Test webhook βœ… tscli set webhook test --id <id>
Rotate webhook secret βœ… tscli set webhook --id <id> --rotate
Services
List services βœ… tscli list services
Get service βœ… tscli get service --service <name>
List service devices βœ… tscli list services devices --service <name>
Get service approval βœ… tscli get service approval --service <name> --device <id>
Update service βœ… tscli set service --service <name> --body '<json>'
Set service approval βœ… tscli set service approval --service <name> --device <id> --approved=true
Delete service βœ… tscli delete service --service <name>
Tailnet Settings
Get tailnet settings βœ… tscli get settings
Update tailnet settings βœ… tscli set settings --devices-approval …

Quick examples

# Approve a waiting device
tscli set device authorization --device node-abc123 --approve

# Rotate an auth-key that expires in 30 days
tscli create key --description "CI" --expiry 720h | jq .key

# Create a reusable preauthorized ephemeral auth-key
tscli create key \
  --type authkey \
  --description "short-lived runner key" \
  --expiry 24h \
  --reusable \
  --ephemeral \
  --preauthorized

# Create Slack webhook for device deletions
tscli create webhook \
  --url https://hooks.slack.com/services/T000/B000/XXXXX \
  --provider slack \
  --subscription nodeDeleted

Device deletion filters

tscli delete devices accepts --include and --exclude partial-match filters. Use --include pattern (repeatable) to limit deletions to names containing the pattern, or --exclude pattern to skip matching devices. These flags are mutually exclusive so that a single filter axis controls the selection.

πŸ›  Development

git clone https://github.com/jaxxstorm/tscli
cd tscli
TAILSCALE_API_KEY=tskey-… TAILSCALE_TAILNET=example.com go run ./cmd/tscli list devices

Tests & lint:

make test-unit
make test-integration
make test

Documentation workflows:

make docs-generate
make docs-check
make docs-serve

Generate a CLI/OpenAPI coverage-gap report:

make coverage-gaps
# writes:
#   coverage/coverage-gaps.json
#   coverage/coverage-gaps.md

OpenAPI snapshot workflow

tscli pins a Tailscale OpenAPI snapshot for deterministic contract checks:

  • Schema: pkg/contract/openapi/tailscale-v2-openapi.yaml
  • Metadata: pkg/contract/openapi/snapshot-metadata.yaml
  • Command mapping: pkg/contract/openapi/command-operation-map.yaml

Refresh the snapshot (requires network access), then re-run tests and report generation:

curl -sS "https://api.tailscale.com/api/v2?outputOpenapiSchema=true" \
  > pkg/contract/openapi/tailscale-v2-openapi.yaml
shasum -a 256 pkg/contract/openapi/tailscale-v2-openapi.yaml
make test
make coverage-gaps

πŸ“„ License

MIT β€” see LICENSE.

About

A CLI tool to interact with the Tailscale API

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 6

Languages