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:
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| Flag | Default | Description |
|---|---|---|
--watch | off | Enable polling |
--poll-interval <seconds> | 30 | How often to check |
--signal restart|sighup | restart | restart = 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.
# 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.pidbella agent --init # scaffold config
bella agent # start sidecar3. SDK Refresh Interval
With any Bella SDK, configure a refresh interval and the SDK periodically re-fetches secrets from the API:
const client = await createBaxterClient({
baxterUrl: process.env.BELLA_BAXTER_URL!,
apiKey: process.env.BELLA_BAXTER_API_KEY!,
refreshInterval: 60, // seconds
})client = BaxterClient(
baxter_url=os.environ["BELLA_BAXTER_URL"],
api_key=os.environ["BELLA_BAXTER_API_KEY"],
refresh_interval=60,
)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
| Approach | Best for | Process restart? |
|---|---|---|
bella run --watch | Development, scripts | Yes (by default) |
| Agent sidecar | Production, long-running services | No (SIGHUP or file update) |
| SDK refresh interval | Microservices, containerised apps | No (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
- A
VaultDatabaseprovider configured in Bella (see VaultDatabase configuration) - The provider assigned to your environment (Environment → Providers tab)
- 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(ifconnection_url_templateis configured) — with copy button and masked revealusernameandpassword— individual copy buttons- Live TTL countdown (green → amber → red as expiry approaches)
- Lease ID for manual revocation if needed
Generate credentials via API
POST /api/v1/projects/{projectRef}/environments/{envSlug}/providers/{providerSlug}/credentials/generate
Authorization: Bearer <token>
Content-Type: application/json
{
"role": "app-readwrite",
"ttlSeconds": 3600
}{
"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
GET /api/v1/projects/{projectRef}/environments/{envSlug}/providers/{providerSlug}/credentials/rolesReturns 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
| Property | Value |
|---|---|
| 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 |