Skip to content

Dynamic Secrets ​

Dynamic secrets are secrets that are fetched at runtime by your application (not written to a .env file at startup). They update automatically when they change in the provider.

The Three Approaches ​

1. bella run --watch (CLI) ​

The simplest approach — Bella CLI polls for changes and restarts your process when secrets change:

sh
bella run --watch -- node server.js
bella run --watch --poll-interval 10 -- npm start
bella run --watch --signal sighup -- gunicorn app:app   # reload without restart
FlagDefaultDescription
--watchoffEnable polling
--poll-interval <seconds>30How often to check
--signal restart|sighuprestartrestart = kill+respawn; sighup = send SIGHUP

2. Agent Sidecar (bella agent) ​

A long-running daemon that watches secrets and writes local files. Use in production where you don't want the CLI to own the process lifecycle.

yaml
# bella-agent.yaml
watches:
  - project: my-project
    environment: production
    poll-interval: 30

sinks:
  - type: dotenv
    path: ./.env
  - type: json
    path: ./secrets.json

process:
  signal: sighup
  pid-file: ./app.pid
sh
bella agent --init    # scaffold config
bella agent           # start sidecar

3. SDK Refresh Interval ​

With any Bella SDK, configure a refresh interval and the SDK periodically re-fetches secrets from the API:

typescript
const client = await createBaxterClient({
  baxterUrl: process.env.BELLA_BAXTER_URL!,
  apiKey: process.env.BELLA_BAXTER_API_KEY!,
  refreshInterval: 60,  // seconds
})
python
client = BaxterClient(
    baxter_url=os.environ["BELLA_BAXTER_URL"],
    api_key=os.environ["BELLA_BAXTER_API_KEY"],
    refresh_interval=60,
)
go
client := bellabaxter.NewClient(bellabaxter.ClientOptions{
    BaxterURL:       os.Getenv("BELLA_BAXTER_URL"),
    APIKey:          os.Getenv("BELLA_BAXTER_API_KEY"),
    RefreshInterval: 60 * time.Second,
})

Choosing an Approach ​

ApproachBest forProcess restart?
bella run --watchDevelopment, scriptsYes (by default)
Agent sidecarProduction, long-running servicesNo (SIGHUP or file update)
SDK refresh intervalMicroservices, containerised appsNo (in-memory refresh)

Hash-Based Efficient Polling ​

Bella uses a lightweight GET /secrets/hash endpoint that returns an opaque change token for the current secret set — not a digest of the values: compare it for equality with the last one you saw, nothing more. The full GET /secrets is only called when the token changes — minimising API calls and bandwidth. The token changes once when Bella is upgraded to the keyed form, so every watcher refetches once.


Database Credentials ​

VaultDatabase provider — generate ephemeral database credentials via OpenBao's Database secrets engine.

VaultDatabase providers work differently from other provider types. Instead of storing static secrets, they generate short-lived database usernames and passwords on demand. Credentials expire automatically at the end of the lease — no manual revocation required.

Prerequisites ​

  1. A VaultDatabase provider configured in Bella (see VaultDatabase configuration)
  2. The provider assigned to your environment (Environment → Providers tab)
  3. OpenBao Database secrets engine enabled with at least one role

Generate credentials via WebApp ​

Navigate to Environment → Dynamic Credentials. Bella shows all VaultDatabase providers assigned to the environment. Select a role, optionally set a TTL, and click Generate Credentials.

The UI displays:

  • DATABASE_URL (if connection_url_template is configured) — with copy button and masked reveal
  • username and password — individual copy buttons
  • Live TTL countdown (green → amber → red as expiry approaches)
  • Lease ID for manual revocation if needed

Generate credentials via API ​

http
POST /api/v1/projects/{projectRef}/environments/{envSlug}/providers/{providerSlug}/credentials/generate
Authorization: Bearer <token>
Content-Type: application/json

{
  "role": "app-readwrite",
  "ttlSeconds": 3600
}
json
{
  "username": "v-approle-app-readwri-XXXXXX",
  "password": "A1B2-C3D4-E5F6",
  "expiresAt": "2026-03-17T17:00:00Z",
  "leaseDurationSeconds": 3600,
  "leaseId": "database/creds/app-readwrite/XXXX",
  "connectionUrl": "postgres://v-approle-app-readwri-XXXXXX:A1B2-C3D4-E5F6@db:5432/myapp"
}

Leave role null to use the provider's default_role.

List available roles ​

http
GET /api/v1/projects/{projectRef}/environments/{envSlug}/providers/{providerSlug}/credentials/roles

Returns the list of role names configured in OpenBao's database engine mount.

Using the credentials in your app ​

Dynamic credentials are generated through the API (the credentials/generate endpoint above). Your SDK or your application calls it and uses the returned username and password until the lease expires.

bella sdk run does not generate dynamic credentials. It starts your process with the connection details the SDK needs to authenticate, and makes no secrets API calls itself. See the CLI reference.

Security characteristics ​

PropertyValue
Credentials stored by Bella❌ Never
Credentials stored in .env❌ Only if you write them there
Rotation on each call✅ Always — every call is a new user
Revocation on expiry✅ Automatic (by OpenBao)
Audit trail✅ Access log entry per generation
Security scan✅ Skipped — ephemeral creds are not flagged

Released under the ELv2 License.