# ztnet-cli
> A fast, ergonomic command-line interface for managing [ZeroTier](https://www.zerotier.com/) networks through [ZTNet](https://github.com/sinamics/ztnet).
```
$ ztnet network list --json
[
{ "nwid": "abcdef1234567890", "name": "my-network", "private": true }
]
```
## Features
- **Full REST API coverage** — networks, members, orgs, stats, planet files
- **Named profiles** — switch between multiple ZTNet instances with `auth profiles use`
- **Host-bound credentials** — stored tokens/sessions are only used for their configured host
- **Smart name resolution** — reference networks and orgs by name, not just ID
- **Flexible output** — table, JSON, YAML, or raw for scripting
- **Hosts file export** — generate `/etc/hosts` entries from network members
- **Raw API escape hatch** — call any endpoint with `api get /api/v1/...`
- **Dry-run mode** — preview HTTP requests without sending them
- **Automatic retries** — exponential backoff on transient errors and rate limits
- **Shell completions** — bash, zsh, fish, PowerShell, elvish
## Quickstart
### Install
<details>
<summary>cargo install</summary>
```bash
cargo install ztnet
```
</details>
<details>
<summary>npm (prebuilt binaries)</summary>
```bash
npm install -g ztnet-cli
```
</details>
<details>
<summary>Build from source</summary>
```bash
git clone https://github.com/JKamsker/ztnet-cli.git
cd ztnet-cli
cargo build --release
# Binary: target/release/ztnet (target/release/ztnet.exe on Windows)
```
</details>
### Authenticate
```bash
# Point to your ZTNet instance (validated automatically)
ztnet config set host https://ztnet.example.com
# Save your API token (grab it from ZTNet web UI -> Account -> API tokens)
ztnet auth set-token YOUR_API_TOKEN
# Or read from stdin to keep it out of shell history
# Verify it works
ztnet auth test
```
### Core commands
```bash
# List all your networks
ztnet network list
# Create a new network
ztnet network create --name "dev-network"
# List members of a network (by name!)
ztnet member list dev-network
# Authorize a member
ztnet member authorize dev-network abc1234567
# Export hosts file for DNS
ztnet export hosts dev-network --zone ztnet.local
# Get output as JSON for scripting
ztnet network list --json
```
## Command overview
| `auth` | Manage API tokens, profiles, and test connectivity |
| `config` | View and edit configuration, set defaults |
| `user` | Create platform users (admin/bootstrap) |
| `org` | List and inspect organizations |
| `network` | Create, list, get, and update networks |
| `member` | List, authorize, deauthorize, and manage members |
| `stats` | Fetch admin statistics |
| `planet` | Download custom planet files |
| `export` | Generate hosts files, CSV, or JSON from members |
| `api` | Raw HTTP requests to any endpoint |
| `trpc` | Call tRPC procedures (experimental) |
| `completion` | Generate shell completion scripts |
## Global flags
```
-H, --host <URL> ZTNet base URL
-t, --token <TOKEN> API token
--profile <NAME> Config profile to use (default: "default")
--org <ORG> Organization scope (ID or name)
--network <NETWORK> Default network (ID or name)
-o, --output <FMT> Output format: table, json, yaml, raw
--json Shortcut for --output json
--dry-run Print the HTTP request without sending it
--timeout <DURATION> HTTP timeout (default: 30s)
--retries <N> Retry count for transient errors (default: 3)
-y, --yes Skip confirmation prompts
-v, --verbose Increase log verbosity
--no-color Disable ANSI colors
--quiet Suppress interactive output
```
## Configuration
Config lives in a TOML file with named profiles:
| Linux | `~/.config/ztnet/config.toml` |
| macOS | `~/Library/Application Support/ztnet/config.toml` |
| Windows | `%APPDATA%\ztnet\config.toml` |
```toml
active_profile = "default"
[profiles.default]
host = "https://ztnet.example.com"
token = "your-api-token"
output = "table"
[profiles.staging]
host = "https://staging.ztnet.example.com"
token = "staging-token"
```
**Precedence:** CLI flags > environment variables > config file > defaults.
See [docs/configuration.md](docs/configuration.md) for the full reference.
## Documentation
| [Configuration](docs/configuration.md) | Profiles, environment variables, config file format, precedence rules |
| [Command Reference](docs/commands.md) | Every command, subcommand, flag, and option |
| [API Reference](docs/api-reference.md) | Endpoint mapping, authentication, HTTP client behavior, exit codes |
| [Development](docs/development.md) | Building from source, Docker setup, smoke tests, architecture |
## Shell completions
```bash
# Bash
ztnet completion bash > ~/.local/share/bash-completion/completions/ztnet
# Zsh
ztnet completion zsh > ~/.zfunc/_ztnet
# Fish
ztnet completion fish > ~/.config/fish/completions/ztnet.fish
# PowerShell
ztnet completion powershell >> $PROFILE
```
## Examples
### Multi-profile workflow
```bash
# Set up profiles for different environments
ztnet --profile prod config set host https://ztnet.prod.example.com
ztnet --profile prod auth set-token PROD_TOKEN
ztnet --profile staging config set host https://ztnet.staging.example.com
ztnet --profile staging auth set-token STAGING_TOKEN
# Optionally set defaults per host (used when you pass --host without --profile)
ztnet auth hosts set-default https://ztnet.prod.example.com prod
ztnet auth hosts set-default https://ztnet.staging.example.com staging
# Switch between them
ztnet auth profiles use prod
ztnet network list
ztnet auth profiles use staging
ztnet network list
```
### Organization-scoped operations
```bash
# Set a default org so you don't have to pass --org every time
ztnet config context set --org my-org
# Now all commands use that org
ztnet network list
ztnet network create --name "team-network"
ztnet member list team-network
```
### Scripting with JSON
```bash
# Get all network IDs
ztnet network list --ids-only
# Pipe member data into jq
# Authorize all unauthorized members
done
```
### Export hosts file
```bash
# Generate /etc/hosts entries
ztnet export hosts my-network --zone ztnet.local > /tmp/ztnet-hosts
# Or as JSON for further processing
ztnet export hosts my-network --zone ztnet.local --format json
```
### Dry-run and debugging
```bash
# See exactly what HTTP request would be made
ztnet --dry-run network list
# GET http://localhost:3000/api/v1/network
# x-ztnet-auth: sk_1…abcd
```
## License
AGPL-3.0-only (see `LICENSE`)