Skip to content

Credential Vault — Secure Credential Management for AI Agents

Credential Vault — Secure Credential Management for AI Agents

Section titled “Credential Vault — Secure Credential Management for AI Agents”

Every organization using AI agents faces credential sprawl: API keys scattered across repos, agents, and services with no visibility into who’s using what, no rotation, no scoping, and no audit trail.

The BaselineOS Credential Vault solves this at the OS layer.


import { CredentialVault, STANDARD_CREDENTIALS } from 'baselineos';
// Or standalone: import { CredentialVault } from '@baselineos/vault';
const vault = new CredentialVault({
persistPath: '.baseline/vault',
masterKey: process.env.BASELINE_VAULT_KEY!,
credentials: STANDARD_CREDENTIALS, // Auto-resolves ANTHROPIC_API_KEY, etc.
});
await vault.initialize();
// Check what resolved
const health = vault.getHealth();
console.log(`${health.resolvedFromEnv} credentials resolved from environment`);
console.log(`${health.missingFromEnv} credentials missing`);

All credentials are encrypted with AES-256-CBC before storage:

  • Random 16-byte IV per credential
  • Master key derived via SHA-256 from BASELINE_VAULT_KEY or BASELINE_MASTER_KEY
  • Encrypted values stored in SQLite (WAL mode)
  • Raw values exist only in memory during active use

Master key is required. The vault refuses to initialize without one — no insecure defaults.

Borrowed from Anthropic Managed Agents: agents can USE credentials but never READ them.

Traditional: Agent calls vault.retrieve() → gets "sk-ant-..." → uses it → keeps it in memory forever
Opaque: Agent calls requestOpaqueCapabilities() → gets handle → runner injects into child process → agent never sees the value

The agent receives a CapabilityHandle:

{
credentialName: 'vercel-token',
leaseId: 'lease-1713100800000-x7k2m1', // Opaque reference
expiresAt: 1713100860000, // 60 seconds from now
envVar: 'VERCEL_TOKEN', // Where runner will inject
}

The handle does NOT contain the raw credential value. Only the ProtocolRunner can resolve a handle to the actual value, and only at the moment of child process injection.

Borrowed from HashiCorp Vault: credentials are dynamic and short-lived.

Traditional: VERCEL_TOKEN lives forever until manually rotated
Leased: VERCEL_TOKEN lease valid for 60 seconds, auto-revoked after step completion

The lease lifecycle:

1. Agent requests capability for a step
2. Vault issues lease with TTL = step timeout (e.g., 60s)
3. Runner resolves lease → injects into child process env
4. Command executes
5. Runner revokes lease immediately
6. Even if agent is compromised: lease is already dead

Every credential can require a minimum trust score:

vault.store('production-deploy-key', 'secret', {
type: 'api-key',
minTrustScore: 85, // Only highly trusted agents
provider: 'aws',
});
// Agent with trust 60 → denied (logged)
// Agent with trust 90 → granted (logged)

Credentials can be scoped to specific agents:

vault.store('analytics-key', 'secret', {
type: 'api-key',
scope: 'agent',
allowedAccessors: ['analytics-agent', 'reporting-agent'],
});
// deploy-agent → denied
// analytics-agent → granted

Every credential access is logged to SQLite:

const log = vault.getAccessLog({ credentialName: 'vercel-token' });
// [
// { accessedBy: 'deploy-agent', granted: true, reason: 'Lease issued: lease-xxx (TTL: 60s)', timestamp: ... },
// { accessedBy: 'deploy-agent', granted: true, reason: 'Lease revoked: lease-xxx', timestamp: ... },
// { accessedBy: 'untrusted-agent', granted: false, reason: 'Trust score 30 below required 70', timestamp: ... },
// ]

The audit trail persists across restarts (SQLite-backed).


Define credentials as part of your organization’s baseline:

import { defineBaseline } from 'baselineos';
export default defineBaseline({
organization: {
name: 'Acme Corp',
industry: 'fintech',
},
credentials: {
anthropic: {
type: 'api-key',
envVar: 'ANTHROPIC_API_KEY',
provider: 'anthropic',
required: true,
},
vercel: {
type: 'bearer-token',
envVar: 'VERCEL_TOKEN',
provider: 'vercel',
scope: 'workflow',
},
database: {
type: 'connection-string',
envVar: 'DATABASE_URL',
provider: 'neon',
scope: 'agent',
allowedAccessors: ['data-agent', 'migration-agent'],
minTrustScore: 70,
},
sentry: {
type: 'api-key',
envVar: 'SENTRY_DSN',
provider: 'sentry',
required: false,
},
},
});

When the vault initializes with this config, it auto-resolves each credential from the corresponding environment variable.


new CredentialVault({
persistPath: string, // SQLite storage path
masterKey?: string, // Encryption key (or from BASELINE_VAULT_KEY env)
autoResolveEnv?: boolean, // Auto-resolve from env on init (default: true)
credentials?: Record<...>, // Credential definitions to resolve
})
MethodDescription
store(name, value, options)Store a credential (encrypted at rest)
retrieve(name, accessor)Retrieve a credential (access-controlled, audited)
rotate(name, newValue)Re-encrypt with new value
revoke(name)Delete a credential
has(name)Check if credential exists and is valid
list()List all credentials (metadata only, never values)
MethodDescription
issueLease(name, accessor, ttlMs)Issue a time-boxed lease
resolveLease(leaseId)Resolve lease to raw value (runner only)
revokeLease(leaseId)Revoke a lease early
getActiveLeases()List all active leases
MethodDescription
getHealth()Vault health summary
getExpiring(withinMs)Credentials expiring within a window
getAccessLog(filter?)Access audit trail

const health = vault.getHealth();
// {
// total: 5,
// active: 4,
// expired: 1,
// expiringWithin7Days: 1,
// unusedOver30Days: 0,
// missingFromEnv: 1,
// resolvedFromEnv: 3,
// providers: { anthropic: 1, vercel: 1, neon: 1, sentry: 1, openai: 1 },
// }

BaselineOS includes pre-configured credential definitions for common AI providers:

import { STANDARD_CREDENTIALS } from 'baselineos';
// Includes:
// anthropic → ANTHROPIC_API_KEY (required)
// openai → OPENAI_API_KEY (optional)
// tavily → TAVILY_API_KEY (optional)
// baseline-api → BASELINE_API_KEY (optional)
// sentry → SENTRY_DSN (optional)

Use as a starting point:

const vault = new CredentialVault({
credentials: {
...STANDARD_CREDENTIALS,
// Add your own:
vercel: { type: 'bearer-token', envVar: 'VERCEL_TOKEN', provider: 'vercel' },
},
});

When pipeline.produce() calls an LLM, the API key is resolved through the vault:

createAnthropicGenerator({ vault, accessor: { agentId: 'my-agent' } })
resolveApiKey(): vault.retrieve('anthropic', accessor) → audited
Falls back: options.apiKey → process.env.ANTHROPIC_API_KEY
Anthropic SDK initialized with resolved key

Every LLM call goes through the vault’s access control and audit trail.


FeatureBaselineOS VaultHashiCorp Vaultprocess.env1Password CLI
Encryption at restAES-256-CBCAES-256-GCMPlaintext1Password-managed
Per-agent scopingYesRole-basedNoNo
Trust scoringYesNoNoNo
Time-boxed leasesYesYes (dynamic secrets)NoNo
Opaque grantsYesNoNoNo
Persistent audit trailSQLiteEnterprise auditNo1Password audit
Agent-awareYesNoNoNo
Convention-over-configdefineBaseline()HCL/APIN/ACLI
Zero dependenciesbetter-sqlite3 onlyFull serverNone1Password app