Core concepts
The ideas behind how varsafe works, and the terms the rest of these docs use.
Three ideas carry the whole product:
- One place for every secret. A secret lives in exactly one environment of one project, and everyone reads it from there, so there is no copy to drift or leak.
- Access scoped to the work. People get a role, pipelines get a token, agents get a grant you approve. Each reaches only what its job needs.
- A record of every read and change. Who, which client, from where, and when.
This page explains each in turn. If you have not used varsafe yet, getting started is five minutes.
How secrets are organized
Four things nest, each inside the one before it: a team owns projects, a project has environments, and an environment holds secrets. The same key name in three environments is three separate values under three separate access rules.
Team
acme
Project
api-backend
development
DATABASE_URL
JWT_SECRET
REDIS_URL
staging
DATABASE_URL
JWT_SECRET
REDIS_URL
production
DATABASE_URL
JWT_SECRET
REDIS_URL
Project
web-dashboard
Teams
Your organization in varsafe. Holds the members and their roles, the projects, and the billing.
Projects
An application or repository that needs secrets. Belongs to exactly one team.
Environments
A stage of a project: development, staging, production, or your own.
Secrets
A key and its value in one environment, encrypted at rest and versioned on every change.
Teams
A team is the people who work on the same projects, and the boundary around everything they share.
- Members join by invitation and each has a role. One person can belong to several teams.
- One team’s data is invisible to another’s. Every request is checked by the application, and the most sensitive tables (secrets, tokens, agent grants, invitations, single sign-on settings and the audit log) are also protected by row-level security in the database. The security model has the details.
- Billing and the plan are per team. On the Team plan, members can sign in through your identity provider with SSO (SAML 2.0 or OIDC).
Projects
A project is usually one application or repository. It has its own environments, its own secrets and its own history. Signing up creates a starter project; add one for each application you run.
Environments
Every new project starts with three environments:
| Environment | Purpose | Protected |
|---|---|---|
development |
Local development | No |
staging |
Pre-production testing | No |
production |
Live systems | Yes |
Add your own (qa, demo, preview) whenever you need another stage. The slug, the name you pass to -e, is fixed when the environment is created; you can rename an environment freely, and only its display name changes.
Secrets
A secret is a key and a value:
- Key: starts with an uppercase letter, then uppercase letters, digits and underscores (e.g.
DATABASE_URL), up to 255 characters - Value: up to 64 KB, encrypted at rest in a dedicated secrets vault hosted in the EU
- Versions: every change creates a new version, and the last 10 are kept so you can roll back
Values are stored, not interpreted
A secret’s value is bytes. varsafe stores what you give it and returns exactly that: it does not
expand ${...}, read your shell environment, or substitute anything into a value. A secret whose
value is ${DB_PASS} is a secret whose value is the eight characters ${DB_PASS}, on every read.
So secrets that hold templates as data, such as a Grafana dashboard, a Kubernetes manifest or another tool’s config file, pass through untouched.
Composed secrets
When you do want one secret built from others, create it as a composed secret:
DATABASE_URL = postgres://${DB_USER}:${DB_PASS}@db.internal/app
Composition is something you choose when you create the secret (varsafe set --template in the CLI),
never inferred from what a value happens to look like. Rotate DB_PASS and every secret built from it
follows, so a password lives in one place instead of inside every connection string. Reads return the
resolved value; you get the template only when you ask for it with --source. See
composed secrets.
Who can reach what
People, pipelines and agents each reach varsafe with a credential of their own, and each is limited to its job.
| Role | CLI login | API token | Agent grant | |
|---|---|---|---|---|
| For | Each member of a team | You, through varsafe login |
Something that is not a person: a CI pipeline, a cron job, a backend service, an internal tool | A coding agent, connected over MCP |
| Access | One of six roles, which decides what the member can do in each environment | Read only, Standard (runs and reads values) or Full access, chosen when you approve it; never more than your role | One team, optionally narrowed to chosen projects and environments; read-only, or read and write | A team, and optionally projects and environments; an access level from Read only (names and comparisons, no values) up to levels that also read values and create, change or delete secrets; never more than your role |
| When it ends | Removing the member revokes their CLI logins, agent grants and the API tokens they created, in one step | Revoked when you are removed; a revoked login is refused on its next request | Always expires: up to 90 days on the Developer plan, up to 365 on Team. Revocable at any time, and revoked when its creator is removed | Revoked from Agents in the dashboard, or when you are removed |
People: roles
Each member of a team has one role, and the role decides what they can do in each environment:
Owner
Non-protectedRead and write
ProtectedRead and write
Admin
Non-protectedRead and write
ProtectedRead and write
Developer
Non-protectedRead and write
ProtectedRead-only
Operator
Non-protectedRead-only
ProtectedRead-only
Viewer
Non-protectedRead-only
ProtectedNo access
Billing
Non-protectedNo access
ProtectedNo access
Six roles exist: owner, admin, developer, operator, viewer and billing. Owners and admins manage the team and can change secrets everywhere. Developers change non-protected environments and read protected ones. Operators read everything and change nothing. Viewers read non-protected environments only. Billing members manage the subscription and never see a secret. The full matrix is in the roles reference.
Your CLI login is a credential of its own: when you approve varsafe login, you choose what it may do (Read only, Standard, which runs and reads values, or Full access), and it can never exceed your role.
Pipelines and scripts: API tokens
An API token (vs_at_…) is a credential for something that is not a person: a CI pipeline, a cron job, a backend service reading its configuration at startup, an internal tool.
- It belongs to one team and can be narrowed to chosen projects and environments.
- It is read-only or read and write. The dashboard form starts on read and write, so choose read-only unless the job changes secrets.
- It always expires: up to 90 days on the Developer plan, up to 365 on Team.
- Its reads and changes appear in the audit log under the token’s name, so give each pipeline its own.
- It is stored only as a salted hash, shown once at creation, and revocable at any time.
See API tokens and CI/CD pipelines.
Agents: grants you approve
A coding agent such as Claude Code, Cursor, Codex or VS Code connects over MCP. The first time, your browser shows a consent screen and you decide what the agent may do:
- Which team, and optionally which projects and environments.
- What it may do: an access level, from Read only (names and comparisons, no values) up to levels that also read values and create, change or delete secrets. Values stay out of reach unless you approve a level that includes them.
A grant can never exceed your own role. Every secret operation the agent makes is checked against your role as it stands at that moment, so a downgrade or a removal takes effect at once, and every one is recorded with the agent as the client. You can see and revoke each grant from Agents in the dashboard, where it is listed as a connection.
An agent that runs shell commands can also use the CLI. It then acts with the CLI login on that machine, limited to what you approved for that login, which the agent’s grant does not narrow. Agent safety covers keeping both narrow. varsafe run hands the values to the command it starts, not to the agent, so the agent sees only what that command prints.
When someone leaves
Removing a member revokes their CLI logins, their agent grants and the API tokens they created, in one step. Values they already revealed stay revealed, so rotate those at their source.
How varsafe run delivers secrets
varsafe run is the everyday workflow, for people, pipelines and agents alike:
varsafe run -- npm run dev
- varsafe CLI → varsafe APIAsk for the environmentauthenticated, over TLS
- varsafe API → Secrets vaultFetch the values
- Secrets vault → varsafe APIValues
- varsafe API → varsafe CLIValues, over TLSthe read is audited
- varsafe CLI → Your appStart your app with them in its environmentvarsafe writes nothing to disk
What that gives you:
- Nothing on disk. On your machine the values exist only in the memory of the CLI and of your process. When the process exits, they are gone.
- Only what the command needs.
--include 'API_*'passes a subset of keys to your command;--prefixrenames them on the way in. The CLI still fetches the whole environment, and the audit log records every key it fetched. - Not the token. An API token in
VARSAFE_API_TOKENorVARSAFE_TOKENis removed from your command’s environment, so the script you run gets the secrets but not the credential that fetched them (unless you pass--pass-credential). - Your command, unchanged. It runs directly, with no shell in between unless you ask for one with
--shell, andvarsafe runexits with its exit code. - A record. The read is logged with the key names that were fetched, who asked, and from which client.
The values are encrypted in transit and at rest. The varsafe API decrypts them to serve an authorized request; this is a server-side secrets manager, and the security model explains exactly what that means.
When a tool needs a file
If a tool will only read a file, varsafe export writes one. By default a .env export is encrypted, value by value, to its environment, so the file is safe to commit; --plain writes plaintext, and --tmpfs keeps it in RAM. JSON, YAML and Docker formats are always plaintext. See exporting and encrypted .env files.
Each environment has its own keypair for these files. varsafe holds the private key, protected by a managed key service, and opens a file itself for anyone allowed to read the environment (varsafe run --env-file), so an encrypted export is not end-to-end encryption; see the access model.
Which project and environment a command uses
Most commands need a project and an environment. The CLI checks four sources in order, and the first that answers wins:
1
Explicit flags
-p and -e always win. Give only one inside a repository and the other comes from its .varsafe file (CLI 7.8.1 and later).
-p / -e
2
Local .varsafe file
A small file in your repository, found by searching from the current directory up to the git root.
./.varsafe
3
Saved context
Set with varsafe use outside a repository, and kept for your user.
varsafe use
4
Auto-selection, or a prompt
Selected automatically when only one option exists; otherwise the CLI asks rather than guessing.
interactive
# Choose once
varsafe use -p my-api -e development
# Every command after that uses it
varsafe list
varsafe run -- npm run dev
# Flags override it for one command
varsafe list -p my-api -e staging
Run varsafe use inside a git repository and the choice is written to a .varsafe file at the repository root. It holds only project and environment IDs, no secrets, so you can commit it and everyone who clones the repository starts in the same project. Outside a repository, the choice is saved for your user. varsafe use --clear resets it, and varsafe status shows what the next command will use.
History and rollback
Every save to an environment, of one secret or many, creates a new version of it, and each environment keeps its last 10. A value can be restored while the version that saved it is still among them. In the dashboard you can:
- Restore one secret from its history. Restoring creates a new version with the old value, so nothing is lost.
- Roll back a whole environment from its Environment history: pick a change, preview the result, and undo that change and everything after it.
Each restore and rollback is recorded in the audit log, like any other change.
Audit trail
Every value read and every change is recorded, along with sign-ins, role changes, token and agent activity. An event looks like this (illustrative):
{
"action": "secret.accessed",
"actor": "alice@example.com",
"source": "cli",
"project": "api-backend",
"environment": "production",
"secretKeys": ["DATABASE_URL", "API_KEY"],
"ip": "203.0.113.24",
"timestamp": "2026-02-15T10:30:00Z"
}
The audit log is:
- Append-only: the application can add events but never edit or delete them.
- Searchable: filter by team, action, target and date in the dashboard’s Audit log.
- Exportable: owners and admins on the Team plan can export it for a compliance review.
How long events are kept depends on the plan: 7 days on Developer, 90 days on Team.
Signing in and sessions
You sign in to the dashboard with email and password, GitHub, GitLab, Google or Bitbucket, or a passkey, and can add TOTP two-factor; Team plans add SSO. The CLI signs in through the browser with varsafe login, and its credential is stored only as a salted hash on our side.
Your profile lists your active sessions, and you can revoke any of them. A revoked dashboard session stops working within 30 seconds; a revoked API token or CLI login is refused on its next request. See authentication and the security model.
Related documentation
- CLI reference: every command and flag
- Roles: the full permissions matrix
- Dashboard guide: secrets, history, teams and the audit log
- API tokens: credentials for pipelines and scripts
- AI agents (MCP): connecting an agent on a grant you approve
- Security model: what is encrypted, who can see what, and what we do not claim
- Operations: day-to-day operational workflows