Skip to content
Solo ProTeamBusinessEnterprise

Profiles & defaults

Which credential a command uses and what its paths are relative to — the config file, the resolution order, and the difference between a profile and a project.

Commands on this page: config

Before any command touches the network, two things have already been decided: which stored credential it authenticates with, and what a short path is relative to. Both are settings you control, both are stored per profile in ~/.pulsvault/config, and both are managed by one command — pulsvault config.

Getting them right is what turns this:

bash
pulsvault get acme-web/prod/product/web/db/password

into this:

bash
pulsvault get prod/product/web/db/password

Profile or project?#

They look alike on a command line and are nothing alike underneath, which makes this the easiest mistake to make:

What it is How you choose it
Profile a stored credential — an API key plus the deployment it belongs to --profile, PULSVAULT_PROFILE, config use
Project the first segment of a secret's path --project, PULSVAULT_PROJECT, config project

Selecting a profile never changes which project you address, and setting a project never changes which key you use. So pulsvault config use acme-web does not make pulsvault list prod/api work — that path still begins with a project name, and the CLI will look for a project called prod.

Confusing the two produces a specific error, which names every profile you do have:

text
$ pulsvault --profile acme-web whoami
error: profile "acme-web" has no host — configured profiles are: "mycorp", "prod"
  · a PROFILE is a stored credential (an API key + its deployment)
  · a PROJECT is the first segment of a path: `<project>/<env>/<namespace>/<secret>`
  · to address a project, keep your profile and name it in the path, or pass --project acme-web
  · to create it, run `pulsvault login --profile acme-web --host URL`
An unknown profile fails rather than falling back

A profile the CLI does not recognise — a typo like --profile prd, or one you never gave a --host — errors out instead of resolving to production. That is deliberate: inheriting the production host on a typo is how a "staging" command writes a live value.

Choosing which credential a command uses#

config list is the inventory. It marks the active profile, resolves each host, and says which profiles are unusable and why:

text
$ pulsvault config list
* prod           https://api.pulsvault.com/api/v1
  ci-deploy      https://api.pulsvault.com/api/v1   (no token — run `pulsvault login --profile ci-deploy`)
  mycorp         https://vault.mycorp.com/api/v1

* = active profile (used when --profile / PULSVAULT_PROFILE is absent)
  /home/you/.pulsvault/config

A profile keeps its host after pulsvault logout, so appearing in this list says nothing about being usable — the (no token — …) marker is the part that matters. This is where to look after a command fails with no API token.

The rest of the command surface:

bash
pulsvault config                  # same as `config list`
pulsvault config use ci-deploy    # make it the active profile
pulsvault config remove old-ci    # forget a profile's token, host and project
pulsvault config rm old-ci        # `rm` is accepted for `remove`
pulsvault config path             # print the config file's location and nothing else

config use refuses a profile that holds no token, because making an empty profile active would break every later command with a puzzling error rather than this one:

text
$ pulsvault config use ci-deploy
error: profile "ci-deploy" has no stored credentials — run `pulsvault login --profile ci-deploy`

Three ways to pick a profile, in increasing order of how long the choice lasts:

bash
pulsvault --profile ci-deploy whoami          # this command only
PULSVAULT_PROFILE=ci-deploy pulsvault list acme-web/prod/api   # this shell, or this CI job
pulsvault config use ci-deploy                # until you change it again

Resolution is --profile → PULSVAULT_PROFILE → the config's active profile → prod. Global flags work on either side of the command name, so pulsvault --profile mycorp whoami and pulsvault whoami --profile mycorp are the same invocation.

`config` reads its own target differently

Every command except config itself honours PULSVAULT_PROFILE. The config subcommands do not: config project looks at --profile and then the stored active profile, and config list, use and remove look only at the stored active profile.

So PULSVAULT_PROFILE=mycorp pulsvault config project acme-web writes the project onto whichever profile is active, not onto mycorp. Name it explicitly instead:

bash
pulsvault config project --profile mycorp mycorp-app

Setting a default project#

Every path is {project}/{env}/{namespace}/{secret} — the scheme is explained in Paths, plans & roles. Store a project and you may drop that leading segment:

text
$ pulsvault config project acme-web
default project for profile "prod" is now "acme-web"
  paths may now omit it: `pulsvault list <env>/<namespace>` addresses acme-web/…

It is stored per profile, because a key issued by one deployment usually addresses one project there — someone holding keys for two deployments wants a different default in each, not one that follows them across. All three forms:

text
$ pulsvault config project                 # what is it currently?
acme-web (profile "prod")

$ pulsvault config project -               # clear it
cleared the default project for profile "prod"

$ pulsvault config project
no default project for profile "prod" — paths must name one: <project>/<env>/<namespace>/<secret>

A project is one path segment, and passing more than one is refused rather than silently stored:

text
$ pulsvault config project acme-web/prod
error: a project is a single path segment, got "acme-web/prod" — pass just the project, e.g. `pulsvault config project acme-web`

What the prefix actually does#

The rule is deliberately simple, and it never guesses: a path is left alone if it already starts with your configured project; otherwise the project is prepended. With acme-web stored, all four of these address the same secret:

bash
pulsvault get prod/product/web/db/password                        # prefixed
pulsvault get acme-web/prod/product/web/db/password               # already qualified, untouched
pulsvault --project acme-web get prod/product/web/db/password     # flag wins over the stored value
PULSVAULT_PROJECT=acme-web pulsvault get prod/product/web/db/password

The CLI does not try to work out whether your first segment is a project or an environment. That would depend on server state, and guessing wrong means reading the wrong project's secret.

"Left alone" means *your* project, not *any* project

The check is against the one project you configured, so a path naming a different project is prefixed like anything else — with acme-web stored, other-team/prod/api resolves to acme-web/other-team/prod/api, not to other-team. To reach another project, either clear the default, override it for one command with --project other-team, or write the path out in full from a profile that has no project set.

With no project configured anywhere, paths must be fully qualified, and a short one fails immediately:

text
error: expected <project>/<env>: pass a fully-qualified path, or set --project / PULSVAULT_PROJECT

What the config file holds#

~/.pulsvault/config is a flat key=value text file — no sections, no quoting, comment lines start with #. pulsvault config path prints its location, which is the only reliable way to find it on a machine where $HOME is unusual:

bash
pulsvault config path

A machine with three profiles looks like this:

text
# PulsVault CLI config
profile=prod
profile.ci-deploy.host=https://api.pulsvault.com/api/v1
profile.ci-deploy.token=pv_live_…
profile.mycorp.host=https://vault.mycorp.com/api/v1
profile.mycorp.project=mycorp-app
profile.mycorp.token=pv_live_…
profile.prod.host=https://api.pulsvault.com/api/v1
profile.prod.project=acme-web
profile.prod.token=pv_live_…

Four keys exist, and nothing else is written:

Key Holds Written by
profile the active profile — which one commands use when none is named config use, and the first-ever login
profile.<name>.token that profile's API key, in plain text login
profile.<name>.host that profile's API base, including /api/v1 login
profile.<name>.project that profile's default project config project

Two consequences of it being a plain file the CLI rewrites whole:

  • Editing it by hand works, but comments do not survive. Every write serializes the whole file from scratch, so anything you add beyond the four keys is dropped the next time login or config runs. Prefer the commands.
  • Only login creates the directory. config and logout write into ~/.pulsvault/ but never create it, so config project on a machine that has never logged in fails with a filesystem error. Run pulsvault login first.
The file is mode 0600, and the CLI checks

login writes it readable only by your user account. If the permissions are ever loosened, every command warns until you fix it:

text
warning: /home/you/.pulsvault/config is mode 0644 and holds an API key in plain text — run `chmod 600 /home/you/.pulsvault/config`

Any command that writes the file also repairs it — login, logout and config replace it with a fresh 0600 file rather than truncating the loose one in place. The warning is therefore about the window before that next write, which is exactly when a read-only command like get is exposing the key.

Secret values never touch this file, or any file — only the API key does. See Authenticating for what that key can and cannot reach.

Resolution, all four settings#

Each setting resolves independently, first match wins:

Setting 1st 2nd 3rd 4th
profile --profile PULSVAULT_PROFILE profile= in the config prod
token --token PULSVAULT_TOKEN profile.<p>.token — fails with no API token
host --host PULSVAULT_HOST profile.<p>.host the built-in host for a known profile name
project --project PULSVAULT_PROJECT profile.<p>.project — paths must be fully qualified

The profile resolves first, and the other three key off it. --profile mycorp therefore changes which token, host and project are read out of the file — while an explicit --token, --host or --project overrides whatever that profile stored.

The host is also validated before the token, so a mistyped profile name reports that rather than complaining about a missing credential.

A stored key stays with its host. Overriding the host with --host or PULSVAULT_HOST needs a key given alongside it, through --token or PULSVAULT_TOKEN. On its own, the override is refused rather than sending the profile's stored key to a server it was never verified against:

text
error: refusing to send profile "prod"'s stored key to https://vault.example.com/api/v1 — it belongs to https://api.pulsvault.com/api/v1
  · pass the key for that host with PULSVAULT_TOKEN (or --token)
  · or store one for it: pulsvault login --profile <name> --host https://vault.example.com/api/v1

A stray PULSVAULT_HOST left in a .envrc or a CI setting used to be all it took to hand a production key to another server. Naming the profile's own host is not an override and is accepted.

The host must be https://. Plain http:// is refused for anything but the machine you are on, because every request carries the key as a bearer token and http would put it on the network in clear text. A redirect from https to http is refused for the same reason.

In CI: no config file at all#

Skip login entirely and set environment variables. Nothing is written to disk, which is what you want on a runner whose filesystem outlives the job:

bash
# PULSVAULT_TOKEN comes from a masked CI variable — the runner already has it in its environment
export PULSVAULT_PROJECT=acme-web
pulsvault get prod/product/web/db/password

Name the CI variable PULSVAULT_TOKEN and the CLI reads it with no shell at all; Using in CI has the per-platform setup for GitLab and GitHub.

These four are the complete set — the CLI reads no other environment variables:

Variable Replaces
PULSVAULT_TOKEN the stored API key
PULSVAULT_HOST the profile's API base
PULSVAULT_PROFILE which stored profile to use
PULSVAULT_PROJECT the stored default project

--insecure, -q and -o are flags only; there is no environment variable for any of them.

In a pipeline, prefer the variables to a profile

A profile is convenient on a laptop, where you set it once and forget it. In CI the opposite is true: PULSVAULT_TOKEN plus PULSVAULT_PROJECT makes the job's credential and scope visible in the job definition, leaves nothing behind on the runner, and cannot drift from a config file written by some earlier build.

Was this page helpful?