Use the OpenBao secret backend
By default SupaCloud reads every secret from environment variables. Setting
SECRET_BACKEND=openbao switches to OpenBao as the authoritative source: the
server hydrates all application-level secrets from the single KV v2 path
supacloud/app before any required-secret validation runs. Environment
variables remain a permitted override on top of vault — they always win over the
vault-provided value.
Prerequisites
Section titled “Prerequisites”- An OpenBao or HashiCorp Vault-compatible server with KV v2 enabled at the
supacloudmount. - The SupaCloud server process can reach the vault address (
VAULT_ADDR). - Either a static token or an AppRole with read access to
supacloud/appand the existing per-subsystem paths (supacloud/db,supacloud/messaging,supacloud/management,supacloud/ai,supacloud/registry).
1. Configure vault authentication
Section titled “1. Configure vault authentication”-
Static token — set a single env var:
Terminal window VAULT_ADDR=https://vault.example.comVAULT_TOKEN=<your-token> -
AppRole — set the role ID and secret ID instead:
Terminal window VAULT_ADDR=https://vault.example.comOPENBAO_ROLE_ID=<role-id>OPENBAO_SECRET_ID=<secret-id>The server logs in at boot, then a background loop renews the token at roughly half its TTL and re-logs-in automatically if renewal fails (Issue #385). The AppRole’s policy should permit
auth/token/renew-self; if not, the re-login fallback covers it.
2. Write secrets to supacloud/app
Section titled “2. Write secrets to supacloud/app”All application-level secrets live in a single KV v2 map keyed by their env-var name. Write it once; update individual keys when rotating a secret:
vault kv put supacloud/app \ SUPACLOUD_CREDENTIAL_ENCRYPTION_KEY="base64:<32-byte key>" \ SUPACLOUD_JWT_SECRET="<min-32-chars>" \ OIDC_CLIENT_SECRET="<value>" \ SUPACLOUD_MCP_CONFIRMATION_SECRET="<min-32-chars>" \ DISCORD_BOT_TOKEN="<value>" \ STRIPE_SECRET_KEY="<value>" \ STRIPE_WEBHOOK_SECRET="<value>" \ SUPACLOUD_LINEAR_OAUTH_CLIENT_ID="<value>" \ SUPACLOUD_LINEAR_OAUTH_CLIENT_SECRET="<value>" \ SUPACLOUD_NOTION_OAUTH_CLIENT_ID="<value>" \ SUPACLOUD_NOTION_OAUTH_CLIENT_SECRET="<value>"Only include the keys your deployment uses; unknown keys are ignored.
The credential-encryption key is loaded into an in-process holder (secret_store)
and never written to std::env, so it cannot be inherited by agent containers
or the code-runner subprocess.
3. Set SECRET_BACKEND=openbao
Section titled “3. Set SECRET_BACKEND=openbao”Add to your server environment (or .env):
SECRET_BACKEND=openbaoThe server will now:
- Hydrate
supacloud/appfrom vault, filling anyAppConfigfield still empty after the env pass. - Seed the in-process encryption-key holder and the generic secret map for lazy call sites (Stripe, SMTP, FinTS, edition license).
- Run required-secret validation —
SUPACLOUD_JWT_SECRETand the credential-encryption key are re-checked after hydration (inAppConfig::validate_required), so a vault-sourced value satisfies them.SUPACLOUD_INITIAL_ADMIN_EMAILSis a non-secret prod guard validated infrom_envbefore vault is read and cannot be supplied via thesupacloud/appmap. - Fatal at boot if a required secret is absent from both vault and env.
Verifying the setup
Section titled “Verifying the setup”Check the server startup log for the Loaded … from OpenBao entries (and
Cached vault secret: … per path), for example:
Loaded app secrets from OpenBaoCached vault secret: supacloud/appCached vault secret: supacloud/dbIf a required secret is missing from both sources the process exits with a message naming the variable, e.g.:
SUPACLOUD_CREDENTIAL_ENCRYPTION_KEY must be set in prod so databasecredential material is encrypted at restReverting to env-only
Section titled “Reverting to env-only”Set SECRET_BACKEND=env (or remove VAULT_TOKEN/OPENBAO_ROLE_ID so
auto-detection falls back to env). The server will not contact OpenBao
and will read every secret directly from environment variables.