ztnet 0.1.20

ZTNet CLI — manage ZeroTier networks via ZTNet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
# Command Reference

Complete reference for every ztnet-cli command, subcommand, and flag.

## Global options

These flags are available on all commands:

```
-H, --host <URL>          ZTNet base URL (e.g., https://ztnet.example.com)
-t, --token <TOKEN>       API token (x-ztnet-auth header)
    --profile <NAME>      Named config profile (default: "default")
    --org <ORG>           Organization scope (ID or resolvable name)
    --network <NETWORK>   Default network (ID or resolvable name)
    --json                Output as JSON (shortcut for --output json)
-o, --output <FORMAT>     Output format: table|json|yaml|raw (default: table)
    --no-color            Disable ANSI colors
    --quiet               Suppress interactive output (prompts, spinners)
-v, --verbose             Verbose logging (repeat for more: -vv, -vvv)
    --timeout <DURATION>  HTTP timeout (default: 30s, humantime format)
    --retries <N>         Retry count for transient errors (default: 3)
    --dry-run             Print the HTTP request and exit without sending it
-y, --yes                 Skip confirmation prompts
-h, --help                Print help
-V, --version             Print version
```

---

## auth

Manage API tokens, profiles, and connectivity.

### auth set-token

Save an API token to a config profile (host-bound).

By default, `auth set-token` validates the token against the server. Use `--no-validate` to skip the check.

Before storing a token, configure the profile’s host (or pass `--host` / set `ZTNET_HOST`). This prevents accidentally reusing the same token across different ZTNet instances.

```bash
# Recommended: set host first, then store token
ztnet config set host https://ztnet.example.com
ztnet auth set-token <TOKEN>

# Or read token from stdin (avoids shell history)
ztnet auth set-token --stdin

# One-off: bind via host flag
ztnet --host https://ztnet.example.com auth set-token <TOKEN>

# Explicit profile
ztnet --profile prod config set host https://ztnet.prod.example.com
ztnet --profile prod auth set-token <TOKEN>
```

| Flag | Description |
|------|-------------|
| `--stdin` | Read the token from standard input instead of an argument |
| `--no-validate` | Skip token validation against the server |
| `--profile <NAME>` | Profile to store the token under (global flag) |

### auth unset-token

Remove the saved token from a profile.

```bash
ztnet auth unset-token
ztnet --profile prod auth unset-token
```

### auth show

Display the effective auth context: host, profile, token (redacted), org, and network.

```bash
ztnet auth show
```

### auth test

Validate your token by making an API call.

```bash
ztnet auth test
ztnet auth test --org my-org    # test org-scoped access
```

### auth profiles list

Show all profiles and which one is active.

```bash
ztnet auth profiles list
```

### auth profiles use

Switch the active profile.

```bash
ztnet auth profiles use production
```

### auth login

Log in with email/password and store a NextAuth session cookie in the selected profile (used by session-auth commands like `admin` and some `trpc` operations).

```bash
ztnet config set host https://ztnet.example.com
ztnet auth login --email user@example.com --password "..."

ztnet --profile prod --host https://ztnet.prod.example.com auth login --email user@example.com --password "..."
```

### auth logout

Clear the stored session cookie from the selected profile.

```bash
ztnet auth logout
ztnet --profile prod auth logout
```

### auth hosts

Manage per-host default profiles.

```bash
ztnet auth hosts list
ztnet auth hosts set-default https://ztnet.example.com production
ztnet auth hosts set-default https://ztnet.example.com            # infer/create profile
ztnet auth hosts unset-default https://ztnet.example.com
```

---

## config

View and edit the config file, manage defaults.

### config path

Print the config file location.

```bash
ztnet config path
```

### config get

Read a config value using dotted key notation.

```bash
ztnet config get active_profile
ztnet config get profiles.default.host
```

### config set

Write a config value.

When setting a host (`config set host ...` or `profiles.<name>.host`), the URL is normalized and validated by default (and can correct a missing/extra `/api`). Use `--no-validate` to skip the server check. At runtime, the CLI can also auto-retry with/without `/api` and print a banner with a suggested fix (unless `--quiet`).

```bash
ztnet config set host https://ztnet.example.com
ztnet config set profiles.default.output json

# Skip host validation (format is still normalized)
ztnet config set host https://ztnet.example.com --no-validate
```

### config unset

Remove a config value.

```bash
ztnet config unset profiles.default.default_org
```

### config list

Print the full effective config with tokens redacted.

```bash
ztnet config list
```

### config context show

Display the default org and network for the active profile.

```bash
ztnet config context show
```

### config context set

Set default org and/or network so you don't have to pass `--org` / `--network` every time.

```bash
ztnet config context set --org my-org
ztnet config context set --network my-net
ztnet config context set --org my-org --network my-net
```

### config context clear

Remove default org and network from the active profile.

```bash
ztnet config context clear
```

---

## user

Create platform users. Primarily used for bootstrapping the first admin user.

### user create

```bash
ztnet user create \
  --email admin@example.com \
  --password SecurePass123 \
  --name "Admin"
```

| Flag | Description |
|------|-------------|
| `--email <EMAIL>` | **(required)** User email |
| `--password <PASSWORD>` | **(required)** User password |
| `--name <NAME>` | **(required)** Display name |
| `--expires-at <ISO8601>` | Token expiry timestamp |
| `--generate-api-token` | Ask the server to generate an API token |
| `--store-token` | Save the returned token to the config profile |
| `--print-token` | Print the returned token to stdout |
| `--no-auth` | Skip the `x-ztnet-auth` header (required for bootstrapping the first user on an empty database) |

**Bootstrap example** (fresh ZTNet, no existing users):

```bash
ztnet user create \
  --email admin@example.com \
  --password SecurePass123 \
  --name "Admin" \
  --generate-api-token \
  --store-token \
  --no-auth
```

---

## org

List and inspect organizations.

### org list

```bash
ztnet org list
ztnet org list --details      # fetch full details per org (N+1 calls)
ztnet org list --ids-only     # print only org IDs
```

### org get

```bash
ztnet org get <ORG>           # by ID or name
```

### org users list

```bash
ztnet org users list --org my-org
```

---

## network

Create, list, get, and update networks.

### network list

```bash
ztnet network list
ztnet network list --org my-org         # org-scoped
ztnet network list --details            # fetch full details (N+1 calls)
ztnet network list --ids-only           # print only network IDs
ztnet network list --filter "name~=dev" # filter by name substring
```

| Flag | Description |
|------|-------------|
| `--org <ORG>` | List networks in this organization |
| `--details` | Fetch per-network details (additional API calls) |
| `--ids-only` | Print only the network IDs |
| `--filter <EXPR>` | Client-side filter expression (see below) |

**Filter syntax:**

Combine filters with commas:

```
name~=substring       case-insensitive substring match on network name
private==true         exact match on the private flag
private==false        exact match on the private flag
```

Example: `--filter "name~=prod,private==true"`

### network create

```bash
ztnet network create --name "my-network"
ztnet network create --org my-org --name "team-network"
```

### network get

```bash
ztnet network get <NETWORK>             # by ID or name
ztnet network get <NETWORK> --org my-org
```

### network update

Update an organization-scoped network. (Personal network updates are not exposed in the ZTNet API.)

```bash
ztnet network update <NETWORK> --org my-org [OPTIONS]
```

| Flag | Description |
|------|-------------|
| `--name <NAME>` | Rename the network |
| `--description <TEXT>` | Set the description |
| `--mtu <MTU>` | Set the MTU |
| `--private` | Make the network private |
| `--public` | Make the network public |
| `--flow-rule <TEXT>` | Set ZeroTier flow rules inline |
| `--flow-rule-file <PATH>` | Set flow rules from a file |
| `--dns-domain <DOMAIN>` | Set the DNS search domain |
| `--dns-server <IP>` | Add a DNS server (repeatable) |
| `--body <JSON>` | Override request body with raw JSON |
| `--body-file <PATH>` | Read request body from file |

---

## member / network member

Manage network members. `member` is a top-level alias for `network member`.

### member list

```bash
ztnet member list <NETWORK>
ztnet member list <NETWORK> --org my-org
ztnet member list <NETWORK> --authorized      # only authorized members
ztnet member list <NETWORK> --unauthorized    # only unauthorized members
ztnet member list <NETWORK> --name "alice"    # filter by name substring
ztnet member list <NETWORK> --id abc123       # filter by node ID
```

### member get

```bash
ztnet member get <NETWORK> <MEMBER>
ztnet member get <NETWORK> <MEMBER> --org my-org
```

### member update

```bash
ztnet member update <NETWORK> <MEMBER> [OPTIONS]
```

| Flag | Description |
|------|-------------|
| `--name <NAME>` | Set the member name |
| `--description <TEXT>` | Set the member description (personal scope only) |
| `--authorized` | Authorize the member |
| `--unauthorized` | Deauthorize the member |
| `--body <JSON>` | Override with raw JSON |
| `--body-file <PATH>` | Read body from file |

### member authorize

Convenience shortcut to authorize a member.

```bash
ztnet member authorize <NETWORK> <MEMBER>
ztnet member authorize <NETWORK> <MEMBER> --org my-org
```

### member deauthorize

Convenience shortcut to deauthorize a member.

```bash
ztnet member deauthorize <NETWORK> <MEMBER>
```

### member delete

Stash (soft-delete) a member. Prompts for confirmation unless `-y` is passed.

```bash
ztnet member delete <NETWORK> <MEMBER>
ztnet member delete <NETWORK> <MEMBER> -y    # skip confirmation
```

Alias: `member stash`

---

## stats

### stats get

Fetch admin-level statistics from ZTNet.

```bash
ztnet stats get
ztnet stats get --json
```

---

## planet

### planet download

Download a custom planet file from ZTNet.

```bash
ztnet planet download                     # writes to ./planet
ztnet planet download --out /etc/planet   # custom path
ztnet planet download --stdout            # write to stdout
ztnet planet download --force             # overwrite existing file
```

---

## export

Generate derived files from network data.

### export hosts

Generate a hosts(5) file, CSV, or JSON from network members.

```bash
ztnet export hosts <NETWORK> --zone ztnet.local
ztnet export hosts <NETWORK> --zone ztnet.local --out /tmp/hosts
ztnet export hosts <NETWORK> --zone ztnet.local --format csv
ztnet export hosts <NETWORK> --zone ztnet.local --format json
```

| Flag | Description |
|------|-------------|
| `--zone <DOMAIN>` | **(required)** DNS zone suffix (e.g., `ztnet.local`) |
| `--out <PATH>` | Write to file instead of stdout |
| `--format <FMT>` | Output format: `hosts` (default), `csv`, `json` |
| `--authorized-only` | Include only authorized members (default) |
| `--include-unauthorized` | Include unauthorized members too |
| `--org <ORG>` | Organization scope |

---

## api

Raw HTTP escape hatch for calling any ZTNet endpoint.

### api request

```bash
ztnet api request GET /api/v1/network
ztnet api request POST /api/v1/network --body '{"name":"test"}'
ztnet api request POST /api/v1/network --body-file payload.json
ztnet api request GET /api/v1/network --header "X-Custom: value"
ztnet api request GET /api/v1/network --no-auth
ztnet api request GET /api/planet --raw
```

| Flag | Description |
|------|-------------|
| `--body <JSON>` | Request body |
| `--body-file <PATH>` | Read body from file |
| `--header <K:V>` | Add a custom header (repeatable) |
| `--no-auth` | Skip the `x-ztnet-auth` header |
| `--raw` | Output raw bytes instead of JSON |

### api get / api post / api delete

Convenience shortcuts:

```bash
ztnet api get /api/v1/network
ztnet api post /api/v1/network --body '{"name":"test"}'
ztnet api delete /api/v1/network/abc123
```

---

## trpc

Call tRPC procedures on the ZTNet backend. This is experimental and requires a NextAuth session cookie (not an API token).

### trpc list

List all known routers and procedures.

```bash
ztnet trpc list
```

### trpc call

```bash
ztnet trpc call network.getAll --cookie "next-auth.session-token=..."
ztnet trpc call network.getAll --cookie-file cookie.txt
ztnet trpc call networkMember.create --input '{"networkId":"abc123"}' --cookie "..."
```

| Flag | Description |
|------|-------------|
| `--input <JSON>` | JSON input for the procedure |
| `--input-file <PATH>` | Read input from file |
| `--cookie <COOKIE>` | NextAuth session cookie |
| `--cookie-file <PATH>` | Read cookie from file |

---

## completion

Generate shell completions.

```bash
ztnet completion bash
ztnet completion zsh
ztnet completion fish
ztnet completion powershell
ztnet completion elvish
```

**Installation:**

```bash
# Bash
ztnet completion bash > ~/.local/share/bash-completion/completions/ztnet

# Zsh (add ~/.zfunc to your fpath)
ztnet completion zsh > ~/.zfunc/_ztnet

# Fish
ztnet completion fish > ~/.config/fish/completions/ztnet.fish

# PowerShell
ztnet completion powershell >> $PROFILE
```