Getting Started
Install the varsafe CLI, log in, and get from zero to injected secrets in under five minutes.
By the end of this page you will have the CLI installed, a secret stored in varsafe, and that secret injected into a running process — without a plaintext .env file on disk.
Prerequisites
You need
A varsafe account — sign up free
Supported
Linux and macOS, x64 and arm64, on both glibc and musl — including Raspberry Pi, Graviton and ARM CI runners. Windows works via WSL.
From zero to injected secrets
Install the CLI
The CLI ships as a self-contained binary — it is not published on npm. The installer places it in ~/.varsafe/bin and adds that directory to your shell’s PATH:
curl -fsSL https://varsafe.dev/install.sh | bashOpen a new shell (or source your shell config), then verify:
varsafe --versionThe CLI keeps itself current — run varsafe update any time to install the latest release.
Log in
varsafe loginThis opens your browser and shows a short confirmation code in the terminal. Approve the request in the browser — the code on screen must match the one in your terminal — and the CLI completes automatically. No password ever passes through the terminal.
If a browser isn’t available (headless machine, container, CI runner), use an API token instead — see CLI authentication.
Point at a project and environment
varsafe use -p my-api -e developmentSaved locally, so the commands that follow do not need -p or -e.
Store a secret
Secret values are never accepted as arguments — they would leak into shell history and ps output. Pick whichever input suits you:
varsafe set DATABASE_URLOmit the value and the CLI asks for it with masked input. Best at a terminal.
printf %s 'postgres://app:app@localhost:5432/app' | varsafe set DATABASE_URL --stdinBest in scripts, where there is nothing to type into.
varsafe set TLS_PRIVATE_KEY --from-file ./server.keyBest for multi-line values such as certificates and private keys.
varsafe set API_KEY --from-env BUILD_API_KEYBest in CI, where the value already exists as a variable.
Then confirm it landed:
varsafe list # keys only — values stay masked
varsafe get DATABASE_URL # revealing a value is auditedInject it into your app
This is the core workflow. varsafe run fetches your secrets over TLS and injects them as environment variables into the child process — never written to disk, gone when the process exits:
varsafe run -- npm run devYour app sees DATABASE_URL in its environment; your shell and filesystem never do. To prove it:
varsafe run -- sh -c 'echo "DATABASE_URL is ${DATABASE_URL:+set}"'Expected output: DATABASE_URL is set.
Recap
| Command | What it does |
|---|---|
varsafe use -p <project> -e <env> |
Points the CLI at a project and environment, saved locally |
varsafe set KEY |
Stores a secret, prompting for the value with masked input |
varsafe list |
Shows the keys in scope, values masked |
varsafe run -- <command> |
Runs your app with secrets in its environment, nothing on disk |
varsafe export -o .env |
Writes an encrypted file, for tools that insist on one |
Everything else is a variation on these. varsafe status tells you which credential,
project, and environment the next command will use.
Export when a file is required
Some tools insist on a .env file. varsafe export writes one — encrypted by default, so the file is useless without your environment’s keys:
varsafe export -o .env
Plaintext requires an explicit opt-in with --plain. See encrypted .env files and the export workflow for formats and options.
The same flow, scripted
The script below uses the automation-style login: $VARSAFE_TOKEN holds an API token (in CI, from your CI secret store), $VARSAFE_PROJECT names the project, and $VARSAFE_API_URL points at the API — in normal usage you omit --api-url and the CLI talks to https://api.varsafe.dev.
# docs-test: local-stack
# Store a secret and read it back.
set -euo pipefail
# No `varsafe login` here: VARSAFE_TOKEN is already in the environment and the
# CLI picks it up automatically. That is the only auth path that works on a
# headless runner, where `login` has no keychain to persist to.
# Set your default project and environment once
varsafe use -p "$VARSAFE_PROJECT" -e development
# Store a secret. The value comes in on stdin — passing it as an argument is
# refused, because arguments leak via shell history and `ps`.
printf %s 'postgres://app:app@localhost:5432/app' | varsafe set DATABASE_URL --stdin
# List keys (values stay hidden by default)
varsafe list
# Read a single value (pipe-friendly)
varsafe get DATABASE_URL > /dev/null
echo "OK:quickstart"And the injection flow end to end — note the child process assertion proving the secret arrived:
# 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"Project setup
The dashboard gives you a full overview of your secrets across all projects and environments:

Signing up already created a team, a project, and three environments — which is why the commands above worked without any setup. You only add more when you outgrow them: another project per repository, another environment for a QA or demo stage.
| Environment | -e slug |
Purpose | Protected |
|---|---|---|---|
| Development | development |
Local development | No |
| Staging | staging |
Pre-production testing | No |
| Production | production |
Live systems | Yes |
The slug is what you pass to -e, and it is also the address your secrets are stored
under. Renaming an environment is safe; changing its slug re-points everything that
referred to it, including API-token environment allowlists — so treat the slug as fixed
once secrets exist.
Protected means writes ask for confirmation: varsafe set and varsafe unset require
you to type the environment name, or pass -y in automation. Production starts protected;
you can protect any environment in its settings.
Two things the dashboard does that the CLI does not:
- Bulk import — on the Secrets page, “Add secrets” → “Bulk” takes a pasted or
drag-dropped
.envfile, which is the fastest way to move an existing project in. - Seeing everything at once — every project and environment side by side, plus the audit trail of who read or changed what.
Team collaboration
Invite teammates from the Teams page (“Invite member”) and assign each a role: owner, admin, developer, operator, viewer, or billing. Roles determine read/write access per environment — production is protected by default, restricting writes to owners and admins. See the roles reference for the full permissions matrix.
CI/CD integration
For automated pipelines, use an API token instead of a personal session:
- Go to your team settings in the dashboard
- Navigate to “API Tokens”
- Create a new token
- Add it to your CI/CD secrets as
VARSAFE_API_TOKEN
# GitHub Actions example
- name: Deploy with secrets
run: varsafe run -p my-api -e production -- ./deploy.sh
env:
VARSAFE_API_TOKEN: ${{ secrets.VARSAFE_API_TOKEN }}
The CLI picks up VARSAFE_API_TOKEN automatically — no varsafe login step needed. See CI/CD usage for GitLab CI and more.
Next steps
- Core concepts — Understand teams, projects, environments, and injection
- CLI reference — Full command reference with examples
- Dashboard guide — Managing secrets, teams, and audit logs
- API tokens — Service accounts for CI/CD pipelines
- MCP server — Connect AI agents like Claude Code and Cursor
- Security model — How your secrets stay safe