Sign in Get API keys
CLI Reference · ztna

ztna CLI

The ztna command is QuickZTNA's device agent and admin tool. Every device in your tailnet runs it — as a background service for the VPN, and as an interactive CLI for status, diagnostics, and policy operations. 39 commands across 14 categories. Every command below is implemented in the current release; no aspirational documentation.

Installing the CLI On every supported platform: curl -fsSL https://login.quickztna.com/install.sh | sh (Linux/macOS) or irm https://login.quickztna.com/install.ps1 | iex (Windows). Pre-auth rollout: add ZTNA_AUTH_KEY=tskey-auth-xxx to the environment.

Global environment + config

Every command respects these environment variables and reads/writes these files:

ZTNA_AUTH_KEY env optional

Pre-auth key for headless login. When set, overrides all other auth methods. Used by ztna login and ztna up.

QUICKZTNA_LOG_LEVEL env optional

Log level: debug, info, warn, error. Overridden by --log-level flag if present.

~/.config/ztna/config.json file optional

API URL, org ID, hostname, DNS, route config.

~/.config/ztna/state.json file optional

Machine ID, tailnet IP, WireGuard public key, DERP auth token.

~/.config/ztna/tokens.json file optional

JWT access + refresh tokens.

~/.config/ztna/ztna.log file optional

Daemon log (rotating, 10MB max, 2 backups).

~/.config/ztna/profiles.json file optional

Saved connection profiles for multi-org users.

~/.config/ztna/ztna.pid file optional

Daemon PID (separate PID for userspace mode).

Windows + admin behaviour Without administrator privileges, ztna up automatically falls back to userspace mode (no TUN device, no root required). TUN mode is only attempted if elevated. Log file location shifts to %ProgramData%\QuickZTNA\ztna.log on Windows.

Authentication & session

ztna login

Authenticate with the QuickZTNA control plane. Four supported methods: browser (default), auth key (headless), OAuth, interactive email+password.

--auth-key string optional

Pre-auth key for non-interactive login (tskey-auth-xxx). Alternative: set ZTNA_AUTH_KEY env var.

--github bool optional

Authenticate via GitHub OAuth (opens browser).

--google bool optional

Authenticate via Google OAuth.

--sso string optional

SSO login for a given org slug (--sso acme).

--interactive bool optional

Use terminal email/password prompts instead of browser.

--timeout duration optional

Callback timeout. Default 5m.

$ ztna login
→ Opening browser at https://login.quickztna.com/auth?cli_state=...
→ Waiting for callback on http://127.0.0.1:51823/callback
✓ Authenticated as you@company.com
✓ Org: acme (acme.zt.net)
✓ Tokens saved to ~/.config/ztna/tokens.json
$ ZTNA_AUTH_KEY=tskey-auth-abc123 ztna login
✓ Registered with pre-auth key
✓ Machine ID: m_9fX2kR · Tailnet IP: 100.64.1.42
✓ Tagged: tag:laptop, tag:prod
$ ztna login --sso acme
→ Redirecting to Okta for acme.zt.net
✓ Authenticated via SAML assertion

ztna logout

Clear local tokens and authentication state. Org ID is preserved so re-login returns you to the same tenant.

$ ztna logout
✓ Cleared ~/.config/ztna/tokens.json
✓ Preserved org_id (use `ztna login` to resume)

Connection lifecycle

ztna up

Alias: ztna connect. Register the machine (if not registered) and bring the WireGuard tunnel online. Auto-detects elevation and falls back to userspace if not root/admin. On Windows the command auto-daemonizes after preflight; on Linux/macOS it stays foreground unless --daemon.

--auth-key string optional

Pre-auth key for non-interactive registration. Reads ZTNA_AUTH_KEY if unset.

--hostname string optional

Override machine hostname.

--advertise-exit-node bool optional

Offer this machine as an exit node for other peers.

--exit-node string optional

Route all outbound traffic through a peer (IP or auto).

--exit-node-allow-lan-access bool optional

Keep local LAN reachable when routing through an exit node.

--advertise-routes string optional

Comma-separated CIDRs to advertise (e.g. 10.0.0.0/24,10.1.0.0/24).

--advertise-tags string optional

Apply ACL tags (tag:prod,tag:k8s).

--accept-routes bool optional

Accept subnet routes advertised by peers.

--gateway bool optional

Run as subnet gateway (headless, requires --advertise-routes).

--shields-up bool optional

Block all incoming connections.

--ssh bool optional

Enable SSH server on the tailnet IP.

--dns bool optional

Enable MagicDNS. Default true.

--dns-domain string optional

DNS search domain.

--userspace bool optional

Force userspace networking (no TUN device).

--operator string optional

Allow non-root user to manage the tunnel.

--daemon bool optional

Run VPN in background; logs to ~/.config/ztna/ztna.log.

--force-reauth bool optional

Force re-authentication before connecting.

--reset bool optional

Reset all preferences to default.

$ ztna up --auth-key tskey-auth-abc123 --hostname laptop-alex --advertise-tags tag:laptop,tag:eng
→ Detecting elevation... root ✓
→ Loading WireGuard kernel module... ✓
→ Generating ML-KEM-768 + X25519 hybrid keypair
→ Registering machine "laptop-alex"
✓ Machine ID: m_Pq7r2w · Tailnet IP: 100.64.1.42
✓ MagicDNS: laptop-alex.acme.zt.net
→ Exchanging keys with 99 peers (48 direct / 51 via DERP)
→ All peers configured with hybrid PSK
✓ Tunnel up — connected as you@company.com, quantum-safe
$ ztna up
⚠ Not running as root — falling back to userspace mode (no TUN)
→ Userspace stack (netstack/gVisor) listening
✓ Connected — SSH/HTTPS to peers works; kernel routing does not
$ sudo ztna up --gateway --advertise-routes 10.0.0.0/16,192.168.5.0/24 --accept-routes
→ IP forwarding enabled (sysctl net.ipv4.ip_forward=1)
→ Advertising 2 subnet routes (pending admin approval)
✓ Gateway online
PID locking Only one VPN instance can run per user. Running ztna up a second time will refuse to start — see ~/.config/ztna/ztna.pid. TUN and userspace modes use separate PID files.

ztna down

Alias: ztna disconnect. Stop the tunnel, kill the daemon, and send an offline heartbeat so the control plane marks this machine offline immediately.

--accept-risk string optional

Confirm risky action (e.g. --accept-risk lose-ssh when you're SSH-connected through the tunnel you're tearing down).

$ ztna down
→ Sending offline heartbeat
→ Stopping daemon (PID 24891)
→ Removing WireGuard interface
✓ Disconnected. Use `ztna up` to reconnect.

Status & inventory

ztna status

One-shot status: machine info, auth state, tailnet IP, DERP region, peer summary, ACL-blocked peers, exit node config.

--json bool optional

Output as JSON (machine-parseable).

--active bool optional

Only include online peers in output.

--peers bool optional

Include peer list. Default true.

$ ztna status
QuickZTNA v3.2.0 · linux-amd64
────────────────────────────────
● Connected — quantum-safe (PQC active)
  Tailnet:  acme.zt.net (org_9fX2kR)
  Machine:  laptop-alex · 100.64.1.42
  Mode:     TUN (kernel) · MTU 1280
  DERP:     derp-blr (38ms)
  Peers:    99 online · 0 offline · 0 ACL-blocked
  Exit:     (none)

Recent peers (top 5):
  db-primary      100.64.1.12  4.2ms  direct · tag:prod
  ci-runner-03    100.64.1.18  38ms   direct · tag:ci
  eu-edge-07      100.64.1.31  172ms  derp-lon
  ...
$ ztna status --json
{
  "version": "3.2.0",
  "connected": true,
  "machine_id": "m_Pq7r2w",
  "tailnet_ip": "100.64.1.42",
  "mode": "tun",
  "derp_region": "blr",
  "peers_online": 99,
  "peers_offline": 0,
  "peers": [{ /* ... */ }]
}

ztna peers

List all peers (or one specific peer) with NAT traversal and connection details.

<name> arg optional

Optional specific peer to detail.

--json bool optional

Machine-parseable output.

--active bool optional

Only reachable peers.

$ ztna peers --active
NAME              TAILNET IP     PATH    LATENCY  TAGS             LAST SEEN
laptop-alex       100.64.1.42    self    —        tag:laptop       now
db-primary        100.64.1.12    direct  4.2ms    tag:prod         5s ago
ci-runner-03      100.64.1.18    direct  38ms     tag:ci           20s ago
eu-edge-07        100.64.1.31    derp    172ms    tag:edge         45s ago
legacy-vpc        100.64.1.55    direct  12ms     tag:legacy       1m ago
...
$ ztna peers db-primary
Name:          db-primary
Tailnet IP:    100.64.1.12
MagicDNS:      db-primary.acme.zt.net
Endpoint:      52.34.12.88:41824 (direct)
DERP:          blr (fallback available)
Tags:          tag:prod, tag:db
Advertised:    10.0.0.0/16
Exit node:     no
Last seen:     5 seconds ago
RX/TX:         124 MB / 38 MB
PQC:           enabled (ML-KEM-768)

ztna ip

Show this machine's tailnet IP, or look up a peer's IP by name.

$ ztna ip
100.64.1.42
$ ztna ip db-primary
100.64.1.12

ztna whois

Reverse lookup: find which machine owns a tailnet IP.

$ ztna whois 100.64.1.18
Machine:  ci-runner-03
ID:       m_Kx9j4m
IP:       100.64.1.18
Owner:    bot@acme.com
Endpoint: 34.72.91.44:41824
DERP:     blr · online
Tags:     tag:ci, tag:ephemeral

ztna machines list

List every machine in your org. Admin-only commands (approve, quarantine, delete) are in the dashboard or via REST API.

$ ztna machines list
NAME              IP             OS          STATUS   TAGS              LAST SEEN
laptop-alex       100.64.1.42    linux       online   tag:laptop        now
db-primary        100.64.1.12    linux       online   tag:prod,tag:db   5s ago
ci-runner-03      100.64.1.18    linux       online   tag:ci            20s ago
legacy-vpc        100.64.1.55    linux       offline  tag:legacy        4h ago
...
Total: 100 machines (99 online, 1 offline)

ztna auth-keys list

List auth keys in your org. Admin-only — backend returns 403 for non-admins.

$ ztna auth-keys list
PREFIX             REUSABLE  EPHEMERAL  USES  TAGS              EXPIRES
tskey-auth-abc12…  yes       no         87    tag:laptop        in 45 min
tskey-auth-xyz78…  no        yes        1     tag:ci            used
tskey-auth-qwe45…  yes       no         0     tag:prod          revoked

Networking commands

ztna ping

Ping a peer through the mesh. Tries three paths: IPC (accurate tunnel latency), control-plane ping (userspace fallback), raw ICMP (TUN fallback).

<target> arg required

Peer name, MagicDNS name, or tailnet IP.

--count int optional

Number of probes. Default 5.

--timeout duration optional

Per-probe timeout. Default 3s.

$ ztna ping db-primary
PING db-primary.acme.zt.net (100.64.1.12) via WireGuard (PQC)
pqc_pong bytes=64 ttl=64 time=4.2ms path=direct
pqc_pong bytes=64 ttl=64 time=4.5ms path=direct
pqc_pong bytes=64 ttl=64 time=4.1ms path=direct
pqc_pong bytes=64 ttl=64 time=4.8ms path=direct
pqc_pong bytes=64 ttl=64 time=4.3ms path=direct

5 probes sent, 5 received, 0% loss
min/avg/max = 4.1ms / 4.38ms / 4.8ms

ztna nc

Netcat-like TCP tunnel through the mesh. Pipes stdin/stdout to/from a peer's port.

$ ztna nc db-primary 5432
# Connected — raw TCP stream. Type bytes, see bytes. Ctrl+D to close.
$ echo -e "GET / HTTP/1.0\r\n\r\n" | ztna nc web-internal 80
HTTP/1.0 200 OK
Content-Type: text/html
...

ztna ssh

SSH to a peer through the tailnet. Resolves the peer name to its tailnet IP and invokes your system ssh with StrictHostKeyChecking=accept-new.

$ ztna ssh db-primary
# Resolved db-primary → 100.64.1.12, connecting as alex@100.64.1.12
Welcome to db-primary (Ubuntu 24.04 LTS)
$ ztna ssh root@db-primary -- -p 2222 -L 5432:localhost:5432
# Forwards local 5432 to remote postgres on port 2222

DNS commands

ztna dns query

Resolve a hostname via MagicDNS. Tries the hostname as-is, then appends the search domain if not found.

$ ztna dns query db-primary
100.64.1.12 (db-primary.acme.zt.net)

$ ztna dns query db-primary.acme.zt.net
100.64.1.12

ztna dns status

Show DNS resolver configuration.

$ ztna dns status
MagicDNS:      enabled
Search domain: acme.zt.net
Resolver:      100.100.100.100
Upstream:      1.1.1.1, 1.0.0.1

Configuration

ztna set

Modify settings dynamically without restarting the tunnel. All flags are optional; each one changes just one setting.

--hostname string optional

Change machine name.

--exit-node string optional

Use exit node (peer IP, auto, or off).

--exit-node-allow-lan-access bool optional

Keep LAN reachable with exit node.

--advertise-exit-node bool optional

Offer this machine as exit node.

--advertise-routes string optional

Advertise CIDRs.

--shields-up bool optional

Block incoming connections.

--ssh bool optional

Enable SSH server on tailnet IP.

--accept-routes bool optional

Accept peer routes.

--auto-update bool optional

Enable auto-updates.

--tags string optional

Set machine tags.

$ ztna set --hostname laptop-alex-new
✓ Hostname updated to "laptop-alex-new"
✓ Control plane notified
$ ztna set --exit-node eu-edge-07
✓ Exit node: eu-edge-07 (100.64.1.31)
✓ All outbound traffic now routed through exit node
$ ztna set --exit-node off

ztna route list

List advertised and approved subnet routes across the tailnet.

--machine string optional

Filter to one machine.

--json bool optional

Machine-parseable output.

$ ztna route list
MACHINE          IP             ADVERTISED           APPROVED           EXIT NODE
laptop-alex      100.64.1.42    —                    —                  no
db-primary       100.64.1.12    10.0.0.0/16          10.0.0.0/16        no
eu-edge-07       100.64.1.31    0.0.0.0/0, ::/0      0.0.0.0/0, ::/0    yes
legacy-vpc       100.64.1.55    192.168.5.0/24       (pending)          no

ztna route approve / reject / approve-exit-node / reject-exit-node

Approve or reject advertised routes for a machine. Hidden from -h output (admin-only — backend enforces role).

$ ztna route approve legacy-vpc
✓ Approved 1 route for legacy-vpc:
  - 192.168.5.0/24
$ ztna route approve db-primary --routes 10.0.0.0/24,10.0.1.0/24
$ ztna route approve-exit-node eu-edge-07
✓ eu-edge-07 approved as exit node
$ ztna route reject legacy-vpc
$ ztna route reject-exit-node eu-edge-07

ztna exit-node list / suggest

Discover and rank exit nodes available in your tailnet.

$ ztna exit-node list
NAME           IP             DERP    ONLINE  APPROVED  SELECTED
eu-edge-07     100.64.1.31    lon     yes     yes       *
us-edge-03     100.64.1.88    nyc     yes     yes
apac-edge-01   100.64.1.120   blr     yes     yes       
$ ztna exit-node suggest
Best match: apac-edge-01 (100.64.1.120)
Reason:     same DERP region (blr) · 28ms RTT · online · approved
To use:     ztna set --exit-node apac-edge-01

ztna split-tunnel list

Show CIDRs that bypass the VPN (split-tunneling).

$ ztna split-tunnel list
BYPASS CIDR        REASON
10.100.0.0/16      LAN (RFC1918)
169.254.0.0/16     Link-local
224.0.0.0/4        Multicast

Certificates & secrets

ztna cert

Issue a TLS certificate for a tailnet hostname. Signed by the per-org internal CA. No wildcards.

[domain] arg optional

Defaults to this machine's registered name.

--cert-file string optional

Output path for cert (default DOMAIN.crt).

--key-file string optional

Output path for key (default DOMAIN.key).

--serve-demo bool optional

Start demo HTTPS server on :443 using the issued cert.

$ ztna cert
→ Requesting cert for laptop-alex.acme.zt.net
✓ Certificate:  laptop-alex.acme.zt.net.crt (signed by acme-root-ca)
✓ Private key:  laptop-alex.acme.zt.net.key
✓ Valid until:  2026-07-19 · 90 days
$ sudo ztna cert --serve-demo
→ Issuing cert · serving on https://laptop-alex.acme.zt.net
→ Visit from another peer to confirm TLS is trusted

ztna secrets list / get / set / delete / rotate

Encrypted secrets vault (Business+ plan). AES-256-GCM, per-org DEK, role-scoped access.

--value string optional

Secret value (reads from stdin if omitted).

--description string optional

Secret description.

--category string optional

generic, database, api_key, ssh_key, certificate, or token. Default generic.

$ ztna secrets list
NAME              CATEGORY    VERSION  ROLES            ROTATED       EXPIRES
db-password       database    3        admin,dba        2 days ago    in 88 days
github-token      api_key     1        admin,ci         3 hours ago   in 30 days
ssh-deploy-key    ssh_key     2        admin,deploy     1 month ago   in 60 days
$ export PG_PASSWORD=$(ztna secrets get db-password)
$ ztna secrets get github-token
ghp_A9b2cX8d...
$ echo "my-super-secret" | ztna secrets set stripe-key --category api_key
✓ Secret stripe-key created (v1) · category api_key
$ ztna secrets rotate db-password
✓ Rotated db-password → v4
✓ Old value retained for 24h (grace period)
$ ztna secrets delete stripe-key
✓ Secret stripe-key deleted (soft-delete; recoverable for 30d)

Access control

ztna acl list

Show current ACL rules for your org.

$ ztna acl list
[
  {
    "id": "acl_7K9xq",
    "src": "tag:laptop",
    "dst": "tag:prod",
    "proto": "tcp",
    "ports": "22,443,5432",
    "time_hour": "09-18",
    "day_of_week": "mon-fri",
    "source_country": "IN"
  },
  {
    "id": "acl_8Ly4r",
    "src": "tag:admin",
    "dst": "*",
    "proto": "*",
    "ports": "*"
  }
]

ztna acl test

Simulate an ACL check. Can source reach destination on this port?

--src string required

Source machine name or ID.

--dst string required

Destination machine name or ID.

--port int required

Destination port.

--proto string optional

tcp, udp, icmp, or *. Default *.

$ ztna acl test --src laptop-alex --dst db-primary --port 5432 --proto tcp
✓ ALLOW

Matched rule: acl_7K9xq
  src:           tag:laptop
  dst:           tag:prod
  port 5432:     in 22,443,5432 ✓
  time:          14:23 UTC (Thursday) ✓ (09-18 mon-fri)
  source_country IN ✓
$ ztna acl test --src laptop-alex --dst db-primary --port 3306
✗ DENY

No matching rule (port 3306 not in any allow rule)
Default policy: deny

Posture & security

ztna posture status

Show device posture compliance — OS version, disk encryption, firewall, AV — and server verdict.

$ ztna posture status
Device Posture · laptop-alex
─────────────────────────────
OS Version:           Ubuntu 24.04.1 LTS     ✓ (required: 22.04+)
Disk Encryption:      LUKS2 (active)         ✓
Firewall:             ufw (enabled)          ✓
Antivirus:            clamav (active)        ✓
Screen Lock:          enabled (5min)         ✓
Auto-Update:          enabled                ✓

Server Verdict: COMPLIANT — all posture checks pass
Policy:         posture-strict-prod (org default)

ztna threat check

Query threat intelligence feeds for IP, domain, or file hash. Auto-detects target type.

$ ztna threat check 185.220.101.42
Target:      185.220.101.42 (IPv4)
Confidence:  98% malicious

Sources:
  AbuseIPDB:     reported 47 times (last 7d)
  VirusTotal:    flagged by 12/88 engines
  QuickZTNA IOC: match — category: tor-exit-node

Recommendation: BLOCK

ztna audit list

List recent audit log entries. Scoped to your org.

--days int optional

Days to look back. Default 7.

--format string optional

json or csv. Default json.

$ ztna audit list --days 1 --format csv | head -5
timestamp,org_id,user_id,action,resource_type,resource_id,ip_address
2026-04-19T17:32:04Z,org_9fX2kR,user_5k4aLw,auth.login,user,user_5k4aLw,104.28.42.88
2026-04-19T17:34:12Z,org_9fX2kR,user_5k4aLw,auth_key.created,auth_key,ak_Pq7r,104.28.42.88
2026-04-19T17:45:01Z,org_9fX2kR,bot_CI,machine.registered,machine,m_K9xq,34.72.91.44
2026-04-19T18:02:18Z,org_9fX2kR,user_5k4aLw,acl_rule.updated,acl_rule,acl_7K9xq,104.28.42.88

ztna compliance report

Aggregate compliance snapshot: machines, posture rate, ACL counts, certs, threats. Useful for monthly review.

$ ztna compliance report --json
{
  "generated_at": "2026-04-19T18:00:00Z",
  "org_id": "org_9fX2kR",
  "machines": { "total": 100, "online": 97, "quarantined": 1 },
  "posture": { "compliant": 96, "non_compliant": 4, "rate": 96 },
  "acl": { "rules": 18, "deny_default": true },
  "certs": { "active": 42, "expiring_30d": 3 },
  "threats": { "detected_7d": 12, "blocked_7d": 12 }
}

Diagnostics

ztna netcheck

Network reachability probe. Tests UDP, STUN, DERP, and NAT type. Run this first when something isn't connecting.

$ ztna netcheck
QuickZTNA netcheck — 2026-04-19T18:03:12Z
────────────────────────────────────────
UDP:            OK
IPv4:           104.28.42.88
IPv6:           2606:4700::1
Public UDP:     104.28.42.88:51823
NAT type:       endpoint-independent (full cone)
STUN (blr):     OK · 38ms
STUN (nyc):     OK · 204ms
STUN (lon):     OK · 172ms
STUN (sfo):     OK · 242ms
Nearest DERP:   blr (38ms) — preferred
DERP blr TLS:   OK · TLS 1.3 · X25519Kyber768

Verdict: OK — direct P2P should succeed for most peers

ztna debug goroutines / derp / metrics

Developer diagnostics. goroutines dumps all stacks; derp shows relay state; metrics dumps daemon counters.

$ ztna debug goroutines | head -20
goroutine 1 [running]:
runtime/pprof.writeGoroutineStacks(...)
...
goroutine 42 [select, 3 minutes]:
ztna/client/pkg/agent.(*Agent).heartbeatLoop(...)
$ ztna debug derp
DERP home region:  blr (139.59.26.108)
STUN:              stun.quickztna.com:3478
Connected:         yes (TLS 1.3)
Peers via relay:   4
Peers direct:      95

Peer path table:
  db-primary      direct   52.34.12.88:41824
  eu-edge-07      derp     via lon (172ms)
  ...
$ ztna debug metrics
version:           3.2.0
os/arch:           linux/amd64
go:                1.24.2
goroutines:        47
derp:              blr
dns enabled:       true
userspace:         false

ztna log

Tail the daemon log. Follows like tail -f when -f.

-f, --follow bool optional

Follow output.

-n, --lines int optional

Number of recent lines. Default 50.

--clear bool optional

Truncate the log file.

$ ztna log -n 5
2026-04-19T17:45:02.113Z INFO  agent: peer db-primary path changed direct → derp-lon
2026-04-19T17:45:02.201Z INFO  dns: refreshed MagicDNS zone (100 entries)
2026-04-19T17:45:10.004Z INFO  agent: peer db-primary path changed derp-lon → direct
2026-04-19T17:45:12.887Z INFO  heartbeat: 99/99 peers reachable
2026-04-19T17:45:15.012Z DEBUG pqc: rotated PSK for peer ci-runner-03 (age 24h)

ztna bugreport

Generate a diagnostic zip for support. Sanitized: private keys and tokens are stripped; logs are capped to 200 KB; config is redacted.

$ ztna bugreport --output /tmp/issue.zip
Collecting bugreport...
  ✓ Sanitized config
  ✓ Redacted state
  ✓ Last 200 KB of daemon log
  ✓ Status snapshot
  ✓ System info (OS, kernel, arch)

Bundle: /tmp/issue.zip (38 KB)
Email to: support@quickztna.com

ztna metrics print / write

Expose Prometheus-format metrics. print dumps to stdout; write atomically writes to a file (for node_exporter --collector.textfile.directory).

$ ztna metrics print | head -8
# HELP ztna_peer_count Peers currently reachable
# TYPE ztna_peer_count gauge
ztna_peer_count{path="direct"} 48
ztna_peer_count{path="derp"} 2
# HELP ztna_tx_bytes_total Bytes transmitted through the tunnel
# TYPE ztna_tx_bytes_total counter
ztna_tx_bytes_total 248329142
...
$ ztna metrics write /var/lib/node_exporter/textfile_collector/ztna.prom

Version & updates

ztna version

$ ztna version
QuickZTNA v3.2.0
Build:    2026-04-12T09:18:44Z
Commit:   a4b8e2c
OS/arch:  linux/amd64
Go:       1.24.2

ztna update

Check for and apply client updates. Atomic apply: downloads to temp, verifies checksum, swaps binary, restarts service.

--check bool optional

Check only, don't download.

--json bool optional

Machine-parseable output.

--yes bool optional

Skip confirmation.

$ ztna update --check
Current: v3.2.0
Latest:  v3.2.1 (released 2 days ago)
Update?  Run `ztna update` to apply.
$ ztna update --yes
→ Downloading v3.2.1 from releases.quickztna.com
→ SHA-256 checksum verified
→ Installing to /usr/local/bin/ztna.new
→ Restarting service
✓ Upgraded to v3.2.1

Profiles & multi-org

ztna switch

Switch between orgs or saved profiles without logging out. Interactive picker if no argument is given.

[name] arg optional

Org slug or profile name. Omit for interactive picker.

--list bool optional

List available orgs or profiles.

--profile bool optional

Treat name as a profile rather than an org slug.

$ ztna switch --list
ORGS  (* = current)
  * acme        org_9fX2kR    acme.zt.net
    personal    org_3mN7p     personal.zt.net
    consulting  org_8Jk2w     consulting.zt.net
$ ztna switch personal
✓ Switched to org personal (personal.zt.net)
✓ Tailnet IP: 100.90.2.4

ztna profile list / create / delete

Profiles are saved connection configs — useful if you connect to multiple self-hosted QuickZTNA instances.

$ ztna profile list
NAME        ORG_ID       API URL                          ACTIVE
prod        org_9fX2kR   https://login.quickztna.com       *
staging     org_5tH1r    https://staging.login.quickztna…
self-host   —            https://zt.internal.acme.corp
$ ztna profile create self-host --api-url https://zt.internal.acme.corp
✓ Profile self-host created · run `ztna switch --profile self-host` to use
$ ztna profile delete staging
✓ Profile staging removed

System integration

ztna install / uninstall

Install or remove ztna as a system service: systemd on Linux, launchd on macOS, Windows service on Windows.

--force bool optional

Overwrite existing service files (install only).

$ sudo ztna install
→ Writing /etc/systemd/system/quickztna-svc.service
→ systemctl daemon-reload
→ systemctl enable --now quickztna-svc
✓ quickztna-svc is active (running)
$ ztna install
→ Writing ~/Library/LaunchAgents/com.quickztna.daemon.plist
→ launchctl load
✓ com.quickztna.daemon started
PS> ztna install
→ Installing quickztna-svc.exe as Windows service
→ Starting service
✓ QuickZTNA service is running (automatic)
$ sudo ztna uninstall
→ Stopping service
→ Removing unit file
✓ Service removed (config and state preserved in ~/.config/ztna)
configure install-service / configure remove-service Legacy aliases for ztna install and ztna uninstall that still work — documented separately in the Go source for backward compatibility with older install scripts. Prefer the shorter forms.

Shell & misc

ztna completion

Generate shell completion scripts for bash, zsh, fish, or PowerShell.

$ ztna completion bash > /etc/bash_completion.d/ztna
$ ztna completion zsh > "${fpath[1]}/_ztna"
$ ztna completion fish > ~/.config/fish/completions/ztna.fish
PS> ztna completion powershell | Out-String | Invoke-Expression

ztna wg-config export

Export raw WireGuard config — for debugging, or if you want to configure WireGuard manually on a gateway device.

--machine string optional

Machine ID. Defaults to this machine.

--json bool optional

Return JSON with metadata instead of raw wg0.conf.

$ ztna wg-config export
[Interface]
PrivateKey = SF8z...
Address = 100.64.1.42/10
DNS = 100.100.100.100
MTU = 1280

[Peer]
PublicKey = K9xqP...
PresharedKey = 2bN4r...   # derived from ML-KEM-768 + X25519 hybrid
Endpoint = 52.34.12.88:41824
AllowedIPs = 100.64.1.12/32
PersistentKeepalive = 25
...

ztna licenses

Print open source license information.

$ ztna licenses
QuickZTNA uses the following open-source components.
Full text: https://quickztna.com/licenses

Exit codes

All commands follow the same convention:

  • 0 — success
  • 1 — error (Cobra's default; prints error message to stderr)
  • 130 — interrupted (Ctrl+C)

Summary

39 primary commands across 14 categories. All are implemented in the current release. 4 commands are hidden from -h (route approve/reject, route approve-exit-node/reject-exit-node) — still callable, but admin-only on the backend. 1 command is a thin stub (licenses — redirects to the website). Everything else is full-featured with proper error handling, JSON output, and multi-platform support (Linux, macOS, Windows; amd64 and arm64).