Providers
A Provider is a connection to an external secret store. You configure it once with credentials; Bella proxies all secret reads and writes through it.
Supported Provider Types
| Type | Description |
|---|---|
Vault / OpenBao | HashiCorp Vault or OpenBao (KV secrets engine) |
VaultDatabase | OpenBao / Vault Database secrets engine — ephemeral DB credentials |
AwsSecretsManager | AWS Secrets Manager |
AwsParameterStore | AWS Systems Manager Parameter Store |
AzureKeyVault | Azure Key Vault |
GoogleSecretManager | GCP Secret Manager |
HttpRest | Custom HTTP endpoint — bring your own secret backend |
System Provider
Every tenant automatically gets a System Provider — a Bella-managed OpenBao instance. It requires no configuration. Use it immediately after signing up.
The system provider's ID is well-known (00000000-0000-0000-0000-000000000001) and is pre-assigned to every new project.
Create a Provider
bella providers create \
--name "AWS Production" \
--type AwsSecretsManager \
--region us-east-1 \
--access-key-id AKIAXXXXXXXX \
--secret-access-key secretPOST /api/v1/providers
Content-Type: application/json
{
"name": "AWS Production",
"type": "AwsSecretsManager",
"configuration": {
"region": "us-east-1",
"accessKeyId": "AKIAXXXXXXXX",
"secretAccessKey": "***"
}
}Sensitive configuration values (access keys, tokens, client secrets) are encrypted at rest using ASP.NET Data Protection before storing. They are masked in all API responses as ***ENCRYPTED***.
Provider Scoping
Providers are tenant-scoped — they can be assigned to multiple projects:
Tenant
└── "AWS Production" provider
├── assigned to "Project Alpha"
└── assigned to "Project Beta"Assign a Provider to a Project
Planned — not in the CLI yet
bella providers assign is on the roadmap and is not in the CLI yet. Until then, use the WebApp (Project → Providers → Assign Provider) or the REST API. The example below shows the planned syntax.
bella providers assign --project my-project --provider "AWS Production"Or from the WebApp: Project → Providers → Assign Provider
Assign a Provider to an Environment
Each environment within a project can use a specific provider. --provider takes the provider's slug (see bella providers list):
bella environments add-provider production \
--project my-project \
--provider aws-productionMultiple providers can be assigned to one environment — Bella reads from the primary provider and optionally syncs to others.
Test a Provider Connection
Planned — not in the CLI yet
bella providers test is on the roadmap and is not in the CLI yet. Until then, test the connection from the WebApp (the provider's page) or with the REST API. The example below shows the planned syntax.
bella providers test my-provider-idVerifies that credentials are valid and the API is reachable.
Vault / OpenBao Configuration
{
"type": "Vault",
"configuration": {
"url": "https://vault.example.com",
"mountPath": "secret",
"authMethod": "AppRole",
"roleId": "...",
"secretId": "..."
}
}Supported Vault auth methods: AppRole, Token, Kubernetes, GitHub.
AWS Secrets Manager Configuration
{
"type": "AwsSecretsManager",
"configuration": {
"region": "us-east-1",
"accessKeyId": "AKIAXXXXXXXX",
"secretAccessKey": "***"
}
}IAM role-based auth is also supported — omit accessKeyId/secretAccessKey when running on EC2 or ECS with an instance role.
Azure Key Vault Configuration
{
"type": "AzureKeyVault",
"configuration": {
"vaultUrl": "https://my-vault.vault.azure.net",
"tenantId": "...",
"clientId": "...",
"clientSecret": "***"
}
}Managed Identity is supported — omit credentials when running on Azure resources with a system-assigned identity.
VaultDatabase (Dynamic Credentials) Configuration
The VaultDatabase provider type connects to OpenBao's Database secrets engine to generate short-lived database credentials on demand. Unlike other provider types, it does not store static secrets — it issues ephemeral credentials with an automatic TTL.
{
"type": "VaultDatabase",
"configuration": {
"server_url": "https://vault.example.com",
"auth_method": "approle",
"role_id": "...",
"mount_path": "database",
"default_role": "app-readwrite",
"connection_url_template": "postgres://{username}:{password}@db.example.com:5432/myapp"
}
}| Field | Required | Default | Description |
|---|---|---|---|
server_url | ✅ | — | Vault / OpenBao base URL |
auth_method | ✅ | approle | approle or token |
role_id | AppRole | — | AppRole role ID |
secret_id | AppRole | — | AppRole secret ID (stored encrypted) |
root_token | Token | — | Root / service token (stored encrypted) |
mount_path | — | database | Mount path of the Database secrets engine |
default_role | — | — | Default OpenBao role when none is specified |
connection_url_template | — | — | Template with {username} / {password} placeholders |
When connection_url_template is set, the generate endpoint returns a ready-to-use connectionUrl with credentials already substituted.
Setting up OpenBao for dynamic credentials
Before configuring the Bella provider, enable and configure the Database secrets engine in OpenBao:
# Enable the database engine
vault secrets enable database
# Configure a Postgres connection
vault write database/config/my-postgres \
plugin_name=postgresql-database-plugin \
allowed_roles="app-readwrite" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/myapp" \
username="vault-admin" \
password="admin-password"
# Create a role (TTL 1 hour)
vault write database/roles/app-readwrite \
db_name=my-postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"The Bella provider's AppRole policy must allow read on database/creds/*:
path "database/creds/*" {
capabilities = ["read"]
}
path "database/roles/*" {
capabilities = ["list", "read"]
}See Dynamic Database Credentials for usage.
HttpRest — Custom HTTP Provider
The HttpRest provider type lets you connect any secret backend that you control. Bella sends a single signed POST request to your endpoint for every secret operation. Use it to:
- Keep secrets in your own database or internal API without granting Bella cloud credentials
- Proxy to a legacy secret store that doesn't have a native Bella adapter
- Self-host on-premises with a fully custom secret backend
How It Works
Bella API
└─ POST {your-endpoint}
X-Bella-Signature: t={unix},v1={hmac-sha256-hex}
{ApiKeyHeader}: {ApiKey} // optional
{ "action": "list"|"get"|"set"|"delete", "path": "...", "key": "...", "value": "..." }Request / Response Contract
| Action | Request body | Response body |
|---|---|---|
list | { action, path, requestId } | { "secrets": { "KEY": "value" } } |
get | { action, path, key, requestId } | { "value": "..." } |
set | { action, path, key, value, requestId } | { "success": true } |
delete | { action, path, key, requestId } | { "success": true } |
| error | — | non-2xx + { "error": "message" } |
The path is composed as {namespace_prefix}/{environment_slug} (e.g. myapp/production).
Signature Verification
Every request carries an X-Bella-Signature header using HMAC-SHA256 — the same format used by Bella webhooks and rotation functions:
X-Bella-Signature: t={unix-epoch-seconds},v1={hmac-sha256-hex}Verify it in your function using @bella-baxter/sdk:
import { verifyWebhookSignature } from '@bella-baxter/sdk';
const valid = await verifyWebhookSignature(
process.env.BELLA_SIGNING_SECRET!,
req.headers.get('x-bella-signature') ?? '',
rawBody,
);
if (!valid) return { status: 401 };Configuration
{
"type": "HttpRest",
"configuration": {
"endpoint": "https://your-function.example.com/bella-secrets",
"signing_secret": "openssl-rand-hex-32-output",
"api_key_header": "X-Api-Key",
"api_key": "your-api-key-or-ref:SECRET_NAME",
"namespace_prefix": "myapp/production"
}
}| Field | Required | Description |
|---|---|---|
endpoint | ✅ | HTTPS URL Bella will POST all operations to |
signing_secret | Recommended | HMAC-SHA256 signing secret. Generate with openssl rand -hex 32 |
api_key_header | — | HTTP header name to send the API key in (e.g. X-Api-Key, Authorization) |
api_key | — | API key value, or ref:SECRET_NAME to read from the system OpenBao provider |
namespace_prefix | — | Prepended to all paths (e.g. myapp/production) |
The api_key field supports a secret reference. Use ref:MY_API_KEY to read the value from the system provider at secret/data/_bella-baxter/provider-api-keys/{providerId} — write it there via Vault CLI or Terraform to enable automatic rotation.
Function Templates
Ready-to-use templates are available in the repository:
| Platform | Language | Template |
|---|---|---|
| AWS Lambda | Node.js/TypeScript | aws-lambda/nodejs |
| AWS Lambda | C# | aws-lambda/csharp |
| GCP Cloud Run | Node.js/TypeScript | gcp-cloud-run/nodejs |
| GCP Cloud Run | C# | gcp-cloud-run/csharp |
| Azure Functions | Node.js/TypeScript | azure-functions/nodejs |
| Azure Functions | C# | azure-functions/csharp |
| Docker | Node.js/TypeScript | docker/nodejs |
| Docker | C# | docker/csharp |
The WebApp shows a template snippet preview directly in the provider form — pick a runtime, copy the snippet, download the full template, and implement listSecrets(), getSecret(), setSecret(), and deleteSecret().
Related
- Secret Rotation — rotate secrets via an HttpWebhook function (same signing scheme)
- Rotation Function Templates — templates for rotation functions