Bondery Docs

Secrets management

Store, inject, and rotate Bondery secrets in staging and production.

Secrets management

Bondery secrets are ordinary environment variables with the BONDERY_PRIVATE_* prefix (plus DATABASE_URL for Postgres). Local development uses root .env.local; production should use a secrets manager or your platform's encrypted env store — not committed files or chat messages.

For which variables exist and what they do, see Environment configuration and Authentication (contributors).

Principles

  1. Generate once per environment — staging and production each get their own values. Never copy production secrets into local .env.local.
  2. Inject at runtime — containers and processes read secrets from the environment. Do not bake secrets into Docker images.
  3. Rotate on a schedule or on incident — document the blast radius before rotating. Some secrets invalidate all active sessions.
  4. Audit access — restrict who can read deploy env in Dokploy, AWS, or your vault. Prefer break-glass access over shared passwords.

Where to store secrets

EnvironmentRecommended approach
Local devRoot .env.local (gitignored). Generate with openssl rand -hex 32.
Self-host (Dokploy)Dokploy project Environment tab — mark sensitive keys as secret. Same BONDERY_* names as deploy/bondery/.env.example.
CIGitHub Actions encrypted secrets for deploy keys only. CI test Postgres uses ephemeral credentials in the workflow file, not production values.
Cloud (optional)AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault — sync into container env at deploy time via your orchestrator or an init sidecar.

Bondery does not ship a custom secrets agent. Any store that can set process environment variables before api / webapp start is sufficient.

Dokploy (current self-host path)

  1. Open the Bondery stack project → Environment.
  2. Paste BONDERY_PRIVATE_* values from a password manager or one-time generation.
  3. Redeploy affected services (no image rebuild required for env-only changes).

Keep a password manager record per environment listing secret names, generation date, and last rotation — Dokploy stores values but is not a full audit/rotation system.

External secrets manager (larger installs)

Typical pattern:

Secrets Manager (AWS SM / Vault)
    → deploy hook or sidecar fetches JSON
    → exports BONDERY_PRIVATE_* before docker compose up
    → api / webapp containers inherit env

Store one JSON object per environment, keys matching the manifest canonical names. The api service pre_start init containers need the same OAuth and database secrets as the main API process because release-migrate provisions OAuth clients after prisma migrate deploy.

Secret inventory

SecretUsed byRotation impact
BONDERY_PRIVATE_BETTER_AUTH_SECRETSAPIMedium — versioned rotation can avoid mass logout when adding a new version first; removing the last old version invalidates sessions still encrypted with it
BONDERY_PRIVATE_WEBAPP_SESSION_SECRETWebappHigh — invalidates all webapp session cookies
BONDERY_PRIVATE_WEBAPP_OAUTH_CLIENT_SECRETAPI (hash), webapp (plaintext)Medium — update env on API + webapp, re-run provision-oauth-clients, users may need to re-login
BONDERY_PRIVATE_API_KEY_PEPPERAPIHigh — all existing API keys stop validating; create new keys first
BONDERY_PRIVATE_POSTGRES_PASSWORDDB, APIHigh — requires coordinated DB user password change + env update + restart
BONDERY_PRIVATE_AUTH_GITHUB_CLIENT_SECRETAPILow — update GitHub app + env; no user session impact
BONDERY_PRIVATE_AUTH_LINKEDIN_CLIENT_SECRETAPILow — same as GitHub
BONDERY_PRIVATE_REDIS_URLAPIMedium — may embed password; rotating Redis auth drops in-flight rate-limit state

BONDERY_PUBLIC_* OAuth client ids are not secrets. Treat them like usernames — stable per environment, safe in config repos.

Rotation runbooks

BONDERY_PRIVATE_BETTER_AUTH_SECRETS

Format: version:secret[,version:secret...] — highest version (current signing key) first, older decrypt-only versions after.

Bootstrap:

openssl rand -hex 32   # use as: BONDERY_PRIVATE_BETTER_AUTH_SECRETS=1:<value>

When: annual schedule, suspected compromise, or employee offboarding with deploy access.

Zero-downtime rotation:

  1. Generate a new secret: openssl rand -hex 32.
  2. Prepend it as a new version: BONDERY_PRIVATE_BETTER_AUTH_SECRETS=2:<new>,1:<old>.
  3. Deploy api.
  4. Wait for natural session refresh / re-login (or a maintenance window).
  5. Remove the old version: BONDERY_PRIVATE_BETTER_AUTH_SECRETS=2:<new>.

Hard cutover (logs everyone out): replace the entire value with a single new 1:<secret> and redeploy.

  1. Verify: GET https://<api-domain>/status and a test login on webapp + mobile.
  2. Record rotation date in your password manager.

BONDERY_PRIVATE_WEBAPP_SESSION_SECRET

When: annual schedule or webapp-only compromise suspicion.

  1. Generate new value (openssl rand -hex 32).
  2. Update webapp env only.
  3. Redeploy webapp.
  4. All users must log in again; API sessions and mobile tokens are unaffected.

BONDERY_PRIVATE_WEBAPP_OAUTH_CLIENT_SECRET

When: secret may have leaked from webapp env backup or logs.

  1. Generate new secret (openssl rand -hex 32).
  2. Update on both API and webapp env (BONDERY_PRIVATE_WEBAPP_OAUTH_CLIENT_SECRET).
  3. Redeploy api (or run pnpm run provision-oauth-clients against production) so the API's stored hash updates.
  4. Redeploy api and webapp.
  5. Active webapp sessions may fail token refresh until users re-login.

BONDERY_PRIVATE_API_KEY_PEPPER

When: pepper may have leaked — rare; prefer creating new API keys over rotating pepper.

  1. Before changing pepper: ask integrators to create new API keys in Settings.
  2. Update pepper in API env.
  3. Redeploy API.
  4. Old keys return 401 invalid_api_key. Delete old keys from the admin UI after integrators confirm migration.

BONDERY_PRIVATE_POSTGRES_PASSWORD

When: database credential rotation policy.

  1. ALTER USER postgres PASSWORD '...' inside Postgres (or recreate via compose on fresh installs only).
  2. Update BONDERY_PRIVATE_POSTGRES_PASSWORD in deploy env (compose rebuilds DATABASE_URL for services).
  3. Restart db (if needed), then api, and any job workers.
  4. Verify migrations and a sample authenticated API call.

OAuth client ids (BONDERY_PUBLIC_WEBAPP_OAUTH_CLIENT_ID, BONDERY_PUBLIC_OAUTH_CLIENT_ID)

When: rarely — only if you intentionally want new client registrations (e.g. extension ID change in production).

  1. Generate new ids if needed (secrets manager or openssl rand -hex 16).
  2. Update env across affected apps.
  3. Run provisioning (api redeploy / pre_start, or pnpm run provision-oauth-clients -w api manually).
  4. Old refresh tokens for the previous client id will not work.

First-time production setup

Generate all secrets before the first deploy:

openssl rand -hex 32   # BONDERY_PRIVATE_BETTER_AUTH_SECRETS=1:<value>
openssl rand -hex 32   # BONDERY_PRIVATE_API_KEY_PEPPER
openssl rand -hex 32   # BONDERY_PRIVATE_WEBAPP_SESSION_SECRET
openssl rand -hex 32   # BONDERY_PRIVATE_WEBAPP_OAUTH_CLIENT_SECRET
openssl rand -hex 16   # BONDERY_PUBLIC_WEBAPP_OAUTH_CLIENT_ID
openssl rand -hex 16   # BONDERY_PUBLIC_OAUTH_CLIENT_ID
openssl rand -base64 24 | tr -d '/+=' | head -c 32   # BONDERY_PRIVATE_POSTGRES_PASSWORD

Store them in your secrets manager, map into deploy/bondery/.env, then deploy. The api service pre_start runs release-migrate, which applies schema migrations and provisions OAuth clients automatically.

On this page