Skip to main content

CLI for querying the Tailscale API

Project description

query-ts

A command-line tool for querying the Tailscale API. List and filter devices, users, groups, services, and ACLs using a small, glob-friendly query language, and render the results as a table, JSON, YAML, or plain text.

Features

  • Query devices (default), users, groups, services, and ACLs.
  • A simple filter DSL with glob matching, tag matching, AND/OR, parentheses, and negation.
  • Human-friendly coloured tables by default, plus --json, --yaml, and --plain output for scripting.
  • Authenticates with either a Tailscale API key or OAuth2 client credentials (tokens fetched and cached automatically).

Installation

Install with Homebrew:

brew install 1stvamp/tap/query-ts

Or with snap:

sudo snap install query-ts

Or from PyPI with your tool of choice:

pip install query-ts
# or, to install as an isolated tool:
uv tool install query-ts
# or:
pipx install query-ts

This installs a query-ts command and requires Python 3.11+.

To run from a checkout instead (the project is managed with uv):

uv run query-ts --help

Authentication

Provide either an API key or OAuth2 client credentials, not both. One of the two is required; the CLI exits with an error if no credentials are given.

API key

export TAILSCALE_API_KEY="tskey-api-..."
# or pass it inline with -k/--api-key

OAuth2 client credentials

Both the ID and secret are required together:

export TAILSCALE_OAUTH_CLIENT_ID="..."
export TAILSCALE_OAUTH_CLIENT_SECRET="..."
# or pass --oauth-client-id and --oauth-client-secret

An access token is obtained via the client_credentials grant and cached for its lifetime, refreshing automatically before it expires.

Tailnet

By default the authenticated user's own tailnet (-) is used. Override it with -n/--tailnet or the TAILSCALE_TAILNET environment variable.

Usage

query-ts [QUERY] [OPTIONS]

QUERY is an optional filter expression. Without it, all resources of the selected type are listed.

# All devices, as a table
query-ts

# Devices whose name starts with "web-"
query-ts 'web-*'

# Production-tagged devices that aren't deprecated
query-ts 'env:production -tag:deprecated'

# Users matching a pattern
query-ts --users 'admin*'

# JSON output for two name patterns
query-ts --json 'web-* OR api-*'

# Comma-separated device names (implies plain output)
query-ts --comma 'env:prod'

# List ACL groups
query-ts --type=groups

Query language

Patterns are glob-style (fnmatch) and matched case-insensitively against resource names.

Name patterns

Pattern Matches
web-* names starting with web-
*.example.com FQDNs ending in .example.com

Tag patterns

A word containing a colon is treated as a tag filter. The prefix:suffix form expands to the tag prefix-suffix:

Pattern Matches tag
tag:env-prod env-prod
env:prod env-prod (same as above)
env:prod* env-prod, env-production, etc. (glob on the suffix)

Logical operators

Expression Meaning
web AND env:prod both conditions (AND is the default)
web env:prod implicit AND (same as above)
web OR env:prod either condition
(web OR api) env:prod parentheses for precedence

AND and OR are case-insensitive.

Negation

A leading - negates a term:

query-ts '-web-*'            # exclude names matching web-*
query-ts '-tag:env-prod'     # exclude tagged resources

A query that begins with - (such as a negation) would otherwise be mistaken for a command-line option. Use -- to stop option parsing and pass it as the query:

query-ts -- -web-*          # exclude names matching web-*

Options

Resource type

Flag Description
--devices Query devices (default)
--users Query users
--groups Query ACL groups
--services Query Tailscale Services
--acl Query ACL rules
-t, --type TYPE Resource type: devices, users, groups, services, acl

Output format

Flag Description
-j, --json Output as JSON
-y, --yaml Output as YAML
-p, --plain Output as plain text (newline-separated names)
-f, --format FORMAT Output format: table, json, yaml, plain

The default format is a coloured table.

Plain-text output

Flag Description
-s, --separator SEP Field separator for plain output (default: newline)
-c, --comma Use a comma separator; selects plain output unless a format is set explicitly
--field FIELD Field to extract in plain output (e.g. name, id)

These options only affect plain output. When --field is omitted, a default is chosen per resource type (hostname for devices, login for users).

Connection & authentication

Flag Description
-k, --api-key KEY Tailscale API key (env: TAILSCALE_API_KEY)
--oauth-client-id ID OAuth2 client ID (env: TAILSCALE_OAUTH_CLIENT_ID)
--oauth-client-secret SECRET OAuth2 client secret (env: TAILSCALE_OAUTH_CLIENT_SECRET)
-n, --tailnet NET Tailnet name, or - for the authenticated user's tailnet (env: TAILSCALE_TAILNET)

Display

Flag Description
--color / --no-color Force or disable colour output (auto-detected by default)
--show COLS Comma-separated table columns to always show in full (e.g. --show last-seen,tags)
--version Show the version and exit
-h, --help Show help and exit

Responsive tables

The default table output adapts to your terminal width. When space is tight it degrades gracefully: list columns (IP addresses, tags, members) wrap onto multiple lines first, then the lowest-priority columns are dropped, then long values are shortened (emails lose their domain, timestamps drop the time). Hostname and Tags are never dropped.

For devices, Last Seen is hidden by default and the online state is shown as a coloured dot in the Online column. Use --show last-seen to add the timestamp column, or name any columns you always want kept in full, e.g. --show ip,tags,last-seen. Columns passed to --show are exempt from the automatic dropping and shortening.

Development

# Install dependencies (including dev tools)
uv sync

# Run the test suite
uv run pytest tests/ -v

Tests use respx to mock the Tailscale HTTP API, so no real credentials or network access are required.

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

query_ts-1.1.0.tar.gz (55.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

query_ts-1.1.0-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file query_ts-1.1.0.tar.gz.

File metadata

  • Download URL: query_ts-1.1.0.tar.gz
  • Upload date:
  • Size: 55.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for query_ts-1.1.0.tar.gz
Algorithm Hash digest
SHA256 0b441508f642e5bad5aaaa65aa4f0b9fade45cae458b8665349a71b07cd1dcf1
MD5 f76acaba36fb974960961760633e62da
BLAKE2b-256 a5be726075639d839f0e989b7f572ed8e76b84d226bc833a1056ed43c432e509

See more details on using hashes here.

Provenance

The following attestation bundles were made for query_ts-1.1.0.tar.gz:

Publisher: release.yml on 1stvamp/query-ts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file query_ts-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: query_ts-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for query_ts-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b6d7d01c206d2d4a08d13f209fce6392b90dd24b7629cc6e0863721c5e54f7f0
MD5 c5ee9d6b64c0e5d5d99c174a13b4e931
BLAKE2b-256 623a9aab1d69d76f1a8dc3ed1ca1a22dc44e56b99ae380ce0e0c82ef4b932d3f

See more details on using hashes here.

Provenance

The following attestation bundles were made for query_ts-1.1.0-py3-none-any.whl:

Publisher: release.yml on 1stvamp/query-ts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page