---
title: CLI
path: sdks/cli
status: published
---

# CLI

`scaivault` is the command-line client. Same API as the SDKs, designed for interactive use, scripts, CI/CD pipelines, and operator workflows.

## Install

```bash
pip install scaivault
scaivault --version
```

Python 3.10+. The CLI ships in the same package as the Python SDK.

## Configure

The CLI reads configuration from `~/.scaivault/config.json` and the environment. Set the server URL and authenticate once:

```bash
scaivault config set base_url https://scaivault.scailabs.ai
scaivault auth login --token "$SCAIVAULT_TOKEN"
scaivault auth whoami
```

Environment variables override config:

| Variable | Meaning |
|----------|---------|
| `SCAIVAULT_URL` | Base URL |
| `SCAIVAULT_TOKEN` | Bearer token |
| `SCAIVAULT_PARTNER_ID` | Explicit partner context (admins) |
| `SCAIVAULT_TENANT_ID` | Explicit tenant context (admins) |
| `SCAIVAULT_DEBUG` | `1` for verbose output |

In CI, set `SCAIVAULT_URL` and `SCAIVAULT_TOKEN` and skip `auth login` entirely.

## Output formats

Most commands default to a human-readable table. Add `--json` for machine output, then pipe through `jq`:

```bash
scaivault secrets list --json | jq -r '.secrets[].path'
```

Exit codes: `0` success, `1` general error, `2` auth, `3` authz, `4` not found, `5` validation.

## Secrets

```bash
# Read
scaivault secrets read app/db/credentials
scaivault secrets read app/db/credentials --version 2
scaivault secrets read app/db/credentials --field password
scaivault secrets read app/db/credentials --json

# Write — accepts repeated key=value pairs, --json-file, or --type
scaivault secrets write app/db/credentials username=admin password=s3cret
scaivault secrets write app/config --type json --json-file config.json
scaivault secrets write app/api-key key=abc --expires-in 30d

# List
scaivault secrets list
scaivault secrets list --prefix environments/production/ --limit 100

# Delete
scaivault secrets delete app/old --hard

# Versions
scaivault secrets versions app/db/credentials

# Rotate
scaivault secrets rotate app/db/credentials

# Find expiring
scaivault secrets expiring --days 30 --prefix environments/production/
```

## Secret policies (value generation)

```bash
scaivault secret-policies list
scaivault secret-policies get sp_abc
scaivault secret-policies types          # list available generators and charsets

# Create from inline JSON
scaivault secret-policies create \
  --name "Strong Password" \
  --type password \
  --fields '[{"name":"password","generator":"random","config":{"length":32,"charset":"alphanumeric"}}]'

# Preview without storing
scaivault secret-policies generate sp_abc --show
```

See the [Secret Policies reference](../reference/secret-policies) for the generator catalog.

## Access policies

```bash
scaivault policies list
scaivault policies get pol_abc

# Quick create (single rule)
scaivault policies create \
  --name "Production Read" \
  --path "environments/production/**" \
  --permissions read,list

# Bind
scaivault policies bind pol_abc --identity-type group --identity-id group:developers

# Test
scaivault policies test app/db/credentials read --identity user:alice@acme.example

scaivault policies delete pol_abc
```

For multi-rule policies, use `--rules-file rules.json` with the same structure as the REST body.

## Rotation

```bash
scaivault rotation list
scaivault rotation create --name "quarterly" --interval 90d --grace-period 48h --warn-before 7d,1d
scaivault rotation assign rot_quarterly environments/production/salesforce/oauth
scaivault rotation trigger rot_quarterly
scaivault rotation history rot_quarterly --status failed
scaivault rotation due --within-hours 168
```

## PKI

```bash
# CAs
scaivault pki ca list
scaivault pki ca create --name "Root CA" --common-name "Acme Root CA" --validity-days 3650
scaivault pki ca cert ca_root_abc --format pem > root.pem

# Issue
scaivault pki certs issue \
  --ca ca_intermediate_abc \
  --common-name "billing.svc.cluster.local" \
  --san "billing-api.svc.cluster.local" \
  --validity-days 7

# List, revoke
scaivault pki certs list --ca-id ca_intermediate_abc --expiring-within 30d
scaivault pki certs revoke cert_xyz --reason key_compromise

# CSR workflow
scaivault pki csr list --status pending
scaivault pki csr approve csr_abc
scaivault pki csr sign csr_abc --ca ca_intermediate_abc --validity-days 90

# ACME
scaivault pki acme register --provider letsencrypt --email certs@acme.example
scaivault pki acme issue --account acme_abc --domains api.acme.example --challenge dns-01
```

## Dynamic secrets

```bash
scaivault dynamic engines list
scaivault dynamic roles list postgres-prod

# Generate a lease and use it
scaivault dynamic creds postgres-prod readonly --ttl 2h --json
# -> { "lease_id": "...", "data": { "connection_url": "..." } }

scaivault dynamic leases list --engine postgres-prod
scaivault dynamic leases renew lease_abc --ttl 1h
scaivault dynamic leases revoke lease_abc
```

## Audit

```bash
scaivault audit query --action read --start 2026-04-22 --end 2026-04-23
scaivault audit trail environments/production/salesforce/oauth
scaivault audit summary
```

## Identity and roles

For day-to-day role assignment, use the admin UI or the REST API (`PUT /v1/identity/users/{user_id}/roles`). The CLI ships one operator command:

```bash
# Bootstrap an existing user to an admin role.
# Writes directly to the database — bypasses the API and audit log.
# Use only for first-time setup or recovery; prefer the API for ongoing changes.
scaivault auth bootstrap --email admin@acme.example --role super_admin
scaivault auth bootstrap --email partner-admin@acme.example --role partner_admin
scaivault auth bootstrap --email tenant-admin@acme.example --role tenant_admin
```

Valid `--role` values: `super_admin`, `partner_admin`, `tenant_admin`.

**The user must exist before you can bootstrap them.** If the email isn't in the local identity cache, the command errors out and tells you to run `scaivault scaikey sync` first. For users that don't exist in ScaiKey yet, create them in ScaiKey first (admin UI), then sync, then bootstrap.

For revoking or reassigning roles after the bootstrap step, use the API:

```bash
curl -X PUT https://scaivault.scailabs.ai/v1/identity/users/$USER_ID/roles \
  -H "Authorization: Bearer $SUPER_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"roles": ["partner_admin"]}'
```

The API enforces the rule that only an existing `super_admin` can grant `super_admin`, and that users can't self-elevate. The CLI `auth bootstrap` command does **not** enforce these rules — it writes directly to the DB and is intended for initial seeding only.

## Service accounts and API keys

```bash
scaivault service-accounts create --name "CI Pipeline" --roles secrets-reader
scaivault service-accounts create-key sva_abc --name "Production Key"
scaivault service-accounts list-keys sva_abc
scaivault service-accounts delete-key sva_abc key_xyz
```

**API key plaintext is shown once at creation.** Pipe to a secret store immediately, or pass `--store-in <path>` to write the new key directly into ScaiVault under a path you control.

## Subscriptions

```bash
scaivault subscriptions create \
  --name "Slack rotation alerts" \
  --paths "environments/production/**" \
  --events secret.rotated,rotation.due \
  --webhook https://hooks.slack.com/xxx

# Long-poll an existing subscription
scaivault subscriptions poll sub_abc --follow
```

`--follow` keeps polling and prints each event as it arrives — handy for piping into another tool.

## Federation

```bash
scaivault federation list
scaivault federation sync fed_abc
scaivault federation status fed_abc
```

## ScaiKey

Tenant-onboarding workflow lives here:

```bash
scaivault scaikey register --scope tenant --tenant-slug acme --email admin@acme.example
scaivault scaikey status
scaivault scaikey sync                  # trigger identity sync
scaivault scaikey sync-status --history
```

## TPM (self-hosted)

For deployments using a TPM 2.0 as the KMS root:

```bash
scaivault tpm init --sopin <so-pin> --userpin <user-pin>
scaivault tpm status

# CRITICAL — back the key up
scaivault tpm backup --output backup.blob --userpin <user-pin>

# Restore to a new host
scaivault tpm restore --input backup.blob --userpin <user-pin>
```

The TPM-wrapped key never leaves the device in plaintext. Back up the wrapping blob — without it, key recovery on a new host is impossible.

## Common patterns

### CI/CD: fetch credentials at job start

```bash
export SCAIVAULT_URL="https://scaivault.scailabs.ai"
export SCAIVAULT_TOKEN="$CI_VAULT_TOKEN"

# Read JSON and export specific fields
creds=$(scaivault secrets read app/db/prod --json)
export DB_USER=$(echo "$creds" | jq -r '.data.username')
export DB_PASS=$(echo "$creds" | jq -r '.data.password')
```

### Watch for expiring secrets

```bash
scaivault secrets expiring --days 14 --json \
  | jq -r '.secrets[] | "\(.path) expires \(.expires_at)"' \
  | sort
```

### Stream rotation events

```bash
scaivault subscriptions poll sub_abc --follow --json \
  | jq -r 'select(.event_type=="secret.rotated") | "\(.path): \(.data.old_version) -> \(.data.new_version)"'
```

### One-shot lease for a script

```bash
lease=$(scaivault dynamic creds postgres-prod readonly --ttl 30m --json)
url=$(echo "$lease" | jq -r '.data.connection_url')
lease_id=$(echo "$lease" | jq -r '.lease_id')

# Use it
psql "$url" -c "SELECT count(*) FROM users"

# Revoke as soon as we're done
scaivault dynamic leases revoke "$lease_id"
```

## Troubleshooting

**"Not authenticated."** `scaivault auth login --token <token>` or set `SCAIVAULT_TOKEN`.

**"Connection refused."** Check `scaivault config get base_url` and try `curl -s "$URL/v1/health"`.

**"Permission denied."** `scaivault auth whoami` to see the token's identity and scopes; `scaivault policies test <path> <permission>` to see which policy rule blocks (or fails to allow) the action.

**Verbose tracing.** `SCAIVAULT_DEBUG=1 scaivault ...` prints every HTTP request and response.

## What's next

- [Python SDK](./python) — same operations from inside an application.
- [MCP](./mcp) — same operations for AI agents.
- [Quickstart](../getting-started/quickstart) — first steps with the API.
