Useful Commands
A scenario-based reference for the commands an application developer uses to
develop, deploy, and operate their app on the WBSP platform — all through the
wbsp CLI — organised by environment:
compose/dev/standalone— apps deployed on your machine (whole stack in containers, IDE + data-services only, or a single all-in-one container)aws— apps deployed to the platform EKS clusteraws.on-demand— apps deployed as on-demand AWS Lambda functionsaws.demo— throwaway sample-mode instances (Lambda, sample data baked in)universal— publish an appliance-runnable (multi-architecture) image; deploys nothing
Each section is self-contained — commands are repeated where they apply to more
than one environment. Operator commands that need direct AWS/cluster access
(wbsp-platform, kubectl, aws, terraform) are a separate, operator-only
concern and are not covered here.
Deploying and lifecycle. Configs declare a top-level
destination:block and deploy with--destination <name>(reading.env.<destination>). Lifecycle commands (status,logs,remove,stop) scope by--enclave <name>instead (auto-detected from deployment state). See the application creator guide and wbsp-yaml-reference.md.
The binaries
Built into bin/ in the repository (rebuild after any Go change — see
install-binaries):
| Binary | Role |
|---|---|
wbsp | Deploy and manage applications — authenticate (login, logout, whoami, token), deploy & lifecycle (deploy, list, status, logs, stop, remove, services, prepare, dump-data), database (db …), and demos (demo …) |
wbsp-platform | Operator tooling — provision/destroy infrastructure, platform health, cleanup |
wbsp-api | The platform API server (runs as a platform component) |
Conventions used below
wbspreads a.envbeside the app's yaml (it never reads platform config).- How
wbspauthenticates to the platform (login, tokens, or direct AWS) is its own topic — see Credentials next. - Naming: an app deploys into namespace
wbsp-<tenant>-<enclave>-<app>with workload<tenant>-<app>. The enclave is the isolation group the app was placed in, and it defaults to the tenant name — which is why a namespace often repeats it, as inwbsp-twist-twist-newsletter. A parallel-services environment uses namespacewbsp-svc-<tenant>-<env-name>.
Credentials — three ways to authenticate
wbsp can reach the platform three different ways. As a normal developer you
use only the first (wbsp login); the other two exist for CI and for
operators.
1. wbsp login — the normal developer path (no AWS keys)
A browser OIDC sign-in against HAP (the platform's auth server). One command sets up everything — the API, git, and container-registry credentials:
wbsp login # opens your browser; signs in to tenant `wbsp` (website accounts)
wbsp login --tenant twist # …or sign in to ONE named tenant
wbsp login --destination twist # …or read the tenant a destination declares
wbsp login status # every tenant you hold a session for
wbsp logout # end every session (--tenant <name> for one)You sign in to a tenant. A session is valid only for the tenant that issued
it, so sign in once per tenant you work in — sessions are kept side by side.
--tenant names the tenant; --destination reads the tenant: a destination
declares in wbsp.yaml. They are not interchangeable: a destination's name
is not its tenant (a destination called aws may well declare tenant
philcal). With neither flag, you sign in to tenant wbsp — the wbsp.ai
website's user namespace, where an account created on the website lives — and
the CLI says so, listing any other tenants your project's destinations reach
so you can re-run with --tenant if you meant one of those.
- Caches one API token per tenant under
~/.config/wbsp/tokens/. Every API-path command (deploy,list,status,logs,remove,demo …) sends it to the platform API (https://api.wbsp.aiby default;WBSP_API_URLoverrides) — no AWS credentials required. - Also registers
wbspas your git + docker credential helper for the platform's hosts, sogit push,docker push, andwbsp deployall work off that one login — each push asks the helper for a freshly-issued token (nothing static is stored in your keychain). The CLI silently renews your session from its refresh token, so you rarely sign in again. For this to work,wbspmust be on yourPATH(git and docker invoke it by name).
No configuration is needed against the wbsp.ai platform — the API endpoint, login issuer, and CLI client id are all built-in defaults. Self-hosted platforms only: override them once (values from your operator) in your shell profile:
export WBSP_API_URL=<your wbsp-api endpoint> # default https://api.wbsp.ai
export WBSP_HAP_BASE_URL=https://<your-hap> # the provider's base — NO /t/<tenant>The CLI's sign-in client is built into every HAP tenant, so there is no client
id to configure any more (WBSP_CLI_CLIENT_ID is only for a provider that
lacks the built-in one).
Being signed in isn't the same as being allowed: the tenant's own administrator grants you the deployer role, in HAP's own screens — no platform operator is involved. A refusal tells you exactly which tenant, which role, and where an administrator of that tenant can grant it. Full walkthrough: HOWTO-bootstrap-developer.md.
2. WBSP_API_TOKEN — one secret for CI / non-interactive
When there's no browser (CI, scripts), supply one bearer instead of
wbsp login: WBSP_API_TOKEN, an access token minted by a service
account at the identity provider — an application in the tenant the
pipeline acts on, created by that tenant's administrator with the scopes the
work needs (wbsp-platform:deploy for a pipeline that deploys). The pipeline
mints it with the client-credentials grant at the start of a job:
export WBSP_API_TOKEN=$(curl -sS -X POST https://hap.wbsp.ai/t/<tenant>/oauth2/token \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \
| jq -r .access_token)
wbsp deploy --destination aws-prodThe token serves one tenant (the one in its issuer) and lives an hour by default, so mint per job rather than storing one. Revoke by disabling the application at the provider — effective within one token lifetime.
The platform itself mints nothing (feature 093): wbsp token create and the
platform-signed CI tokens it produced are gone. The API accepts a service
account's token everywhere a login session is accepted. The registry and git
gateways are the website's, and accept what the website configures them for —
today that is people's CLI sessions; a pipeline that pushes images with a
service-account token needs the website team to admit that audience.
Retired (feature 075):
WBSP_REGISTRY_TOKENand per-developer personal access tokens (PATs) are no longer accepted — settingWBSP_REGISTRY_TOKENis now a hard error pointing you here. UseWBSP_API_TOKEN(a service account's token) orwbsp login(interactive). SSH keys remain available for plaingitaccess.
Override hosts if your platform uses different domains: WBSP_REGISTRY_HOST,
WBSP_GIT_HOST. Full detail:
credential-less-deploy.md.
wbsp whoami — who am I, what am I working on, which service?
The one command that answers all three at once. Run it whenever a git push,
wbsp deploy, or catalog lookup is rejected and the reason isn't obvious.
wbsp whoami # just the variant UUID (script-friendly)
wbsp whoami --user # just the signed-in account (script-friendly)
wbsp whoami --all # every field, labelled (-v is an alias)
wbsp whoami --json # every field as parse-able JSON--all prints nineteen fields in four groups:
user: philcal@mac.com
sub: d8f40197-7a4f-45bd-af04-fe6b8f2aebc9
admin: yes
tenants: wbsp
source: hap
issuer: https://hap.wbsp.ai/t/wbsp
expires: 2026-08-03T17:07:12+08:00 (in 55m)
variant: 64656751-e7f1-43d7-b3aa-f9723170e4d5
name: core
project: 112-school-information-system
slug: core
url: https://wbsp.ai/catalog/school-information-system
recorded: (not recorded in wbsp.yaml)
owner: philip-callender-hh70
repo: 112-school-information-system-core
remote: https://git.wbsp.ai/philip-callender-hh70/112-school-…-core.git
api: https://api.wbsp.ai
version: v0.2.18
token: session
warning: signed in as philcal@mac.com but the repository is owned by philip-callender-hh70That last warning is the point of the command. The most common baffling
403 on this platform is signing in as one account while the repository belongs
to another — the two identifiers look similar enough that nobody spots it by
eye. whoami compares them for you. It also warns when the variant: recorded
in your wbsp.yaml has drifted from the one your remote actually resolves to.
Warnings never change the exit code — this command reports, it never gates.
Every field is optional. Not signed in, no network, not in a git
repository — --all and --json still print everything they can and say
why the rest is missing, then exit 0. Only the two single-value forms (bare and
--user) fail loudly, so a script never captures an empty string.
For automation, use --json. Its keys are a documented contract; the
labelled output is for humans and may change between releases.
wbsp whoami --json | jq -r .user.email
wbsp whoami --json | jq -r '.warnings[] | "\(.field): \(.reason)"'
# The bare form is what you patch into wbsp.yaml:
yq -i ".variant = \"$(wbsp whoami)\"" wbsp.yaml| JSON key | Meaning |
|---|---|
user.email / user.sub | The account that would be used on the next API call |
user.authority / user.kind / user.tenants / user.source | From the platform: the verbs the credential carries in its tenant, whether a person or a service account holds it, the tenant itself |
user.issuer / user.expires | Which HAP tenant issued the session, and when it lapses |
variant.id … variant.url | The catalog variant this repository resolves to |
variant.recorded | The variant: currently written in ./wbsp.yaml |
repo.owner / repo.name / repo.remote | What the git remote points at |
environment.api | Which platform API you are talking to |
environment.version | Your wbsp build — put this in bug reports |
environment.token | session (from wbsp login) or env (WBSP_API_TOKEN) |
warnings[] | {field, reason} — unavailability and mismatches alike |
Unavailable fields are null, never omitted, so the document's shape is safe
to parse in every state. user.tenants distinguishes null ("unknown") from
[] ("member of no tenant").
With
WBSP_API_TOKENset, the identity reported is that token's, not your cached login — because that token is what every call actually presents.environment.tokentells you which is in play. Without it,--useranswers from the local cache and makes no network call at all.
3. AWS_PROFILE + --backdoor — direct AWS (operators only)
Bypasses wbsp-api and drives AWS directly with your own AWS credentials
(build + push to ECR and roll out on EKS yourself). For bootstrap, recovery,
and deep debugging — a normal developer never needs this.
export AWS_PROFILE=<profile> # your AWS credentials
wbsp deploy --config wbsp.yaml --destination prod --backdoor--backdooris a persistent flag on any AWS-typed command; it always requiresAWS_PROFILE(missing it is an immediate error). Region, account, ECR, and the EKS endpoint are auto-derived from the profile.- Without
--backdoor, the default (API) path needs neitherAWS_PROFILEnor AWS keys — use path 1 or 2.
local-machine types — compose / dev / standalone
Apps run on your own machine. A destination's type: in the app's wbsp.yaml
picks one of three deployment shapes:
compose— the whole app stack runs in containers (app + a per-app PostgreSQL/Redis), production-parity.dev— ONLY the data services run in containers; you run the app from your IDE (live reload). The deploy writes a.envnext to the app with discreteDATABASE_*/REDIS_*values pointing atlocalhoston derived ports —sourceit and start your app.standalone— app + DB + Redis inside ONE ephemeral container.
All three share the same contract: deploy with
wbsp deploy --destination <name>, expose the same discrete
DATABASE_* / REDIS_* env vars as aws (no DATABASE_URL/REDIS_URL), use
no Traefik (routes are ignored), and host-publish ports derived from the
project number (Postgres 54NNN, Redis 63NNN, the app from its declared
port; values above 65535 fold into [49152, 65535]).
One-time setup — the shared Docker network
docker network create wbsp-sharedThis network is operator-managed — the compose provider never creates or
destroys it. Every compose-deployed app attaches to it for cross-stack
reachability (HAP, shared services).
Basics — deploy, list, status, remove
# Deploy / redeploy an app (the destination's type selects compose/dev/standalone)
wbsp deploy --config path/to/wbsp.yaml --destination dev
# Deploy AND write a deployment-diagram prompt for this app
# → a text file with an image prompt (paste into Google "Nano Banana") plus a
# mermaid block (render in VS Code / GitHub / mermaid.live). No secrets in it.
wbsp deploy --config path/to/wbsp.yaml --destination dev --diagram my-app.diagram.txt
# List local-machine apps. `local` is the umbrella (like bare `aws`): it shows
# EVERY local deployment — compose + dev + standalone — in one listing, each row
# labelled with its type. Name a single type instead to narrow to just that one.
wbsp list --provider local # all local types at once
wbsp list --provider local --tenant <tenant> # scope to one tenant
wbsp list --provider local --json # machine-readable (type per entry)
wbsp list --provider compose # just one type (compose / dev / standalone)
wbsp list --provider compose --health # also probe each app
# Show one app's status (auto-detects destination if omitted)
wbsp status <app-name>
wbsp status <app-name> --json
# Stop an app but KEEP its data (database + Redis preserved)
# The app stops running; deploy it again with the same name to reconnect to
# the same database. Use this to pause without losing data.
wbsp stop <app-name> --tenant <tenant>
wbsp deploy --config path/to/wbsp.yaml --destination dev # restart → same DB
# Remove an app and all its resources (DELETES the database — data is lost)
wbsp remove <app-name> --tenant <tenant>
wbsp remove <app-name> --tenant <tenant> --force # no promptDev — run the app from your IDE against containerised data services
With a type: dev destination, the deploy brings up DB/Redis in containers and
writes a .env next to the app (discrete DATABASE_* / REDIS_* on
localhost, derived ports). There is no app container and no Traefik — you run
the app yourself from your IDE.
The platform derives ports for the data services; it cannot derive one for
your app, because it does not start it. Pass the derived port yourself, so
your project does not collide with every other one on the machine — for project
356, next dev -p 30356 and the API on 80356. Moving to a different port
needs no HAP change: the shared test client accepts the standard callback paths
on localhost at any port, so it is an AUTH_REDIRECT_URI edit in .env.dev
and nothing else. If your front end and API are separate processes they are two
origins here but one in the cloud — see Running under dev in
wbsp-client-guidelines.md for what that breaks.
# Bring up DB/Redis + write the .env, then run your app from the IDE
wbsp deploy --config path/to/wbsp.yaml --destination dev
source .env && <run your app from the IDE> # e.g. npm run dev
# Stop the dev environment but KEEP its data
wbsp stop <app-name> --tenant <tenant>
# Tear it down and DELETE its data
wbsp remove <app-name> --tenant <tenant>Debugging — logs
wbsp logs <app-name> # last 100 lines
wbsp logs <app-name> --lines 500
wbsp logs <app-name> --follow # streamDebugging — connect to the local platform database
The platform Postgres runs as a docker-compose service (default container
wbsp-postgres-1, user wbsp_admin, password wbsp_local_dev, port 5432).
# psql via the host (if you have psql installed)
PGPASSWORD=wbsp_local_dev psql -h localhost -p 5432 -U wbsp_admin -d <db-name>
# or exec into the container
docker exec -it wbsp-postgres-1 psql -U wbsp_admin -d <db-name>
# list databases (one per app with database.enabled)
docker exec -it wbsp-postgres-1 psql -U wbsp_admin -c '\l'Debugging — inspect the app container directly
docker ps # find the container
docker logs -f <container>
docker exec -it <container> shSample data (shared with aws.demo)
# Dump the app's local DB to SQL seed files (→ sample-data/ next to the config)
wbsp dump-data --config path/to/wbsp.yaml
wbsp dump-data --config path/to/wbsp.yaml --data-only --exclude-table audit_logCompose specifics — files, URLs, and limits
A compose deploy generates wbsp.docker-compose.yml next to your wbsp.yaml,
auto-seeds POSTGRES_PASSWORD / REDIS_PASSWORD into the destination's
.env.<destination> on first deploy, and advertises the app at
<app>.<projectNo>.wbsp.local (stable across machines). To resolve
*.wbsp.local, either point your workstation at a wildcard-resolving DNS
provider (localtest.me-style, recommended for repeat use) or add the per-app
/etc/hosts line the deploy output prints when resolution fails.
Local-machine limits vs production: HTTP only on host ports (no Traefik/TLS/WAF
edge behaviour), no s3: { enabled: true } on the compose provider (the
deploy fails closed), and no Kubernetes features (namespaces, NetworkPolicies,
service accounts) — apps that depend on those need a real cluster.
An app's own docker-compose services (using, not providing)
If your app uses extra services for local development, run them with the app's
own docker-compose.yaml (this is separate from the platform):
docker compose up -d
docker compose ps
docker compose logs -f <service>
docker compose down # stop (keeps named volumes)
docker compose down -v # stop AND delete volumes (data loss)To run those same services on AWS beside a deployed app, see Parallel services under
aws.
aws
Apps deployed to the platform EKS cluster. Image is built locally, pushed
through the platform registry gateway to ECR, and rolled out as a Deployment
behind Traefik. The default (API) path needs neither AWS_PROFILE nor AWS
keys — wbsp login (or WBSP_API_TOKEN in CI) authorises both the deploy and
the image push (see Credentials).
AWS_PROFILE is required only on the operator --backdoor path.
Basics — deploy, list, status, remove
# Deploy / redeploy (builds + pushes image, rolls out) — `prod` is an aws destination
wbsp deploy --config path/to/wbsp.yaml --destination prod
# List apps (queries the cluster across tenants)
wbsp list --provider aws
wbsp list --provider aws --tenant <tenant>
wbsp list --provider aws --json
wbsp list --provider aws --health
# Status of one app (enclave auto-detected; pass --enclave to disambiguate)
wbsp status <app-name> --tenant <tenant>
wbsp status <app-name> --tenant <tenant> --json
# Remove an app and its resources
wbsp remove <app-name> --tenant <tenant>
wbsp remove <app-name> --tenant <tenant> --forceDebugging — logs
wbsp logs <app-name> --tenant <tenant> --lines 200
wbsp logs <app-name> --tenant <tenant> --followConnect to an app's platform database — wbsp db (preferred)
Apps with database.enabled get a private RDS database. RDS is not publicly
reachable, but wbsp db tunnels to it through a persistent in-cluster relay
(no manual relay/credentials needed). It reads the app's connection from the
deployed pod env, so the app must be deployed first.
# Run migrations (the headline use case) — opens a tunnel, runs the command, tears down
wbsp db run --config wbsp.yaml -- npx prisma migrate deploy
# Interactive psql against the app's database
wbsp db connect --config wbsp.yaml
# Hold a tunnel open for a GUI client (TablePlus/DBeaver/…) — prints localhost:PORT + creds
wbsp db proxy --config wbsp.yaml # auto-picks a free local port; --port to override
# Print the connection string
wbsp db url --config wbsp.yamlWhat db run injects. The wrapped command gets the SAME discrete variables a
deployed pod receives — so a migration or seed script written against them works
identically in both places:
| Variable | Notes |
|---|---|
DATABASE_HOST | 127.0.0.1 on aws (the local end of the tunnel) |
DATABASE_PORT | the local tunnel port on aws, the real port locally |
DATABASE_NAME | |
DATABASE_USER | |
DATABASE_PASSWORD | never printed |
DATABASE_SSL | true only when the connection uses TLS |
DATABASE_URL | extra — a convenience for tools that only take a connection string. The platform does NOT inject this into pods; prefer the discrete variables in application code. |
Over the aws tunnel the server certificate's CN is the RDS host, never
localhost, so CA verification cannot succeed — use require/no-verify. The
injected DATABASE_URL already carries sslmode=require&uselibpqcompat=true for
exactly this. db run and db connect print the resolved target
(destination "aws" → user@127.0.0.1:54321/dbname …) before running, so a
connection failure inside a driver still tells you where it was pointed.
- The relay is provisioned with the platform (
wbsp-platform provision --provider awscreateswbsp-db-proxyin thewbsp-systemnamespace); if it's missing, the commands say so. - A local-machine destination (
compose/dev/standalone) connects straight to the docker-compose database (no relay). - The password is shown only by
db proxy(so you can paste it into a GUI); other commands never print it.
Technical — parallel services environment
Run an app's docker-compose services (postgres, redis, forgejo, …) in a dedicated, isolated namespace beside the app. Full guide: parallel-services-guide.md.
# From the app directory (with a wbsp-services.yaml environment definition)
wbsp services up --config wbsp-services.yaml # additive
wbsp services up --config wbsp-services.yaml --prune # reconcile (remove dropped, keep volumes)
wbsp services down --config wbsp-services.yaml # stop, retain volumes
wbsp services down --config wbsp-services.yaml --purge # delete namespace + volumes (data loss)
# Redeploy the app afterwards so the connection env (<SERVICE>_HOST/_PORT) is injected
wbsp deploy --config wbsp.yaml --destination prodReaching the parallel-services containers directly (kubectl port-forward, Forgejo admin token, repo seeding) requires direct cluster/AWS access and is an operator task, outside the
wbspworkflow.
aws.on-demand
Apps deployed as AWS Lambda functions (scale-to-zero, invoked on demand)
rather than always-on Deployments — an aws destination with mode: on-demand.
Same wbsp verbs as a normal aws destination; routing and ECR are shared,
the compute backend differs. Below, lambda names an aws/mode: on-demand
destination.
Basics — deploy, list, status, remove
wbsp deploy --config path/to/wbsp.yaml --destination lambda
wbsp list --provider aws.on-demand
wbsp list --provider aws.on-demand --json --health
wbsp status <app-name> --tenant <tenant>
wbsp remove <app-name> --tenant <tenant> --forceDebugging — logs
wbsp logs <app-name> --tenant <tenant> --lines 200
wbsp logs <app-name> --tenant <tenant> --followTechnical — database & parallel services
mode: on-demand apps use the same platform RDS — see
connect to an app's database.
They cannot join a parallel-services environment. That environment reaches
its allow-listed apps through Kubernetes network rules matched on the app's own
namespace, and an on-demand app is a Lambda function with no namespace to
match. The parallel services guide states the
same limit; if you need those services, deploy to an ordinary aws
destination.
aws.demo
Throwaway sample-mode instances: a single image with sample data baked in,
run as ephemeral pods in the cluster, each with its own bundled database, on
demo subdomains (WBSP_SAMPLE_BASE_DOMAIN). They are not Lambda functions and
have no platform RDS.
demo is a reserved destination name, like universal below: you declare
demo: {} and the platform supplies its whole identity — no type, mode,
tenant, cluster or enclave (declaring any of them is refused).
Two steps, and it matters that they are separate: prepare builds and pushes
the sample image, deploy --destination demo publishes it — and starts
nothing. An instance begins when somebody launches one, with wbsp demo launch or from the website. Nobody visits a URL because you deployed.
One-time — prepare the sample image
# (If the app has a database) generate seed SQL from your local DB first
wbsp dump-data --config path/to/wbsp.yaml
# Build + push the sample image (sample data baked in)
wbsp prepare --config path/to/wbsp.yaml
wbsp prepare --config path/to/wbsp.yaml --destination demo # the demo destinationBasics — publish, then launch
# Publish the sample image for the demo destination. Starts nothing.
wbsp deploy --config path/to/wbsp.yaml --destination demo
# Start an ephemeral instance of it. This is what produces a live URL.
wbsp demo launch --variant <variant-id>
wbsp demo status --variant <variant-id> # availability + live instances
wbsp demo remove --instance <instance-id> # end one now, without waiting for its TTL
wbsp list --provider aws.demo
wbsp list --provider aws.demo --json --healthInstances expire on their own: wbsp demo reap advances the expired ones from
their time limit, through a grace page, to removal. Run
wbsp demo <command> --help for each command's exact flags.
Debugging — logs
wbsp logs <app-name> --tenant <tenant> --lines 200
wbsp logs <app-name> --tenant <tenant> --followDemo apps carry their data inside the image (no platform RDS), so there is no external database to connect to. Wildcard TLS for the demo subdomains is provisioned and managed by the platform (an operator concern).
universal
Publishes an appliance-runnable image and stops. Nothing is deployed, nothing runs, no database or route is provisioned, and no tenant is involved at any point. This is the destination to use when you want your app to run on a home appliance (wbsp-vm) or any Apple Silicon machine, and it is the whole flow for an appliance-only app that declares no cloud destination at all.
universal is a reserved destination name: you declare the block, and the
platform supplies its entire identity — no type, tenant, cluster or
enclave (declaring any of them is a hard error).
destination:
universal: {}Basics — publish
# Build for linux/amd64 + linux/arm64 and push as one multi-architecture index
wbsp deploy --config path/to/wbsp.yaml --destination universal
# --destination may be omitted when `universal` is the only destination declared
wbsp deploy --config path/to/wbsp.yamlIt reads .env.universal (same one-file-per-destination rule as every other
placement) and prints a machine-readable result:
{
"app": "my-app",
"destination": "universal",
"image_ref": "registry.wbsp.ai/<owner>/<variant>/app:latest",
"platforms": ["linux/amd64", "linux/arm64"],
"status": "published",
"deployed": false
}"deployed": false is the point of the destination, not a footnote.
There are no lifecycle commands
list, status, logs, stop and remove do not apply — nothing was
deployed, so there is nothing to inspect or tear down. To verify a publish,
read the image_ref and platforms above.
Credentials — login only, never --backdoor
Publishing goes through the registry gateway with your wbsp login session and
needs no AWS credentials:
wbsp login # required
export WBSP_API_URL=<your wbsp-api endpoint> # required--backdoor is refused for this destination — the error names the reason.
See credential-less-deploy.md.
Deploying to the cloud as well — order matters
A cloud deploy publishes the cloud architecture only, moving the image tag off any appliance index published earlier. Run the universal publish last:
wbsp deploy --config path/to/wbsp.yaml --destination aws # cloud
wbsp deploy --config path/to/wbsp.yaml --destination universal # restore appliance runnabilityA cloud deploy of an app that declares universal: prints a reminder to that
effect.
Declare release: where the appliance will see it
An appliance cannot read your wbsp.yaml — it reads the run manifest stamped on
the image, built from this destination's effective configuration. An empty
universal: {} inherits your top-level release:; override it under the block
when the appliance needs something different:
destination:
universal:
release: "node scripts/migrate-appliance.js"
env:
TELEMETRY: "off"Troubleshooting
| Message | Meaning |
|---|---|
no destination "universal" is declared | Add universal: {} under destination: — declaring the block is how an app opts into appliance publishing. |
--backdoor is not supported for the "universal" destination | Drop --backdoor; publish with your wbsp login session. |
WBSP_API_URL is required to publish to the "universal" destination | Set WBSP_API_URL to your wbsp-api endpoint. |
declares neither source: nor dockerfile: — there is nothing to build | universal publishes an image; give it something to build. |
Multi-platform build is not supported for the docker driver | The wbsp-multiarch buildx builder could not be provisioned. Check docker buildx version, or set WBSP_BUILD_PLATFORMS=linux/amd64 to publish a cloud-only image (which will not run on an appliance). |
--publish-only is retired / --universal is retired | Both are replaced by --destination universal. |
First publish may create a
wbsp-multiarchbuilder. A stock Docker Desktop cannot build multi-platform images, so the CLI provisions a dedicated buildx builder on first use and reuses it afterwards. Do not delete it between publishes.WBSP_BUILD_PLATFORMS(comma-separated) narrows the published set for a pipeline that has measured the cost.
Rebuilding the binaries
After any Go change, rebuild all three binaries into bin/ with the version
stamped in (--version reads it):
make build # builds wbsp, wbsp-platform, wbsp-api into bin/ with -ldflags version
# Quick gate
go build ./... && go test ./...Installing them onto another machine (clients/operators) is covered in installation.md.
Help for any command
wbsp --help
wbsp <command> --help # e.g. wbsp deploy --help
wbsp services --help
wbsp-platform --help
wbsp-platform <command> --helpRelated guides
- application-creator-guide.md — writing an app's
wbsp.yaml - wbsp-yaml-reference.md — the
wbsp.yamlfield reference - credential-less-deploy.md — deploying with only your platform login, and the
universalappliance publish - parallel-services-guide.md — docker-compose services beside an app on AWS
- path-prefixes.md — routing apps under a path prefix
External reference documentation
The underlying tools the platform builds on — consult these when a command's behaviour or flags go beyond what's covered above.
Traefik (ingress)
Docker & Docker Compose
- Docker documentation
- Docker Compose overview · Compose file reference
docker composeCLI ·docker exec
Forgejo (the worked-example parallel service)
- Forgejo documentation
- Configuration cheat sheet (
app.ini) - Database preparation
- Container image (Codeberg registry)