WBSP Application Creator Guide

Audience: Application creators who want to deploy and manage applications on the WBSP platform. You do not need Kubernetes, infrastructure, or cloud provider experience to use this guide.

Not what you're looking for? Setting up or administering the platform itself is a separate, operator-only concern and is out of scope for this guide.

Table of Contents

  1. Architecting for the Platform
  2. Preparing Your Application
  3. Configuration
  4. Developing Outside the Platform
  5. Deploying Your Application
  6. Monitoring Your Application
  7. CLI Reference
  8. REST API Reference
  9. Logs and Debugging
  10. Contacting the Platform Team

Architecting for the Platform

The WBSP platform deploys your application as a Docker container. To ensure a smooth deployment, follow these guidelines:

Design for Statelessness

Your application should not store persistent data on the local filesystem. The platform may restart or replace your container at any time. Use the platform-provided database for persistent storage and environment variables for configuration.

Listen on Port 8080

Your application must listen for HTTP traffic on port 8080 — the platform's container-port convention. The platform's ingress layer routes external traffic to this port automatically, and you do not need to configure TLS — the platform handles that.

Keep the port consistent across your app: listen on 8080, EXPOSE 8080 in your Dockerfile, and declare port: 8080 in your config (the port: field defaults to 8080). Use 8080 in your examples rather than a framework default like 3000, so the same image runs identically on every destination.

Accept Configuration via Environment Variables

The platform injects configuration into your container as environment variables. Your application should read database credentials, feature flags, and runtime settings from the environment rather than config files baked into the image.

When you declare a database dependency, the platform automatically injects these variables:

VariableDescription
DATABASE_HOSTHostname of the provisioned Postgres instance
DATABASE_PORTPort number (typically 5432)
DATABASE_NAMEName of your application's database
DATABASE_USERUsername for database access
DATABASE_PASSWORDPassword for database access
DATABASE_SSLSet to true when the database requires SSL (e.g., AWS RDS)

Your application should handle DATABASE_SSL — when set to true, enable SSL/TLS in your database client. On local, this variable is not set.

You can also define custom environment variables in your configuration file (see Configuration).

Declare Dependencies Explicitly

If your application needs a database, declare it in your configuration file. The platform provisions resources on demand — only what you request is created. If you don't declare a database, none is provisioned.


Preparing Your Application

Installing the CLI

Install the wbsp-app CLI (no source-repo access required):

brew install worlds-biggest-software-project/wbsp/wbsp-app
# or:
curl -fsSL https://get.wbsp.ai/install.sh | bash

You'll also need Docker and Docker Compose v2. Full options (pinning a version, choosing an install dir, upgrading) are in Installing the WBSP CLIs.

Writing a Dockerfile

Your application must be packaged as a Docker image. Here is a minimal example:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]

Key points:

  • Expose port 8080 — the platform routes traffic to this port
  • Keep images small — use Alpine-based images where possible
  • Don't include secrets — credentials are injected via environment variables at runtime

Building Your Image

docker build -t my-app:v1.0.0 .

Making Your Image Available

Local: Images built on the same machine are available automatically.

AWS with source and dockerfile: If your configuration file includes source and dockerfile fields, the platform builds and pushes the image to Amazon ECR automatically during deploy. You don't need to build or push anything manually:

name: my-crm
image: my-crm:latest
source: path/to/source
dockerfile: path/to/Dockerfile

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: crm
access: public
routes:
  prod: { domain: crm.acme.com }   # ignored on the `dev` (local-machine) destination

The platform creates an ECR repository (wbsp-my-crm), builds the image with --platform linux/amd64 (required for EKS nodes), and pushes it. The image field is used as the local build tag — the actual ECR image reference is resolved automatically.

AWS with pre-built images: If you don't include source/dockerfile, provide a fully qualified image reference that the EKS cluster can pull:

# Example: push to ECR manually
aws ecr get-login-password | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com
docker tag my-app:v1.0.0 <account>.dkr.ecr.<region>.amazonaws.com/my-app:v1.0.0
docker push <account>.dkr.ecr.<region>.amazonaws.com/my-app:v1.0.0

Testing Locally Before Deploying

Before deploying to the platform, verify your image runs correctly:

docker run -p 8080:8080 -e APP_ENV=development my-app:v1.0.0

Visit http://localhost:8080 to confirm your application responds.


Configuration

Applications are configured via YAML files. This file tells the platform what to deploy, where to route traffic, and what resources to provision.

Required Fields

FieldTypeDescription
namestringDNS-safe application name ([a-z0-9][a-z0-9-]*)
image or dockerfile+sourcestringDocker image reference (e.g., myregistry/my-app:v1.0.0), or how to build it
destinationmapOne or more named placements, each declaring its own tenant (see below)

The tenant is declared per-destination (under each entry of destination:), never at the document root — a root-level tenant: is rejected.

Build Fields (Optional)

When provided, the platform builds and pushes the image automatically on AWS deploys.

FieldTypeDescription
sourcestringPath to application source directory (relative to repo root)
dockerfilestringPath to Dockerfile (relative to repo root)

Destinations, components, enclaves

A config splits where an app runs from what runs:

  • destinationwhere the app runs (placement only). A destination has a type (compose, dev, standalone, or aws) and, for aws, a mode; an aws destination also names the cluster and enclave it lands in. The local-machine types (compose/dev/standalone) need neither.
  • componentwhat runs. A single-component app declares image/dockerfile/port/access/routes at the top level (this is the one unnamed component); a multi-component app lists each workload under a components: map.

Destinations

Each key under destination: is a placement name you choose (dev, demo, prod, …) — it replaces the old target name and becomes the --destination value.

FieldTypeApplies toDescription
typestringallcompose, dev, standalone, or aws
modestringawsnormal (aws default), demo, on-demand, gvisor, kata
clusterstringawsCluster this placement targets (required for aws)
enclavestringawsApplication-isolation group within the cluster (required for aws)

The aws modes are: type: aws (with mode: normal, the default) for an always-on EKS deployment, type: aws, mode: demo for a throwaway sample-data instance, and type: aws, mode: on-demand for an AWS Lambda deployment.

An enclave is an application-isolation group within a single cluster. NetworkPolicies select peers by tenant + enclave, so by default only apps sharing both can talk to each other; the Kubernetes namespace becomes wbsp-<tenant>-<enclave>-<app>. The local-machine types (compose/dev/standalone) relax these controls and ignore cluster/enclave.

Components, access, and routes

A component declares accesspublic (reachable from outside), enclave (its port is reachable by other apps in the same enclave), or none (internal / sibling-only, the default).

routes in v2 is a map keyed by destination name (not a list), declared on the component. It is required iff access: public, and is ignored on the local-machine types (compose/dev/standalone) — there a public component is published directly on its port.

access: public
routes:
  demo: { domain: hello.wbsp-demo.com }   # key = destination name; ignored on a local-machine destination

Worked example (single component)

name: hello

destination:
  dev:                       # placement: local-machine (run app from your IDE)
    type: dev
    tenant: test
  demo:                      # placement: aws cluster "staging", enclave "app1"
    type: aws
    mode: demo
    tenant: test
    cluster: staging
    enclave: app1

# Single unnamed component declared at the top level:
dockerfile: ./Dockerfile
port: 8080                 # the platform's container-port convention (see "Listen on Port 8080")
access: public
routes:
  demo: { domain: hello.wbsp-demo.com }   # ignored on the `dev` (local-machine) destination

Deploy it with wbsp-app deploy hello --destination demo (the --destination flag is optional when the config declares exactly one destination). See wbsp-yaml-reference.md for a worked config spanning dev, prod-parity, stage, prod, and standalone.

Multi-component apps and inter-app deps

A multi-component app moves each workload under components: { <name>: {…} }, and references peer components/apps with uses: using <enclave>-<app>-<component> addressing (the platform injects the peer's host+port as environment variables).

Incremental today. Multi-component (components:) deploy parses and validates but is not yet fully wired — only the single unnamed component is folded into the deploy path so far. Treat it as forward-looking. Managed data stores use the top-level database.enabled / redis.enabled / s3.enabled blocks (the resources: { database, redis, s3 } shape has been removed).

Route Entry

FieldTypeDefaultDescription
domainstringrequiredFully qualified domain name
path_prefixstring/URL path prefix for sub-routing
strip_prefixbooleantrueStrip the path prefix before forwarding to your app (see Path Prefixes)

Optional Fields

database

FieldTypeDefaultDescription
enabledbooleanfalseProvision a dedicated PostgreSQL database
shared_withstringShare another app's database instead of provisioning a new one

Extensions. On aws (RDS) and on the dev/compose container the managed PostgreSQL ships the vector (pgvector) extension plus the standard contrib bundle (pg_trgm, hstore, uuid-ossp, pg_stat_statements, …). The local containers use the pgvector/pgvector image so they match the RDS extension surface, closing the old "works on aws, fails on dev" gap. You still enable an extension your schema uses with CREATE EXTENSION IF NOT EXISTS <name> in a migration or your release: step — the platform only guarantees it is available. Extensions outside that set (e.g. postgis) are not bundled, and the ephemeral standalone image carries only the base engine (best-effort parity).

Scaling (component fields — declared at the top level for a single-component app)

FieldTypeDefaultDescription
replicasinteger1Fixed instance count (mutually exclusive with autoscale)
autoscalemap{ min, max, targetCPU }

redis

FieldTypeDefaultDescription
enabledbooleanfalseProvision a managed Redis (injects REDIS_*, including REDIS_NAMESPACE and REDIS_TLS)
durablebooleanfalsefalse = cache mode (ElastiCache; treat as ephemeral). true = persistent store (MemoryDB) for data that must survive a restart

s3

FieldTypeDefaultDescription
enabledbooleanfalseProvision a per-app S3 bucket + IRSA role (feature 061). Not supported on the compose provider.

Enable managed stores with the top-level database.enabled, redis.enabled, and s3.enabled blocks. (The old resources: { database, redis, s3 } block has been removed — use the .enabled blocks.)

env

A key-value map of environment variables injected into the container.

Example: Simple Application

name: my-crm
image: myregistry/crm:v2.1.0

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: crm

database:
  enabled: true

access: public
routes:
  prod: { domain: crm.acme.com }   # ignored on the `dev` (local-machine) destination

env:
  APP_ENV: production
  LOG_LEVEL: info

Example: Multi-Route Application

name: portal
image: myregistry/portal:latest

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: portal

access: public
routes:
  # one route entry per destination; mount under a path prefix with `path_prefix`
  prod: { domain: apps.acme.com, path_prefix: /portal }

Example: Application with Automatic Build

name: my-crm
image: my-crm:latest
source: src/crm
dockerfile: src/crm/Dockerfile

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: crm

database:
  enabled: true

access: public
routes:
  prod: { domain: crm.acme.com }

env:
  APP_TITLE: My CRM

On an aws destination, the platform builds the image from source using the specified dockerfile, pushes it to ECR, and deploys it. On a local-machine destination (compose/dev/standalone), the source/dockerfile fields are ignored and the platform uses the image field directly (you must build it yourself with docker build).

Example: Shared Database

Multiple apps can share a single database. Deploy the primary app first, then reference it with shared_with:

# primary app — owns the database
name: app-one
image: app-one:latest

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: shared

database:
  enabled: true

access: public
routes:
  prod: { domain: app-one.acme.com }

env:
  APP_TITLE: App One
# secondary app — shares app-one's database
name: app-two
image: app-two:latest

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: shared

database:
  shared_with: app-one

access: public
routes:
  prod: { domain: app-two.acme.com }

env:
  APP_TITLE: App Two

The secondary app receives the same DATABASE_* environment variables as the primary. The primary app must be deployed first — the platform reads its credentials from state. Both apps connect to the same database, so design your schema accordingly.

Example: Application with Resource Limits

name: api-service
image: myregistry/api:v3.0.0

destination:
  dev:
    type: dev
    tenant: acme-corp
  prod:
    type: aws
    tenant: acme-corp
    cluster: main
    enclave: api

database:
  enabled: true

# Component fields (single unnamed component) live at the top level:
access: public
replicas: 2
routes:
  prod: { domain: api.acme.com }

env:
  APP_ENV: production

Connecting to Another Application's Service (Inter-App Access)

By default applications are network-isolated. To let other applications call your app's service, declare the ports you expose and who may reach each one. Access is controlled entirely by you (the provider) — another application can never grant itself access to your service.

Expose a port (provider)

ports:
  - name: auth          # how consumers refer to it
    port: 8080          # the TCP port your app listens on
    access: tenant      # tenant (default) | any | public
    allow:              # optional cross-tenant allow-list
      - wbsp/ai-native-crm   # a specific app:  tenant/app
      - wbsp/*               # all apps in a tenant: tenant/*
  • A declared port is always reachable by other apps in your own tenant.
  • access: any opens it to any tenant; access: public to the Internet.
  • allow: grants specific cross-tenant apps without opening to everyone.

Consume a port (consumer)

uses:
  - app: test/headless-auth   # provider tenant/app
    port: auth                # provider port name or number
    alias: AUTH               # optional env-var prefix

On deploy the platform injects connection details (no hard-coded endpoints):

HEADLESS_AUTH_AUTH_HOST=test-headless-auth.wbsp-test-headless-auth.svc.cluster.local
HEADLESS_AUTH_AUTH_PORT=8080

(With alias: AUTH the variables are AUTH_HOST / AUTH_PORT.)

Rules to remember

  • You can't self-grant. Adding a uses: entry for a service you aren't authorized for gets you nothing — the provider must expose the port to your tenant (same-tenant), allow-list your app, or set it to any.
  • Same-tenant is open: apps in one tenant can reach each other's declared ports automatically; a uses: entry just adds the injected connection env.
  • The platform controls reachability only. Authenticating the caller (if your service needs it) is your application's responsibility — use your own API keys/tokens over the connection.
  • No secrets go in ports:/uses: or the injected connection details.
  • Enforcement is real on AWS (EKS network policy); the local-machine types (compose/dev/standalone) do not enforce it (open network) but inject the same connection env.

Developing Outside the Platform

During active development you want fast feedback — hot reload, instant code changes, AI-assisted editing — without waiting for a Docker build and deploy cycle on every change. A dev destination lets you run your application from your IDE while the platform runs everything else in containers: your PostgreSQL and Redis.

How It Works

A dev-type placement runs only the data services (PostgreSQL and Redis) in containers; the app itself is run from your IDE with live reload. When you deploy a dev destination, the platform:

  1. Provisions your database and Redis (if declared in config) in containers
  2. Host-publishes their ports, derived from your project number (Postgres 54NNN, Redis 63NNN)
  3. Writes a .env next to your app with discrete DATABASE_* / REDIS_* values pointing at localhost on those derived ports

There is no app container and no Traefik for a dev destination — routes are ignored. You run the app yourself and it connects to the containerised data services.

Starting the Dev Environment

Give the placement type: dev in your config, then deploy it. This brings up the DB/Redis containers and writes the .env:

wbsp-app deploy --config my-app.yaml --destination dev

Then source the generated .env and run your app from your IDE:

source .env

# Next.js
npm run dev

# Go
go run .

# Python / Django
python manage.py runserver

# Any framework — it reads DATABASE_* / REDIS_* from the environment

Most frameworks (Next.js, Django, Rails, Spring Boot) load the .env automatically, so you may not even need source.

Dev Alongside Other Apps

A dev destination applies to a single app. Other apps in your tenant continue running in their own placements. This lets you develop one app while interacting with the rest of the environment:

# Bring up supporting apps in containers (whole stack)
wbsp-app deploy --config api-service.yaml --destination compose
wbsp-app deploy --config worker.yaml --destination compose

# Run just the frontend from your IDE
wbsp-app deploy --config frontend.yaml --destination dev

Stopping and Tearing Down

Stop (preserve the database and Redis data):

wbsp-app stop my-crm --tenant acme-corp

Remove (tear down and delete the data):

wbsp-app remove my-crm --tenant acme-corp --force

To switch to the fully containerised version of the app, deploy a compose (or standalone) destination instead — wbsp-app deploy --config my-app.yaml --destination compose.

Dev Environment Variables

A dev destination exposes the same discrete DATABASE_* / REDIS_* env contract as aws (there is no DATABASE_URL / REDIS_URL). The values point at localhost on host-published, project-derived ports so your IDE-run app can connect:

Variabledev Valueaws Value
DATABASE_HOSTlocalhostRDS hostname
DATABASE_PORTDerived host port (54NNN)5432
DATABASE_NAMESameSame
DATABASE_USERSameSame
DATABASE_PASSWORDSameSame
REDIS_HOSTlocalhostElastiCache hostname
REDIS_PORTDerived host port (63NNN)6379

(Ports are derived from your project number; values above 65535 fold into [49152,65535].) Custom environment variables from your config file's env section are included as well.

Requirements

  • Docker Desktop (macOS or Windows) to run the data-service containers
  • No local platform to provision — the dev type is self-contained (the data-service containers are created by the deploy itself)
  • Run your app from your IDE; it reads the host/port values from the generated .env

Deploying Your Application

Prerequisites

  • The wbsp-app CLI is installed (see Installing the CLI). The CLI is self-contained — there is no local platform daemon to start.
  • Docker is running (for the local-machine destinations compose/dev/standalone, which run your containers on your own machine).
  • You have a Docker image available (see Preparing Your Application).
  • You have a YAML configuration file (see Configuration).
  • For an aws destination only: your platform operator has already provisioned the target cluster (you do not run or manage the platform yourself).

Deploy via CLI

wbsp-app deploy --config my-app.yaml --destination dev

Add --diagram <file> to also write a deployment-diagram prompt for your app — a text file containing an image-generation prompt (paste into Google "Nano Banana") plus a mermaid block you can render in VS Code, GitHub, or mermaid.live. It shows your app, its pods, port, routing, and the Postgres/Redis it uses, and never contains secrets:

wbsp-app deploy --config my-app.yaml --destination dev --diagram my-app.diagram.txt

(Platform operators can render the whole-landscape view with wbsp-platform diagram landscape.txt --provider <t>.)

On success, the CLI outputs a JSON result with your application's status, routes, and database details (if provisioned):

{
  "application_id": "acme-corp-my-crm",
  "name": "my-crm",
  "tenant": "acme-corp",
  "status": "running",
  "routes": ["http://crm.acme.com/"],
  "database": {
    "provisioned": true,
    "host": "postgres",
    "port": 5432,
    "database": "acme_corp_my_crm"
  }
}

The wbsp-app CLI is the deployment interface for application creators. The platform also exposes an HTTP API for programmatic use (see REST API Reference), but it is not a local server you start — it lives at the platform endpoint your operator provides.

Verifying Your Deployment

After deploying, check that your application is running:

wbsp-app status my-crm --tenant acme-corp

Deployment Types

The three local-machine types (compose, dev, standalone) all run on the developer's machine, expose the same discrete DATABASE_* / REDIS_* env contract as aws, use no Traefik (routes are ignored), and host-publish ports derived from your project number. Each is selected by a destination's type: (and, for aws, its mode:) and deployed with --destination <name>.

AspectcomposedevstandaloneAWS (always-on)AWS (on-demand)AWS Sample
Destination type / modetype: composetype: devtype: standalonetype: aws (mode: normal)type: aws, mode: on-demandtype: aws, mode: demo
What runsApp + DB + Redis containersDB + Redis containers; app from your IDEApp + DB + Redis in one containerECR (auto-built)ECR + Lambda Web AdapterECR + Lambda Web Adapter
Image sourceLocal Docker imagesLocal Docker images (data services only)Local Docker imagesECR (auto-built)ECR + Lambda Web AdapterECR + Lambda Web Adapter
DatabasePer-app local PostgresPer-app local PostgresEmbedded in the containerAmazon RDSShared RDSEmbedded (SQLite or Postgres)
ScalingFixed replicasn/a (run from IDE)Single containerFixed replicasScale-to-zeroScale-to-zero
Cold startNoneNoneNoneNone~10s (VPC)~3-8s (no VPC)
CleanupManualManualManual (ephemeral)ManualManualAuto (configurable timeout)
Use caseProduction-parity localInner-loop developmentThrowaway local instanceProductionLow-traffic appsDemos and trials

On-Demand Destination (type: aws, mode: on-demand)

Add an aws destination with mode: on-demand to deploy as an AWS Lambda function using the Lambda Web Adapter — your code runs without modification. The function connects to the same RDS database as always-on mode and scales to zero when idle.

destination:
  lambda:
    type: aws
    mode: on-demand
    tenant: acme-corp
    cluster: main
    enclave: my-app
access: public
routes:
  lambda: { domain: my-app-lambda.example.com }

AWS Sample / Demo Destination (type: aws, mode: demo)

Deploy an ephemeral demo instance with embedded sample data. Each deploy creates a unique instance with an unguessable subdomain:

wbsp-app deploy --config demo-app.yaml --destination demo
# Output: Demo URL: http://a3f8b2c1.my-crm.demo.acme.com/
#         Expires at: 2026-05-28T15:10:00Z (10 minutes)

The instance token (a3f8b2c1) becomes a subdomain, so the app always runs at / with no path-prefix complexity. The base domain is configured by the platform operator at provisioning time.

The demo auto-cleans up after the configured timeout. Your app can detect demo mode via the WBSP_SAMPLE_MODE environment variable.

sample_data Configuration

For a demo destination (type: aws, mode: demo), add a sample_data section:

FieldTypeDefaultDescription
sample_data.pathstringrequiredPath to SQL seed files
sample_data.enginestringlightweightlightweight (SQLite) or postgres
sample_data.timeout_minutesint10Auto-cleanup timeout
sample_data:
  path: examples/sample-data/crm
  engine: lightweight
  timeout_minutes: 10

For local development, add your application's domain to /etc/hosts:

127.0.0.1  crm.acme.com

Monitoring Your Application

Check Application Status

CLI:

wbsp-app status my-crm --tenant acme-corp

Output:

Name:     my-crm
Tenant:   acme-corp
Image:    myregistry/crm:v2.1.0
Status:   running
Routes:   http://crm.acme.com/
Database: acme_corp_my_crm (port 5432)

API: (platform endpoint — see REST API Reference)

curl "$WBSP_API/api/v1/apps/acme-corp/my-crm"

List All Your Applications

CLI:

wbsp-app list --provider compose --tenant acme-corp

API: (platform endpoint — see REST API Reference)

curl "$WBSP_API/api/v1/apps?tenant=acme-corp"

Application Status Values

StatusMeaning
pendingDeployment registered, not yet started
deployingContainer and resources being provisioned
runningApplication is up and serving traffic
devA dev destination is up — data services run in containers; you run the app from your IDE
failedDeployment or startup failed (check logs)
removingApplication is being torn down
removedApplication and all resources cleaned up

CLI Reference

wbsp-app deploy

Deploy an application from a YAML configuration file.

wbsp-app deploy [--config <file>] --destination <name> [flags]
FlagRequiredDescription
--configNoPath to the YAML configuration file (default: wbsp.yaml in the current directory)
--destinationYes¹Destination name as declared under destination:; selects the placement. Its type: is compose, dev, standalone, or aws (with a mode:)
--verboseNoEnable verbose output

¹ --destination is optional when the config declares exactly one destination; with more than one it is required. The selected destination's .env.<destination> beside the yaml supplies external config. The lifecycle commands (stop, status, logs, remove) scope by --enclave instead (auto-detected from deployment state — see those commands below).

Note: every config must declare a top-level destination: block.

Example:

wbsp-app deploy --config wbsp.yaml --destination dev
wbsp-app deploy --config my-app.yaml --destination prod
wbsp-app deploy --config demo-app.yaml --destination demo

wbsp-app stop

Stop a running application while preserving its data (database and Redis). Use this to pause a local-machine deployment — including a dev destination's data services — without deleting anything. Re-run wbsp-app deploy to bring it back.

wbsp-app stop <app-name> --tenant <tenant> [--enclave <name>] [flags]
FlagRequiredDescription
--tenantYesTenant that owns the application
--enclaveNoEnclave to stop (resolved from deployment state; required only to disambiguate same-name deployments)

Example:

wbsp-app stop my-crm --tenant acme-corp

To tear down and delete the data, use wbsp-app remove instead.


wbsp-app list

List all deployed applications, optionally filtered by tenant.

wbsp-app list --provider <provider> [flags]
FlagRequiredDescription
--providerYesProvider/type to list: a local-machine type (compose, dev, standalone) or aws; or an AWS deployment type to scope to (aws.on-demand, aws.demo)
--tenantNoFilter results to a specific tenant
--jsonNoOutput as JSON

Example:

wbsp-app list --provider compose --tenant acme-corp

wbsp-app status

Show the status of a specific application.

wbsp-app status <app-name> --tenant <tenant> [--enclave <name>] [flags]
FlagRequiredDescription
--tenantYesTenant that owns the application
--enclaveNoEnclave to scope to (auto-detected; required only to disambiguate same-name deployments)
--jsonNoOutput as JSON

Example:

wbsp-app status my-crm --tenant acme-corp --json

wbsp-app logs

Retrieve or stream logs from an application.

wbsp-app logs <app-name> --tenant <tenant> [--enclave <name>] [flags]
FlagRequiredDescription
--tenantYesTenant that owns the application
--enclaveNoEnclave to scope to (auto-detected; required only to disambiguate same-name deployments)
--followNoStream logs continuously (like tail -f)
--linesNoNumber of recent lines to retrieve (default: 100)

Example:

wbsp-app logs my-crm --tenant acme-corp --follow

wbsp-app remove

Remove an application and clean up all associated resources (container, routes, database).

wbsp-app remove <app-name> --tenant <tenant> [--enclave <name>] [flags]
FlagRequiredDescription
--tenantYesTenant that owns the application
--enclaveNoEnclave to remove (resolved from deployment state; required only to disambiguate same-name deployments)
--forceNoSkip the confirmation prompt

Example:

wbsp-app remove my-crm --tenant acme-corp --force

On success, outputs a JSON result confirming what was cleaned up:

{
  "name": "my-crm",
  "tenant": "acme-corp",
  "container_removed": true,
  "routes_removed": true,
  "database_dropped": true
}

REST API Reference

For most application creators the wbsp-app CLI is all you need — it is self-contained and does not talk to a local server. The platform also exposes an HTTP API for programmatic/CI use. It lives at the platform API endpoint your operator gives you (there is no localhost API server to start); the examples below use $WBSP_API as that base URL:

export WBSP_API="https://<platform-api-endpoint>"   # ask your platform operator

All endpoints accept and return JSON.

POST /api/v1/apps

Deploy a new application.

Request:

{
  "name": "my-crm",
  "tenant": "acme-corp",
  "image": "myregistry/crm:v2.1.0",
  "routes": [
    { "domain": "crm.acme.com", "path_prefix": "/" }
  ],
  "database": { "enabled": true },
  "resources": { "replicas": 1 },
  "env": { "APP_ENV": "production" }
}

Responses:

StatusMeaning
202 AcceptedDeployment started — response includes application details
400 Bad RequestConfiguration validation failed — response includes error details
409 ConflictRoute conflict — another application already uses this domain/path

Example:

curl -X POST $WBSP_API/api/v1/apps \
  -H "Content-Type: application/json" \
  -d '{"name":"my-crm","tenant":"acme-corp","image":"nginx:alpine","routes":[{"domain":"crm.local"}]}'

GET /api/v1/apps

List all deployed applications, optionally filtered by tenant.

Query parameters:

ParameterRequiredDescription
tenantNoFilter by tenant identifier

Response: 200 OK

{
  "applications": [
    {
      "name": "my-crm",
      "tenant": "acme-corp",
      "status": "running",
      "routes": ["http://crm.acme.com/"]
    }
  ]
}

Example:

curl "$WBSP_API/api/v1/apps?tenant=acme-corp"

GET /api/v1/apps/{tenant}/{name}

Get the full status of a specific application.

Path parameters:

ParameterDescription
tenantTenant identifier
nameApplication name

Responses:

StatusMeaning
200 OKApplication found — response includes full details
404 Not FoundApplication does not exist in the specified tenant

Example:

curl $WBSP_API/api/v1/apps/acme-corp/my-crm

PUT /api/v1/apps/{tenant}/{name}

Update and redeploy an existing application. The platform removes the old deployment and creates a new one with the updated configuration. The name and tenant in the request body must match the URL parameters.

Request: Same format as POST /api/v1/apps.

Responses:

StatusMeaning
202 AcceptedRedeployment started
400 Bad RequestValidation error or name/tenant mismatch
404 Not FoundApplication does not exist

Example:

curl -X PUT $WBSP_API/api/v1/apps/acme-corp/my-crm \
  -H "Content-Type: application/json" \
  -d '{"name":"my-crm","tenant":"acme-corp","image":"myregistry/crm:v3.0.0","routes":[{"domain":"crm.acme.com"}]}'

DELETE /api/v1/apps/{tenant}/{name}

Remove an application and clean up all resources.

Responses:

StatusMeaning
202 AcceptedRemoval started — response confirms what was cleaned up
404 Not FoundApplication does not exist

Example:

curl -X DELETE $WBSP_API/api/v1/apps/acme-corp/my-crm

GET /api/v1/platform/health

Check the health of the platform. This is useful for verifying the platform is ready before deploying.

Responses:

StatusMeaning
200 OKPlatform is healthy
503 Service UnavailablePlatform is degraded — check the components array for details

Response:

{
  "platform_status": "healthy",
  "components": [
    { "component": "database", "status": "healthy", "message": "connected" },
    { "component": "ingress", "status": "healthy", "message": "traefik responding" },
    { "component": "api", "status": "healthy", "message": "self-check passed" }
  ]
}

Example:

curl $WBSP_API/api/v1/platform/health

Logs and Debugging

Viewing Logs

CLI:

# Last 100 lines
wbsp-app logs my-crm --tenant acme-corp

# Last 500 lines
wbsp-app logs my-crm --tenant acme-corp --lines 500

# Stream logs in real time
wbsp-app logs my-crm --tenant acme-corp --follow

Troubleshooting Checklist

If your application is not working as expected, work through these steps in order:

1. Check application status

wbsp-app status my-crm --tenant acme-corp

If the status is failed, the deployment did not complete. Check the deployment output for errors.

2. Check platform health

curl $WBSP_API/api/v1/platform/health

If any platform component is unhealthy, contact your platform operator — the issue is with the platform, not your application.

3. Check application logs

wbsp-app logs my-crm --tenant acme-corp --lines 200

Look for startup errors, crash traces, or connection failures.

4. Verify your configuration

Re-read your YAML config file and check:

  • Is the image name correct and accessible?
  • Is the domain name correct?
  • If you declared a database, is your application using the injected DATABASE_* environment variables?

5. Verify routing

For local deployments, ensure your /etc/hosts file maps the domain to 127.0.0.1. If you're seeing a "route conflict" error, another application is already using that domain and path prefix.

Common Error Patterns

SymptomLikely CauseFix
Status shows failedImage not found or application crashes on startupCheck image name; run docker run locally to debug
409 Conflict on deployAnother app uses the same domain/pathChoose a different domain or path prefix
Application runs but not accessibleMissing /etc/hosts entry (local)Add 127.0.0.1 yourdomain.com to /etc/hosts
Database connection refusedApp not reading injected env varsUse DATABASE_HOST, DATABASE_PORT, etc. from the environment
dev destination: app can't reach the databaseApp not run with the generated .envsource .env (or let your framework load it) before running the app from your IDE — it uses localhost and the derived host ports
dev destination: connection refused on the DB portData-service containers not upRe-run wbsp-app deploy --destination <dev-destination> to bring up Postgres/Redis
Health check shows degradedPlatform component issueContact your platform operator

Contacting the Platform Team

This section is a placeholder. Real contact information will be added in a future update.

If you have exhausted the troubleshooting checklist and still need help:

  • Email: TBD — platform team email will be listed here
  • Chat: TBD — chat channel or Slack workspace will be listed here
  • Issue tracker: TBD — link to issue tracker will be listed here

When reaching out, include:

  1. Your application name and tenant
  2. The output of wbsp-app status <name> --tenant <tenant>
  3. Recent logs (wbsp-app logs <name> --tenant <tenant> --lines 200)
  4. Your YAML configuration file (with any secrets redacted)