Configuring an installation
An installation is one application placed on one platform at one
destination. This guide covers wbsp config — how the configuration values
an installation needs reach the platform that will inject them.
When you need this
You do not need it for the everyday flow. wbsp deploy --destination aws
still reads your .env.<destination> beside wbsp.yaml and always will —
nothing about that changed.
You need wbsp config when the values cannot come from a developer's machine:
- deploying a published image on a machine with no repository and no
.env(the split model: publish once, deploy anywhere); - configuring an installation on a platform that is not the WBSP cloud;
- checking whether an installation is completely configured before anyone tries to run it.
The rule underneath: declarations are universal, values are local. What an
application needs travels with its image (see value_specs in
wbsp-yaml-reference.md); the values live only in the
platform that runs it, and never travel between platforms.
Supplying values
# One at a time. Values are SECRET by default.
wbsp config set --destination aws ANTHROPIC_API_KEY=sk-ant-...
# Non-secret values (these may be displayed later).
wbsp config set --destination aws --plain LOG_LEVEL=info
# In bulk — the low-typo path for a first configuration.
wbsp config set --destination aws --from-env-file .env.awsBeside a wbsp.yaml, the tenant and application come from it. Without one,
name the installation yourself:
wbsp config set --tenant philcal --app logwatch --destination appliance \
ANTHROPIC_API_KEY=sk-ant-...Point at another platform with --platform https://api.your-cluster.example
(the default is WBSP_API_URL, else the WBSP cloud).
Two rules the platform enforces:
- A value is secret unless you say otherwise. An unclassified value is over-protected, never under-protected.
- An empty value is refused. Use
wbsp config unset NAMEto remove a value. The platform stores no empty values, so an application never sees a variable that is present-but-blank. - A bulk load is all-or-nothing. One malformed line and nothing is stored, with the line number named — a partial load is worse than a failed one, because you would believe the file went in whole.
Seeing what is stored
wbsp config show --destination awsNAME SECRET VALUE
ANTHROPIC_API_KEY yes (hidden)
LOG_LEVEL no infoSecret values are write-only. They can be set and replaced, never read back — no platform endpoint returns one, so no command, log, or screen share can leak one. If you have lost a secret value, set it again.
Checking completeness
# Beside a wbsp.yaml — declarations come from its value_specs.
wbsp config status --destination aws
# From a published image — no repository needed.
wbsp config status --tenant philcal --app logwatch --destination appliance \
--image registry.wbsp.ai/philcal/8-log-aggregation.../app:latestThe report shows every declared value's state and provenance, and calls out three kinds of problem:
Problems (2):
- ANTHROPIC_API_KEY: missing-required
- AUTH_GOOGLE_SECRET: group "google" is half-supplied — set this, or unset the other member(s)
Extras (stored but not declared): LEGACY_FLAG- missing-required — a declared value with no stored value. A value with
no
value_specsentry counts as required (the feature-086 default). - group-incomplete — some but not all members of an optional group. A half-configured integration is broken, not degraded, so supply them all or none.
- type-mismatch — a stored value that does not parse as its declared
type. Reported for values the platform can show; a secret value's type cannot be checked from outside the platform. - Extras are informational: an application may read more than it declares.
status exits non-zero when there are problems, so it can gate a script.
Who owns an installation's configuration
Every installation has exactly one writer, decided the moment it is
created and never changed afterwards. show and status say which:
Configuration for acme/helpdesk@prod
Mode: operator-managed (from wbsp config set; last written by you@acme.com at 2026-08-10T09:31:04Z)| Mode | Created by | You configure it with | You deploy it with |
|---|---|---|---|
operator-managed | your first wbsp config set | wbsp config set/unset | wbsp deploy --image … |
deploy-managed | someone's wbsp deploy --destination … | their .env.<destination>, then a redeploy | wbsp deploy --destination … |
Read that table in the direction that costs money: wbsp config set is a
create path. It reads like "store a value" and means "choose this
installation's deploy path, permanently". Storing one value to clear a blocker
on an installation that does not exist yet is enough to commit it, and the
ordinary destination deploy is refused for it from then on.
So a config set that would create an installation says so before it does —
from wbsp v0.2.37 onwards. On an earlier CLI there is no warning: the
write happens silently and decides the mode just the same, so check
wbsp --version before relying on being told.
One caveat on that check, if you build from source rather than installing a release: the version is read from the repository when the binary is compiled, so a build made before its release was tagged reports the previous version while already containing the newer code. A released binary is stamped from the release itself and always reports correctly. So the honest test is v0.2.37 or later, or any build from the v0.2.37 commit or newer — and if the two disagree, believe the commit.
Warning: nothing is stored for acme/helpdesk@prod yet, so this write CREATES it
and decides who owns its configuration — permanently. Storing a value here makes
it operator-managed:
- you configure it with `wbsp config set/unset`, not from a .env file;
- you deploy it with `wbsp deploy --image <ref>`, and
`wbsp deploy --destination prod` will be REFUSED for it from now on;
- modes never change, and the only way back is to remove the installation,
which discards every stored value (secrets are write-only and cannot be
read back).
Create it operator-managed and store the value(s)? [y/N]The question is asked only when someone is there to answer it. In a script the
warning still prints and the write proceeds, so a pipeline that worked before
keeps working; --yes skips the question and keeps the warning. Act on it while
it is cheap — at the first write exactly one value is at stake, which is the
least this decision will ever cost to reverse.
Use the wrong one and you are told so, and told where to go instead:
Error: this installation's configuration is maintained by its deploy
(wbsp deploy --destination aws, from .env.aws; last written by dev@acme.com
at 2026-08-10T09:31:04Z). Change the value there and redeploy.
To manage it here instead, remove the installation and recreate it —
modes never change.Reading always works, whichever mode you are in. Secret values stay hidden either way.
Why there is no command to change a mode
Because two flows writing one installation is the failure the rule exists to
prevent. A developer redeploying from a stale .env would silently erase
values you typed, and you would discover it weeks later as a setting that
mysteriously reverted. A take-ownership command would reintroduce exactly
that.
To swap owners, remove the installation and let the other flow create it. The new installation starts with nothing stored — which is the point: there is nothing left to collide over.
Two things look like they do this and do not.
wbsp config unset empties an installation but does not change who owns it: an
operator-managed installation with no values left is still operator-managed,
and will still refuse wbsp deploy --destination. Emptying it looks like a fix
and is not one.
wbsp remove takes away the application, not its configuration record. The
site stops serving, which looks conclusive, and the record survives with its
original mode and timestamp — so the next deploy is refused exactly as before.
(Removing an application does clear the record for an installation created by
an image install, which is why this catches people: it works for one kind and
silently does not for the other.)
The command that ends an installation is wbsp config remove-installation,
below. The mode ends with the installation, and only that ends it.
When the platform cannot be reached
The one-writer rule is checked before a deploy builds anything. If that check
cannot reach a verdict — no platform session, an expired one, or a platform
that does not answer — the deploy is refused, from wbsp v0.2.38 onwards:
the installation's configuration owner could not be verified for acme/helpdesk@prod
(getting bearer token: login session expired).Not knowing who owns an installation is not the same as knowing it is free, and
only one of those is safe to deploy on. Sign in and retry, or — if you are an
operator holding cloud credentials with no platform session at all, which is a
real case during bootstrap and recovery — pass --allow-unverified-owner to
proceed having been told what you are accepting.
Earlier versions let this through silently: the rule rode on a best-effort bookkeeping call, so anything that stopped that call reaching the platform also skipped the rule, and the deploy proceeded against an installation someone else owned. If you deploy with an expired session on an older CLI, check the installation's mode afterwards.
Swapping modes without losing values
Removing an installation discards everything stored in it, and stored secrets are write-only — the platform will never show you a secret value again, so you cannot read one out on the way past. Work in this order and nothing is ever left with no source.
1. List what is stored, and find each value's real source.
wbsp config show --tenant acme --app helpdesk --destination prodEvery name is listed. Non-secret values are shown; secret ones are not, so for each secret answer the question where did this come from originally — a password manager, an identity provider's registration screen, a colleague. A secret with no answer is the one that will cost you: find it before you remove anything, not after.
2. Put the values where the new owner will read them.
- Becoming deploy-managed: write them into the destination's own
.env.<destination>file, which is what the deploy reads. - Becoming operator-managed: have them to hand to type back in.
3. Check nothing is missing, against the list from step 1. This is the step that makes the swap safe, and skipping it is the whole reason the swap has a reputation for being expensive.
4. End the installation record, then create it through the flow you want.
wbsp config remove-installation --tenant acme --app helpdesk --destination prod
wbsp deploy --destination prod # creates it deploy-managedUse that command and not wbsp remove. Removing the application takes the
running application away and leaves the configuration record behind — same
mode, same provenance, still refusing the deploy you were trying to get back
to. An operator who removes the application and redeploys will find themselves
refused exactly as before, with nothing on screen to say why.
If a secret genuinely cannot be recovered — an identity provider that issues a value once and never shows it again is the usual case — it has to be reissued at its source, and whatever holds the old one has to be updated too. That is not a platform step, and it is why the mode is worth getting right at the first write rather than at the swap.
Deploying from a published image
Once an installation is configured, you can run it without a repository at
all — no clone, no wbsp.yaml, no .env. You need one more thing: a PTA
(Permission To Access) for the image, which you obtain from the website. The
platform presents it to the registry gateway for every image access — that is
how image permissions are enforced, for every platform alike — so without it
the deploy is refused before anything is read.
wbsp deploy --image registry.wbsp.ai/<owner>/<repo>/<image>:<tag> \
--pta <token from the website> \
--tenant acme --app helpdesk --destination prod \
--domain helpdesk.acme.example(--pta falls back to the WBSP_PTA environment variable. The image
reference must be the gateway form shown — this path pulls through the
gateway, under your PTA, and nowhere else.)
The platform reads what the image declares, takes the values from that installation, checks one against the other, and starts the application. What it will not do is start it half-configured:
Error: this installation is not ready to run — 1 problem:
- ANTHROPIC_API_KEY: missing-required (operator, required)
Nothing was started, created, or changed.The check uses the same rule wbsp config status uses, so a clean status is
never followed by a configuration refusal — with one exception, stated
plainly: status runs on your machine and cannot see secret values, so it
cannot check their type. The platform can, and will refuse a stored value
that does not parse.
Some things worth knowing:
- The PTA is the image permission. If the website revokes it, the refusal arrives at the next image access — including a node re-pulling the image later. A refused PTA always means "get a fresh one from the website", never something to fix on your machine.
--domainis optional. Without it the application runs with no public route, and the command says so.- Optional values you did not supply are absent, not empty — the application sees no such variable at all — and each one is named in the output.
- The platform's own variables win.
DATABASE_*,REDIS_*,S3_*,PORTand friends are supplied by the platform; a stored value under one of those names is ignored, and the command tells you which. - An image published before declarations existed deploys fine — there is nothing to check. An image with no run manifest at all cannot be deployed this way (it carries no port, no release command, no topology); republish it with a current CLI.
The platform's own configuration page (no CLI needed)
Every installation has a browser page served by the platform itself:
https://<platform-api-host>/ui/apps/<tenant>/<app>/destinations/<destination>/configSign in through the platform's local login; you must hold the installation's
tenant. The form is generated from the image's declarations — one section
per group, prompts as labels, docs links, typed inputs, required markers.
Secret values are write-only there too: a stored secret shows as a mask, and
an untouched masked field means "unchanged". A paste box takes a whole
.env-format block (all-or-nothing — a malformed line names its number and
nothing is stored). Values the platform supplies itself (identity, generated
entropy) appear under "Supplied by the platform" with nothing to type.
A deploy-managed installation renders read-only with the provenance sentence — the page is a view, never a second writer.
Registering the image (and the retained pull credential)
For a form-first flow — configure before the first deploy — tell the platform which image the installation runs:
wbsp config set-image --tenant acme --app crm --destination prod \
--image registry.wbsp.ai/<variant>:<tag> --pta "$WBSP_PTA"The platform retains both. From then on:
- the config page and
wbsp config statusread the image's declarations server-side — no localwbsp.yamlor registry access needed; wbsp deploy --image …needs no--pta: the retained credential is re-presented, so a platform-initiated redeploy (a node replacement, a settings change) succeeds unattended;- a fresh
--ptareplaces the retained one; revocation still bites — a revoked credential fails the next image access exactly as a hand-carried one, andstatusreports it asaccess_denied; - every successful
wbsp deploy --imagerefreshes the retained pair itself.
The credential is stored with a secret's protections and then some: it is
never an entry, so it cannot appear in any listing, be injected into the
application's environment, or round-trip through any surface. wbsp config show reports it as (retained, hidden).
Identity without typing (images that sign users in)
If the image declares an authentication callback
(auth.callbackPath) and identity-kind values, the platform supplies them
at install: it registers one sign-in client for the installation in its
local identity provider, derives the callback from the deploy's --domain
plus the declared path, mints the session secret and stable encryption key,
and stores all of it as ordinary entries. Redeploys register nothing new; a
new domain adds a callback; removing the installation removes the
client. You type only what genuinely only a person has.
Enclaves: isolated by default, shared by name
An image install that names no enclave runs in its own enclave — isolated from every other installation's private services (public routes stay public). To co-locate applications deliberately:
wbsp enclave register --tenant acme shared-suite
wbsp deploy --image … --enclave shared-suiteAn unregistered --enclave name refuses at install. An installation's
recorded placement never moves on redeploy; installations placed before
these defaults keep their placement.
Where the values actually live
On the WBSP cloud, in a Kubernetes Secret per installation, in a namespace
only the platform's own service account can read. On another platform,
wherever that platform stores them — the API and the CLI are identical either
way (see docs/platform-config-api.md for the contract every platform serves,
and docs/config-locator-contract.md for the storage seam).
See also
- wbsp-yaml-reference.md — declaring
value_specsandauth, the other half of this story. - wbsp-client-guidelines.md — how applications consume configuration (plain environment variables; nothing here changes that).