Documentation Index
Fetch the complete documentation index at: https://docs.steward.fi/llms.txt
Use this file to discover all available pages before exploring further.
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"
}
}
curl -X POST https://api.steward.fi/secrets/{id}/rotate \
-H "X-Steward-Key: your-key" \
-H "Content-Type: application/json" \
-d '{ "value": "sk-ant-new-key..." }'
Rotation creates a new version. Routes automatically use the latest version. No container redeployment needed.curl -X DELETE https://api.steward.fi/secrets/{id} \
-H "X-Steward-Key: your-key"
Soft delete — the secret is marked as deleted but retained for audit purposes.
Credential Injection
When the proxy forwards a request, it:
- Decrypts the credential (held in memory for microseconds)
- Injects it according to the route config (header, query param, or body)
- Forwards the request to the real API
- Zeroes the credential from memory
Injection methods:
| Method | Use Case | Example |
|---|
header | Most APIs (Bearer token, API key header) | Authorization: Bearer sk-proj-... |
query | APIs that use query param auth | ?apiKey=sk-proj-... |
body | APIs 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