Authenticating
Storing an API key, pointing the CLI at your own deployment, checking that a key still works, and signing out.
Commands on this page: login · logout · whoami · status
A profile is a credential set stored on your machine: an API key plus the deployment host it
belongs to, under a name you choose. The built-in prod profile points at
https://api.pulsvault.com/api/v1 and is the default, so a plain pulsvault login needs no flags:
pulsvault login # paste the key at the prompt; input is hidden
pulsvault get acme-web/prod/product/web/db/passwordWhere there is no terminal to prompt on, pipe the key in — login reads it from stdin:
printf '%s' "$PULSVAULT_KEY" | pulsvault login --profile ci-deploy:::caution Keep the key off the command line
--token pv_live_… works, but the argument is written to your shell history and is readable by every
user on the machine for as long as the process runs (ps, /proc/<pid>/cmdline). The CLI says so
each time it is used. In a pipeline, set PULSVAULT_TOKEN as a masked variable instead; on a
workstation, let login prompt.
:::
login verifies the key against the server before storing it, and prints the identity it
authenticated as:
✓ authenticated to https://api.pulsvault.com/api/v1
as ci-bot@service-account · space acme
can: read-prod (1 of 3 — this key is limited)
Saved credentials for profile "prod" (https://api.pulsvault.com/api/v1) to ~/.pulsvault/configA key the server rejects is not stored — you see rejected this API key (HTTP 401) — nothing was saved immediately, rather than a puzzling failure on some later command.
When the check cannot be completed the key is stored, and the reason is printed as a warning so
"verified" is never assumed: the server has no identity endpoint, the check was rate-limited, the
identity response could not be parsed, or you passed --skip-verify yourself:
pulsvault login --profile mycorp --host https://vault.mycorp.com/api/v1 --skip-verifyUse --skip-verify for offline or bootstrap setup, where the deployment is not reachable yet. It
stores the key without asking the server whether it works, so the first real command becomes the test.
It is a local label in ~/.pulsvault/config; the name never reaches the server. Two things it is
often mistaken for:
- Not a PulsVault account. An API key authenticates as a service account (a machine identity). There is no user login in the CLI.
- Not the
{env}in a path. Which environment you read is decided by the path ({project}/{env}/{namespace}/{secret}), never by the profile —--profile prodcan address…/dev/…paths and vice versa.
Pointing the CLI at your own deployment#
If you run PulsVault yourself — self-hosted, on-premises, or a dedicated instance — give the CLI your own endpoint by naming a profile and passing the API base once:
pulsvault login --profile mycorp --host https://vault.mycorp.com/api/v1--profile mycorp— the local name for this credential set. Any name works; it is yours.--host— the API base including the/api/v1suffix. Needed only the first time: it is saved in the profile, so a later plainpulsvault login --profile mycorprefreshes the key and keeps the host, which means the profile cannot be repointed by accident.
Then make it the default, or select it per command:
pulsvault config use mycorp # every later command uses it
pulsvault --profile mycorp get mycorp-app/prod/api/db/password
PULSVAULT_PROFILE=mycorp pulsvault list mycorp-app/prod/api # e.g. in CISeveral deployments coexist — one profile each, no interference between them:
$ pulsvault config list
* mycorp https://vault.mycorp.com/api/v1
prod https://api.pulsvault.com/api/v1
* = active profile (used when --profile / PULSVAULT_PROFILE is absent)Keys are per-deployment: a key issued by one PulsVault instance never authenticates against another,
and login tells you so immediately instead of storing a key that cannot work.
While testing an endpoint that still has a self-signed certificate, --insecure skips TLS verification
and prints a warning on every run. Never use it against a production endpoint.
Selecting a profile#
Once several profiles exist, which one a command uses — and which project its paths are relative to —
is its own subject: see Profiles & defaults for pulsvault config,
the config file, and the full resolution order. The short version is
--profile → PULSVAULT_PROFILE → the config's active profile.
login --profile mycorp writes that profile but does not repoint the other commands — except for the
very first profile stored on a machine, which also becomes the active one. Afterwards use
pulsvault config use mycorp, login --set-default, or pass --profile mycorp per command. If a
command resolves to a profile with no token, the error names the profiles that do have one.
Because that gap is easy to miss, login says so when it applies:
Saved credentials for profile "mycorp" (https://vault.mycorp.com/api/v1)
note: your active profile is still "prod", so commands without --profile keep using it.
run `pulsvault config use mycorp` to switch, or re-run login with --set-default.Working with several keys#
One profile holds one key for one deployment. That pairing is not a limitation to work around — a key is issued by a specific PulsVault instance and carries a specific set of permissions, so the two travel together. More keys simply means more profiles.
A realistic setup — a personal key, a narrow CI key, and a customer deployment:
pulsvault login --profile ci-deploy # read-only key, production paths
pulsvault login --profile mycorp --host https://vault.mycorp.com/api/v1$ pulsvault config list
* prod https://api.pulsvault.com/api/v1
ci-deploy https://api.pulsvault.com/api/v1
mycorp https://vault.mycorp.com/api/v1From there, Profiles & defaults covers choosing between them — per command, per shell, or persistently.
ci-deploy, release-bot, mycorp age well. A profile named after an environment invites the
assumption that switching profile switches environment — it does not. The environment is a segment
of the path ({project}/{env}/…), so one key can address several environments, and two keys can
address the same one. Keeping the two ideas separate is what stops a "staging" profile from being
trusted to be incapable of touching production.
A bare login re-credentials the profile you are already using. If ci-deploy is active,
pulsvault login replaces that key and leaves the others alone — which is what
rotating a key looks like. Name a different profile with --profile to add or refresh one of the
others.
Two habits worth having when several keys are in play:
pulsvault whoamibefore anything destructive. Its first line is the profile, and the rest is the identity and exactly what that key may do. Cheaper than discovering it from a failed write.pulsvault config remove <name>once a key is revoked. A stored credential that no longer works is a confusing failure waiting to happen; removing it makes the next command's error name the profiles that do have a working key.
Checking a key without reading a secret#
Two commands answer "is this credential still good?", and both exit non-zero when it is not — so a pipeline can gate on them:
pulsvault status # can I reach the host, and does the key authenticate?
pulsvault whoami # who am I, what may this key do, when does it expire?status is the first thing to run when a pipeline breaks. It separates cannot reach the host from
token rejected from token expired — three failures that otherwise look identical from inside a
build log:
$ pulsvault status
profile: prod
host: https://api.pulsvault.com/api/v1
status: connected (HTTP 200)whoami reports what this key may do, which is not always what its machine identity may do — a key
can be narrowed to a subset of its service account's policies:
$ pulsvault whoami
profile: prod
host: https://api.pulsvault.com/api/v1
service account: ci-bot@service-account
space: acme
type: CI
permissions: read-prod (1 of 3 — this key is limited)
last used: 2026-08-16T09:12:44Z
key: pv_live_abcd…
key expires: 2026-09-01T00:00:00ZAdd -o json for the full record, including the soonest grant expiry. A permission can lapse while the
key itself stays valid, which otherwise surfaces as an unexplained outage.
status -o jsonalways exits 0. Under structured output the command reports the outcome in the payload and returns success, sopulsvault status -o json && ./deploy.shdeploys even when the key was rejected. Gate on the plainpulsvault status, or parse theconnectedfield:pulsvault status -o json | grep -q '"connected": true' || exit 1whoamiexits 0 against a server with no identity endpoint. An older deployment returns 404, and the CLI falls back to printing your local profile, host and key prefix with a note on stderr. Exit 0 there means "the key was not checked", not "the key is good". A rejected or expired key still exits non-zero.
When the refusal is the subscription, not the key#
A 403 on the machine plane used to mean one thing: this key's policies did not cover the request.
It can now also mean the plan covering the secrets has lapsed — the server enforces the same
post-expiry ceremony on /api/v1/secrets/** that it enforces in the console. The two look identical
in a build log and want opposite fixes, so the CLI tells them apart and says which one you are
looking at.
There are two states, and the difference decides whether you are facing a deadline or an outage.
Read-only window. Reads keep working; only writes are refused:
$ pulsvault set web/prod/db/password 'hunter2'
error: Your account is read-only. Subscribe to a plan to make changes, or export your data. (403)
this is the subscription, not this key
The plan covering these secrets has ended and the account is in its read-only window.
Reading secrets still works — only writes are refused, so a pipeline that just fetches
values keeps deploying.
Renew the plan in the console to restore writes. Nothing here needs changing.A deployment pipeline that only fetches values is unaffected for the whole window. A pipeline that
writes them — a rotation job, an import, a bootstrap step — fails from the first day of it.
Locked. The window has closed and reads stop too:
$ pulsvault get web/prod/db/password
error: Your account is locked. Your data is safe — subscribe to a plan to restore access. (403)
this is the subscription, not this key
The read-only window has closed and the account is locked, so reads are refused too.
Nothing has been deleted. Subscribe again in the console and access returns at once.
This key stays valid throughout — do not revoke or re-issue it.Both messages exist because the obvious reaction to a 403 is to suspect the credential — revoke it,
mint a new one, re-run. That is wasted work here, and it makes the next incident harder to read: the
key is intact, its policies are intact, and a fresh one is refused in exactly the same way. Nothing on
this machine needs changing. The fix is on the account, in the console.
pulsvault login reflects this too. It verifies the key by default, and against a locked account it
cannot read the identity endpoint — so it says the key could not be verified and stores it
unchanged rather than reporting it as rejected. You will not have to log in again after renewing.
Signing out#
pulsvault logout # clear the active profile's token, keep its host
pulsvault logout --profile ci # a specific profile
pulsvault logout --all # every stored token on this machinelogin writes the key to ~/.pulsvault/config with mode 0600 — readable only by your user account —
and re-applies that mode on every write, so a config file whose permissions were loosened is
repaired the next time you log in rather than trusted as-is. The
CLI is a single static binary with no OS-keychain dependency, so it behaves the same on a laptop, in a
container, and on a CI runner. The key is a machine credential — it carries only the actions and
paths its policies grant, it can never unlock a human vault, and it is revocable at any moment from
Machine Identities. Secret values never touch disk.
In CI, set PULSVAULT_TOKEN as a masked variable and skip login entirely — then no credential is
written at all. On a shared or retired machine, pulsvault logout removes the stored token.
If a profile has no host — a typo like --profile prd, or a profile you never gave a --host — the
command fails with an error instead of resolving to production. Re-running pulsvault login on an
existing profile also keeps that profile's host, so it cannot be repointed by accident. Commands that
write print their target as → mycorp https://vault.mycorp.com/api/v1 on stderr before they run; -q
suppresses it.
Related#
- Profiles & defaults — choosing between stored profiles, and setting a default project
- API keys — issuing a key
- Access policies — what a key may do
- Command reference — exact flags