CLI Reference
Install and authenticate the varsafe CLI, resolve context, and master the run/export workflows — plus the generated command reference.

Installation
The CLI is a self-contained binary — it is not published on npm. Six artifacts are published: Linux x64 and arm64, each for glibc and musl, plus macOS x64 and arm64. The installer picks the right one by detecting your architecture and C library. Windows works via WSL.
curl -fsSL https://varsafe.dev/install.sh | bash
The installer downloads the binary to ~/.varsafe/bin and adds it to your shell’s PATH. Verify the install and explore the commands — no account needed:
# docs-test: offline
# Check the CLI is installed and explore its commands — no account needed.
set -euo pipefail
varsafe --version
varsafe --help
echo "OK:help"To upgrade later, the binary updates itself:
varsafe update
Commands at a glance
The CLI has 14 flat commands — no nested subcommands:
| Command | Description |
|---|---|
login |
Authenticate with varsafe |
logout |
Revoke session and clear credentials |
whoami |
Display current authenticated user |
use |
Set default project and environment context |
list |
List secrets for a project/environment (alias: ls) |
get |
Print a single secret value to stdout |
set |
Set a secret in an environment |
unset |
Remove a secret from an environment |
run |
Run a command with secrets as environment variables |
export |
Export secrets to a file or stdout |
status |
Show the credential, the current context, and what is in it |
doctor |
Diagnose whether the CLI can securely store credentials |
theme |
Choose the colour palette (auto, dark, light) |
update |
Update the CLI to the latest version |
Every command and flag is documented in the generated reference at the bottom of this page.
Authentication
There are two ways to authenticate: an interactive browser login for humans, and an API token for machines.
Browser login (default)
varsafe login
The CLI opens your browser and prints a short, human-readable confirmation code in the terminal. Approve the request in the browser — after checking the code matches — and the CLI completes automatically via a server push, falling back to polling if the streaming connection drops. Your password never passes through the terminal.
Browser login stores the credential in your operating system’s keychain. That makes it the wrong choice for CI: a headless runner has no keychain, so varsafe login fails there. Use a token instead.
API token (CI and automation)
API tokens are created in the dashboard under team settings. Three ways to supply one:
# The CLI picks the token up automatically — no login step at all
export VARSAFE_TOKEN="vsafe_example_ci_token"
varsafe run -- ./deploy.sh# "-" reads the token from stdin — keeps it out of argv
secret-manager read varsafe-token | varsafe login -t -# Interactive masked input — never lands in shell history
varsafe login -TFor pipelines, prefer the environment variable and skip login entirely — see CI/CD usage.
Session commands
varsafe whoami # show the authenticated user and teams
varsafe logout # revoke the session server-side and clear local credentials
Logout invalidates the session immediately — a stolen credentials file is useless afterward.
Environment variables
| Variable | Purpose |
|---|---|
VARSAFE_API_TOKEN |
API token for non-interactive auth — the primary CI variable |
VARSAFE_TOKEN |
Shorter alias for VARSAFE_API_TOKEN; also read by varsafe login -t |
VARSAFE_API_URL |
Overrides the API base URL (default https://api.varsafe.dev) — for self-hosted or test stacks |
VARSAFE_DEBUG |
Set to any value to append a troubleshooting log to ~/.varsafe/debug.log (capped at 5 MB). Writes to the file, not to your terminal |
VARSAFE_NO_UPDATE_CHECK |
Presence disables the interactive “update available” prompt — any value counts, including 0 and false, as with NO_COLOR. Set it in CI so the prompt cannot hang an unattended run |
VARSAFE_THEME |
auto, dark, or light — overrides the saved palette for one invocation without rewriting settings. An unrecognized value is ignored |
Context
Commands that touch secrets need a project and environment. Resolution order:
- Flags —
-p/--projectand-e/--env(or-i/--project-idin automation) always win - Local
.varsafefile — found by searching from the current directory up to the git root - Global saved context — set via
varsafe use - Auto-selection when only one option exists; interactive prompt otherwise
# Set context — inside a git repo this writes a .varsafe file,
# elsewhere it saves globally
varsafe use -p my-api -e development
# View the current context
varsafe use
# Clear it
varsafe use --clear
This script removes a secret and exercises the context lifecycle:
# docs-test: local-stack
# Remove a secret and manage the saved context.
set -euo pipefail
# VARSAFE_TOKEN in the environment authenticates every command below; `varsafe
# login` is neither needed nor possible on a headless runner (no keychain).
varsafe use -p "$VARSAFE_PROJECT" -e development
printf %s 'true' | varsafe set TEMP_FLAG --stdin
varsafe unset TEMP_FLAG
# Context can be cleared at any time; commands then need -p/-e flags
varsafe use --clear
varsafe list -p "$VARSAFE_PROJECT" -e development
echo "OK:unset-and-context"Inject secrets into a process
varsafe run is the core workflow: it fetches secrets over TLS and injects them as environment variables into the child process. Nothing is written to disk; when the process exits, the secrets are gone.
varsafe run -- npm run dev
The command after -- is executed argv-exact — no shell re-parsing, so quoting is preserved and arguments arrive in the child process exactly as you wrote them. Status messages (“Injecting 5 secrets”) go to stderr, so stdout stays clean for piping.
That also means shell syntax is not interpreted: ;, |, &&, $() and backticks reach your program as literal characters rather than being executed. This is deliberate — it stops a value such as a CI branch name from running as a command inside the process holding your secrets. When you do want a shell, ask for one explicitly with --shell, which takes a single command string:
varsafe run --shell 'bun run build && ./deploy.sh'
Prefer the argv form when you can. Reach for --shell only for pipes, &&, and redirection — and note that --shell 'setup && exec app' keeps your real process in the foreground, which matters for signal handling.
Shape what gets injected:
# Only secrets matching glob patterns (comma-separated)
varsafe run --include 'VITE_*,CRISP_*' -- bun run build
# Rename injected keys: each secret is exposed with MONITORING_ prepended
# (e.g. DATABASE_URL becomes MONITORING_DATABASE_URL). Keys that already start
# with the prefix are left unchanged.
varsafe run --prefix monitoring -- ./run-monitoring.sh
--include selects which secrets are injected; --prefix renames them. They compose.
Merge in an encrypted .env file — file values win over API values, and the flag can be repeated. Every file must be sealed to the environment the command resolves to, since that is the key it can ask for:
varsafe run --env-file .env --env-file .env.extra -- npm start
--env-file reads encrypted files only; a plaintext file is refused. To merge a plaintext .env, source it in the shell — inside the child, so its values still win over the injected secrets:
varsafe run --shell 'set -a; . ./.env.local; set +a; exec npm start'
The full version, including the child process assertion:
# docs-test: local-stack
# Inject secrets into a child process as environment variables — no file
# is ever written to disk.
set -euo pipefail
# VARSAFE_TOKEN in the environment authenticates every command below; `varsafe
# login` is neither needed nor possible on a headless runner (no keychain).
varsafe use -p "$VARSAFE_PROJECT" -e development
printf %s 'vsafe_example_run_inject_value' | varsafe set API_KEY --stdin
# The child process sees API_KEY; your shell never does.
varsafe run -- sh -c 'test -n "$API_KEY"'
# Only inject secrets matching a pattern
varsafe run --include 'API_*' -- sh -c 'test -n "$API_KEY"'
echo "OK:run-inject"Read a single value
For piping one secret into another tool, varsafe get prints just the value:
# Use a secret inline without exposing it in your shell profile
psql "$(varsafe get DATABASE_URL)"
# Exact bytes, no trailing newline — for tools that are byte-sensitive.
# On Linux, /dev/shm keeps it in RAM; macOS has no /dev/shm, so use a
# path you control and remove it afterwards.
varsafe get SIGNING_KEY -n > ./key
# Structured output: {key, value, version, updatedAt}
varsafe get DATABASE_URL --json
Export secrets to a file
When a tool insists on a file, varsafe export writes one. The .env format is encrypted by default: the file starts with a #@varsafe/v2/ek_<id> header identifying the environment key, and each value is individually encrypted and bound to its variable name (varsafe:v2:...). Encrypted .env files are readable only by your team — safe to commit — and are decrypted on the fly by varsafe run --env-file.
Writing an encrypted file needs only the environment’s public key, so it works with an API token as well as interactively. Reading one back does not: decryption needs the private key, which only an interactive owner or admin can fetch. So a CI job can produce an encrypted .env, but it cannot consume one — see encrypted .env.
# Encrypted .env (default)
varsafe export -o .env
# Plaintext requires an explicit opt-in
varsafe export --plain -o .env
# Other formats: compose (alias: env), docker (alias: kubectl), json, yaml (alias: yml) —
# auto-detected from the output file extension, or forced with -f
varsafe export --plain -o secrets.json
varsafe export --plain -f yaml
# Write to RAM-backed tmpfs (/dev/shm) so the file never touches disk
varsafe export --plain --tmpfs -o app.env
The export flow, covering the encrypted default, --plain, and format variants:
# docs-test: local-stack
# docs-test-allow-plaintext
# Export secrets when a file is genuinely required. The default .env export
# is encrypted; --plain is an explicit opt-in.
set -euo pipefail
# VARSAFE_TOKEN in the environment authenticates every command below; `varsafe
# login` is neither needed nor possible on a headless runner (no keychain).
varsafe use -p "$VARSAFE_PROJECT" -e development
printf %s 'smtp.example.com' | varsafe set SMTP_HOST --stdin
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT
# Encrypted .env (the default). Sealing reads the environment's PUBLIC key, which a
# token is allowed to do, so this works under token auth as well as interactively.
varsafe export -o "$workdir/.env"
grep -q '#@varsafe/v2/' "$workdir/.env"
# Plaintext requires an explicit flag
varsafe export --plain -o "$workdir/plain.env"
grep -q 'SMTP_HOST=' "$workdir/plain.env"
# Other formats: json, yaml, docker
varsafe export --plain -f json -o "$workdir/secrets.json"
varsafe export --plain -f yaml -o "$workdir/secrets.yaml"
echo "OK:export-formats"CI/CD usage
For pipelines, set VARSAFE_API_TOKEN from your CI secret store — no varsafe login step is required:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install varsafe CLI
run: curl -fsSL https://varsafe.dev/install.sh | bash
- name: Deploy with secrets
run: varsafe run -p my-api -e production -- ./deploy.sh
env:
VARSAFE_API_TOKEN: ${{ secrets.VARSAFE_API_TOKEN }}deploy:
stage: deploy
script:
- curl -fsSL https://varsafe.dev/install.sh | bash
- varsafe run -p my-api -e production -- ./deploy.sh
variables:
VARSAFE_API_TOKEN: $VARSAFE_API_TOKENExit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Authentication failed |
| 2 | Network error |
| 3 | Validation error |
| 4 | Resource not found |
| 5 | Permission denied |
| 99 | Internal error |
varsafe run is an exception: once the child process starts, run exits with the child’s exit code, so wrappers and CI steps behave as if the command ran directly.
Uninstall
To remove varsafe from your system:
# Remove the binary and local data
rm -rf ~/.varsafe
Then remove the PATH entry from your shell configuration:
# Edit ~/.zshrc and remove the line:
# export PATH="$HOME/.varsafe/bin:$PATH"# Edit ~/.bashrc and remove the line:
# export PATH="$HOME/.varsafe/bin:$PATH"# Edit ~/.config/fish/config.fish and remove:
# fish_add_path ~/.varsafe/binIf you used varsafe use in any project directories, you can also delete the .varsafe context files in those repositories.
Troubleshooting
Error messages, their causes, and the fix for each are in the
troubleshooting guide — expired tokens, a stuck browser login,
Not signed in, an empty varsafe list, Project is required in CI, secure-storage
failures, and rate limits.
One case belongs here, because it happens before the CLI can report anything: varsafe: command not found right after installing. The installer adds ~/.varsafe/bin to your
shell config, which an already-open shell has not read yet. Restart the terminal, or add it
for the current session:
export PATH="$HOME/.varsafe/bin:$PATH"
If it is still missing, run the installer again and read its Detected platform: line —
it names the artifact chosen for your OS, architecture and C library.
Command reference
Generated from the CLI’s own --help output — always matches the shipped binary.
varsafe login
Authenticate with varsafe
varsafe login [options]
Flag
Type
Description
--api-url <url>
value
API URL (defaults to $VARSAFE_API_URL, then the public API)
-t, --token [token]
optional
Authenticate with an API token. Omit the value for a masked prompt, or pass "-" to read it from stdin. An inline value is rejected — it would leak to shell history.
-T, --token-prompt
boolean
Alias for `--token` with no value (masked prompt)
-f, --force
boolean
Force re-authentication even if already logged in
-h, --help
boolean
display help for command
varsafe whoami
Show the credential this machine is using and what it may do
varsafe whoami [options]
Flag
Type
Description
--json
boolean
Output the credential description as JSON
-h, --help
boolean
display help for command
varsafe logout
Revoke this machine’s credential and clear it locally
varsafe logout [options]
Flag
Type
Description
-h, --help
boolean
display help for command
varsafe list
Alias: ls
List secrets for a project and environment
varsafe list|ls [options]
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
-r, --reveal
boolean
Show actual secret values
--json
boolean
Output as JSON
--include <patterns>
value
Comma-separated glob patterns to filter keys (e.g., --include "VITE_*,*_URL") — same syntax as `varsafe run --include`
--source
boolean
With --reveal, show composed secrets as written instead of resolved
-h, --help
boolean
display help for command
varsafe run
Run a command with secrets as environment variables
varsafe run [options] <command...>
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
--prefix <prefix>
value
Rename injected keys with PREFIX_ prepended — renames, does not filter (e.g., --prefix monitoring). Use --include to select keys
--include <patterns>
value
Comma-separated glob patterns to filter which secrets get injected (e.g., --include "VITE_*,CRISP_*")
--env-file <path>
value
Decrypt an encrypted .env file and merge it into the injected secrets (can be repeated). Only encrypted files are accepted — source a plaintext .env in your shell instead
--shell
boolean
Run a single command string through /bin/sh (enables pipes, &&, etc.): varsafe run --shell 'a && b'. On an interactive terminal, use exec for graceful shutdown, e.g. --shell 'setup && exec main'
-h, --help
boolean
display help for command
varsafe export
Export secrets to a file or stdout (env format encrypts by default, use –plain to disable)
varsafe export [options]
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
-o, --output <file>
value
Write to file instead of stdout (format auto-detected from extension)
-f, --format <format>
value
Override serialization: compose (.env), docker, kubectl, json, yaml (default: compose)
--plain
boolean
Skip encryption and output plaintext values
--tmpfs
boolean
Write to /dev/shm/ (RAM-backed tmpfs)
-h, --help
boolean
display help for command
varsafe get
Print a single secret value to stdout (pipe-friendly)
varsafe get [options] <key>
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
--json
boolean
Output {key, value, version, updatedAt} as JSON
--source
boolean
Print a composed secret as written instead of resolved
-n, --no-newline
boolean
Do not append a trailing newline (exact byte output for piping)
-h, --help
boolean
display help for command
varsafe set
Set a secret in an environment
varsafe set [options] <key>
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
-s, --stdin
boolean
Read the value from stdin (e.g. printf %s "$SECRET" | varsafe set KEY --stdin)
--json
boolean
Output {key, version, project, environment} as JSON
-T, --prompt
boolean
Prompt for the value with masked input (default at a terminal; does not leak to shell history)
--from-env <name>
value
Read the value from an environment variable (for CI)
--from-file <path>
value
Read the value from a file (multi-line certs, private keys)
--chomp
boolean
Strip one trailing newline from the value (for echo-style producers)
--template <source>
value
Store a value composed from other secrets, e.g. 'postgres://${DB_PASS}@db'
-y, --yes
boolean
Skip the confirmation required in protected environments
-h, --help
boolean
display help for command
varsafe unset
Remove a secret from an environment
varsafe unset [options] <key>
Flag
Type
Description
-p, --project <name>
value
Project name
-i, --project-id <id>
value
Project ID (for CI/automation)
-e, --env <environment>
value
Environment (e.g., development, staging, production)
-y, --yes
boolean
Skip the confirmation required in protected environments
--json
boolean
Output {key, removed, project, environment} as JSON
--flatten
boolean
Keep dependent secrets working by copying this value into each of them
--cascade
boolean
Delete every secret composed from this one as well
-h, --help
boolean
display help for command
varsafe doctor
Diagnose whether the CLI can securely store credentials
varsafe doctor [options]
Flag
Type
Description
--addon-only
boolean
Only verify the native keyring addon loads (tolerate a missing OS backend)
--json
boolean
Emit the report as JSON
-h, --help
boolean
display help for command
varsafe status
Show the credential, the current context, and what is in it
varsafe status [options]
Flag
Type
Description
--json
boolean
Output the status as JSON
-h, --help
boolean
display help for command
varsafe theme
Choose the colour palette (auto, dark, light)
varsafe theme [options] [mode]
Flag
Type
Description
--json
boolean
Output the resolved theme as JSON (never prompts)
-h, --help
boolean
display help for command
varsafe update
Update varsafe CLI to the latest version
varsafe update [options]
Flag
Type
Description
-h, --help
boolean
display help for command
varsafe use
Set default project and environment for commands
varsafe use [options]
Flag
Type
Description
-p, --project <name>
value
Project name
-e, --env <environment>
value
Environment (e.g., development, staging, production)
--clear
boolean
Clear the current context
--json
boolean
Output the resulting context as JSON (never prompts)
-h, --help
boolean
display help for command