Secrets & Credentials
How Buildd stores and delivers credentials to workers securely
Secrets & Credentials
Overview
Buildd can manage credentials on behalf of workers so they don't need local API keys. Secrets are encrypted at rest and delivered inline during task claiming — workers receive decrypted values over HTTPS without any intermediate storage.
How It Works
1. User stores a secret in Buildd (dashboard settings)
2. Secret is encrypted with AES-256-GCM and persisted to Postgres
3. Worker calls POST /api/workers/claim
4. Server decrypts matching secrets and includes them in the claim response
5. Runner injects secrets into the subprocess environment
6. Claude Code reads ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN as usualSecrets never touch disk on the runner. They exist only in the subprocess environment for the duration of the task.
Secret Types
| Purpose | Env Var | Description |
|---|---|---|
anthropic_api_key | ANTHROPIC_API_KEY | Claude API key for the worker subprocess |
oauth_token | CLAUDE_CODE_OAUTH_TOKEN | Claude Code OAuth token (seat-based billing) |
mcp_credential | User-defined (via label) | Credentials for MCP server connections |
MCP Credentials
MCP credentials use the label field as the environment variable name. For example, a secret with label DISPATCH_API_KEY is injected as DISPATCH_API_KEY=<value> into the worker's environment, making it available to any MCP server configuration that references that variable.
Encryption
All secret values are encrypted before storage using:
- Algorithm: AES-256-GCM (authenticated encryption)
- Key derivation: scrypt with a random 16-byte salt per secret
- Authentication: GCM auth tag prevents tampering
- Key source:
ENCRYPTION_KEYenvironment variable (minimum 32 characters)
The stored format is base64(salt + iv + authTag + ciphertext). Each secret gets its own random salt and IV, so identical plaintext values produce different ciphertext.
Scoping
Secrets are scoped to a team and optionally to an account:
secrets
id, teamId, accountId, workspaceId,
purpose, label, encryptedValueDuring task claiming, the server looks up secrets for the claiming account's team and includes matching anthropic_api_key, oauth_token, and mcp_credential entries in the response.
Runner Behavior
The runner uses server-managed secrets as a fallback — local credentials always take priority:
if runner has local ANTHROPIC_API_KEY → use it
else if claim response includes serverApiKey → inject itThis means existing runners with their own API keys continue working unchanged. Server-managed secrets only activate when the runner has no local credentials configured.
Data Model
-- Encrypted secrets store
secrets
id uuid PRIMARY KEY
team_id uuid REFERENCES teams(id)
account_id uuid REFERENCES accounts(id) -- nullable
workspace_id uuid REFERENCES workspaces(id) -- nullable
purpose text -- anthropic_api_key | oauth_token | mcp_credential | custom
label text -- env var name (required for mcp_credential)
encrypted_value text -- AES-256-GCM ciphertext
created_at timestamptz
updated_at timestamptz
-- Unique constraint: one secret per account + purpose + label
UNIQUE (account_id, purpose, label)API
The secrets API supports both session cookies and API key (Bearer bld_xxx) authentication.
Store a Secret
POST /api/secrets
Authorization: Bearer bld_xxx
Content-Type: application/json
{
"purpose": "mcp_credential",
"label": "dispatch-api-key",
"value": "dsp_xxx..."
}List Secrets (Metadata Only)
GET /api/secrets
Authorization: Bearer bld_xxxReturns secret records without values — encryptedValue is never exposed via API.
Delete a Secret
DELETE /api/secrets?id=<secret-id>
Authorization: Bearer bld_xxxVia MCP
Agents can manage secrets programmatically through the manage_secrets MCP action:
# List all secrets (metadata only)
buildd action=manage_secrets params={ action: "list" }
# Store an MCP credential
buildd action=manage_secrets params={ action: "set", label: "dispatch-api-key", value: "dsp_xxx..." }
# Delete a secret
buildd action=manage_secrets params={ action: "delete", secretId: "uuid" }This enables agents to self-configure their MCP access — for example, a Chief of Staff role can store credentials for the dispatch and buildd MCP servers without requiring manual setup.