Skip to main content

Secret Vault

The Secret Vault stores encrypted API credentials and injects them into outbound requests through the Proxy Gateway. Agents never see real API keys.

How It Works

Instead of giving agents plaintext API keys:
# ❌ Old way — dangerous
OPENAI_API_KEY=sk-proj-abc123...
ANTHROPIC_API_KEY=sk-ant-def456...
You store credentials in Steward’s Secret Vault and configure routes that map request patterns to credentials:
// 1. Store an encrypted credential
await fetch("https://api.steward.fi/secrets", {
  method: "POST",
  headers: {
    "X-Steward-Key": "your-tenant-key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "openai-prod",
    value: "sk-proj-abc123...",
    description: "Production OpenAI API key",
  }),
});

// 2. Create a route: when an agent calls api.openai.com, inject this key
await fetch("https://api.steward.fi/secrets/routes", {
  method: "POST",
  headers: {
    "X-Steward-Key": "your-tenant-key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    secretId: "secret-uuid",
    hostPattern: "api.openai.com",
    pathPattern: "/*",
    injectAs: "header",
    injectKey: "Authorization",
    injectFormat: "Bearer {value}",
  }),
});
Now when an agent calls OpenAI through the proxy, Steward decrypts the key and injects it automatically.

Encryption

Secrets use the same AES-256-GCM encryption as the Wallet Vault, with a key hierarchy that supports per-tenant isolation:
Master Key (Argon2id from STEWARD_MASTER_PASSWORD)

    ├── Tenant Key A (wrapped by master key)
    │       ├── encrypts "openai-prod"
    │       ├── encrypts "anthropic-main"
    │       └── encrypts "birdeye-key"

    └── Tenant Key B (wrapped by master key)
            └── encrypts "custom-api"
Key benefits:
  • Rotating a tenant key re-wraps it — no re-encryption of all secrets needed
  • Master key rotation is O(tenants), not O(secrets)
  • Future: BYOK (Bring Your Own Key) for enterprise customers

Secret Lifecycle

curl -X POST https://api.steward.fi/secrets \
  -H "X-Steward-Key: your-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "anthropic-prod",
    "value": "sk-ant-def456...",
    "description": "Anthropic production key",
    "expiresAt": "2027-01-01T00:00:00Z"
  }'
Response (value is never returned):
{
  "ok": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "anthropic-prod",
    "description": "Anthropic production key",
    "version": 1,
    "expiresAt": "2027-01-01T00:00:00Z",
    "createdAt": "2026-03-26T12:00:00Z"
  }
}

Credential Injection

When the proxy forwards a request, it:
  1. Decrypts the credential (held in memory for microseconds)
  2. Injects it according to the route config (header, query param, or body)
  3. Forwards the request to the real API
  4. Zeroes the credential from memory
Injection methods:
MethodUse CaseExample
headerMost APIs (Bearer token, API key header)Authorization: Bearer sk-proj-...
queryAPIs that use query param auth?apiKey=sk-proj-...
bodyAPIs that require credentials in the request body{ "api_key": "sk-proj-..." }

Security Properties

  • Secrets never leave the vault in API responses — list/get endpoints return metadata only
  • Decrypted credentials exist in memory for microseconds — zeroed immediately after injection
  • All access is logged — every decryption event creates an audit trail entry
  • Tenant isolation — secrets are scoped to tenants; agents can only trigger decryption of their tenant’s secrets