Store and fetch credentials securely at runtime via AgentLair Vault REST API. Use when an agent needs to read an API key, store a secret, rotate credentials,...
---
name: agentlair-vault
description: Store and fetch credentials securely at runtime via AgentLair Vault REST API. Use when an agent needs to read an API key, store a secret, rotate credentials, or avoid putting secrets in openclaw.json. Credentials stay in the vault — only the AGENTLAIR_API_KEY lives in your environment. Use instead of environment variables or openclaw.json for third-party API keys, tokens, and secrets.
version: 1.1.0
metadata:
openclaw:
homepage: https://agentlair.dev
emoji: "🔐"
os: ["linux", "macos", "windows"]
primaryEnv: AGENTLAIR_API_KEY
requires:
bins:
- curl
tags:
- credentials
- secrets
- vault
- security
- agent-identity
- infrastructure
---
# AgentLair Vault — Runtime Credential Storage for Agents
Keep credentials out of `~/.openclaw/openclaw.json`. Store them in AgentLair Vault. Fetch them at runtime. Rotate with one API call.
Base URL: `https://agentlair.dev`
Auth: `Authorization: Bearer $AGENTLAIR_API_KEY`
**The agent holds a vault token. Not the credential itself.** Only `AGENTLAIR_API_KEY` lives in the environment — everything else is fetched at runtime through an audited, rate-limited API.
---
## Setup: Get an API Key
Self-service, no email required:
```bash
curl -s -X POST https://agentlair.dev/v1/auth/keys \
-H "Content-Type: application/json" \
-d '{}'
```
Response:
```json
{"api_key": "al_live_...", "account_id": "acc_...", "tier": "free", "created_at": "..."}
```
Store `api_key` as `AGENTLAIR_API_KEY` in your environment. This is the **only** credential that needs to be in your environment or config file.
---
## When to Use This Skill
Use this skill when:
- An agent needs to use an API key (Anthropic, OpenAI, Stripe, Slack, etc.)
- A user asks to store, retrieve, rotate, or revoke a credential
- You want to avoid storing secrets in `openclaw.json` or environment variables
- You're setting up a new agent and need to provision credentials
- A skill needs a third-party token that shouldn't be exposed to the filesystem
**Do not** use `openclaw.json` or environment variables for third-party API keys if you can store them in vault instead.
---
## Store a Secret (Setup / Rotation)
Store any secret under a named key. Use descriptive names (`anthropic-key`, `stripe-live`, `slack-bot-token`).
```bash
curl -s -X PUT "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "sk-ant-YOUR-KEY-HERE", "metadata": {"label": "Anthropic API key", "service": "anthropic"}}'
```
Response (first store, HTTP 201):
```json
{
"key": "anthropic-key",
"stored": true,
"version": 1,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
```
Response (update / rotation, HTTP 200):
```json
{
"key": "anthropic-key",
"stored": true,
"version": 2,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
```
**Key naming rules:** 1–128 characters, alphanumeric + `_`, `-`, `.`
**Optional `metadata` object** (max 4KB): human-readable context. Not the secret — just labels, service names, expiry hints. Never put secret values in metadata.
---
## Fetch a Secret at Runtime
Retrieve a stored secret by name. The `ciphertext` field contains the stored value.
```bash
curl -s "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
```
Response:
```json
{
"key": "anthropic-key",
"ciphertext": "sk-ant-YOUR-KEY-HERE",
"value": "sk-ant-YOUR-KEY-HERE",
"metadata": {"label": "Anthropic API key", "service": "anthropic"},
"version": 1,
"latest_version": 1,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
```
Use the `ciphertext` (or `value` — both return the same thing) field as the credential.
**To retrieve a specific version:**
```bash
curl -s "https://agentlair.dev/v1/vault/anthropic-key?version=1" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
```
---
## List All Secrets
Get metadata for all stored keys (never returns ciphertext/values):
```bash
curl -s "https://agentlair.dev/v1/vault/" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
```
Response:
```json
{
"keys": [
{
"key": "anthropic-key",
"version": 1,
"metadata": {"label": "Anthropic API key"},
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
],
"count": 1,
"limit": 10,
"tier": "free"
}
```
---
## Rotate a Secret
Rotation is a PUT with the new value. Creates a new version. The old version is retained (up to 3 versions on free tier) for rollback.
```bash
curl -s -X PUT "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "sk-ant-NEW-ROTATED-KEY", "metadata": {"label": "Anthropic API key", "rotated_at": "2026-03-27"}}'
```
All agents fetching `GET /v1/vault/anthropic-key` automatically get the new value on their next call — no config changes, no restarts.
---
## Revoke a Secret
Delete a key and all its versions:
```bash
curl -s -X DELETE "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
```
Response:
```json
{"key": "anthropic-key", "deleted": true, "versions_removed": 2}
```
**Delete a specific version only:**
```bash
curl -s -X DELETE "https://agentlair.dev/v1/vault/anthropic-key?version=1" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
```
---
## Free Tier Limits
| Limit | Value |
|-------|-------|
| Keys per account | 10 |
| Versions per key | 3 (oldest pruned automatically) |
| Max value size | 16 KB |
| API requests per day | 100 |
---
## Example Session
**User:** "Store my Stripe API key in the vault and then use it to check my balance"
**Agent actions:**
1. Store the Stripe key in vault:
```bash
curl -s -X PUT "https://agentlair.dev/v1/vault/stripe-live" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "sk_live_USER_PROVIDED_KEY", "metadata": {"label": "Stripe live key", "service": "stripe"}}'
```
2. Fetch the key at runtime:
```bash
STRIPE_KEY=$(curl -s "https://agentlair.dev/v1/vault/stripe-live" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" | grep -o '"ciphertext":"[^"]*"' | cut -d'"' -f4)
```
3. Use it:
```bash
curl -s "https://api.stripe.com/v1/balance" \
-H "Authorization: Bearer $STRIPE_KEY"
```
4. Confirm to user: "Stripe key stored in vault as `stripe-live`. Current balance retrieved."
---
## Why Vault Instead of openclaw.json
OpenClaw's default credential storage (`~/.openclaw/openclaw.json`) puts API keys on disk in plaintext. A malicious ClawHub skill running on your agent can read everything there — plus `~/.aws/`, `~/.ssh/`, and any environment variables in the agent's process.
With AgentLair Vault:
- **Only `AGENTLAIR_API_KEY` is in your environment.** Everything else is fetched at runtime.
- **No credentials on disk.** `grep -r "sk-" ~/.openclaw/` finds nothing.
- **Audit trail.** Every credential fetch is logged. Unexpected access at 3am is visible.
- **Rotation without restarts.** Rotate once in vault — every agent gets the new value immediately.
- **Scoped access.** One AGENTLAIR_API_KEY can't read another account's keys.
The blast radius of a compromised skill drops from "all credentials on the machine" to "one rate-limited API key with an audit log."
---
## Client-Side Encryption (Optional)
For secrets you don't want AgentLair to see in plaintext, encrypt before storing:
```bash
# Encrypt locally before storing
SECRET="sk-ant-YOUR-KEY"
ENCRYPTED=$(echo -n "$SECRET" | openssl enc -aes-256-cbc -base64 -k "$LOCAL_PASSPHRASE")
curl -s -X PUT "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"ciphertext\": \"$ENCRYPTED\", \"metadata\": {\"encrypted\": \"aes-256-cbc\", \"label\": \"Anthropic API key\"}}"
# Decrypt when fetching
CIPHERTEXT=$(curl -s "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" | grep -o '"ciphertext":"[^"]*"' | cut -d'"' -f4)
PLAINTEXT=$(echo "$CIPHERTEXT" | openssl enc -aes-256-cbc -d -base64 -k "$LOCAL_PASSPHRASE")
```
Use this when zero-knowledge storage is required. `$LOCAL_PASSPHRASE` never leaves your environment.
The [agentlair-vault-crypto](https://github.com/piiiico/agentlair-vault-crypto) library provides
TypeScript helpers for client-side encryption/decryption with AES-256 and key derivation.
---
## Trust & Security
- **Open-source crypto layer:** [github.com/piiiico/agentlair-vault-crypto](https://github.com/piiiico/agentlair-vault-crypto) — the encryption helpers used by the vault SDK are fully auditable
- **Security documentation:** [agentlair.dev/security](https://agentlair.dev/security) — encryption at rest, key isolation, access audit model
- **Trust architecture:** See the [AgentLair trust model blog post](https://agentlair.dev/blog) for how vault isolation, API key scoping, and audit logging work together
---
## Notes
- The vault stores values as opaque blobs — AgentLair never interprets the content
- Version history retained up to tier limit (3 versions free, 100 paid) — oldest pruned automatically
- Recovery: register a recovery email via `POST /v1/vault/recovery-email` to access vault contents if you lose your API key
- Built by [AgentLair](https://agentlair.dev) — infrastructure for autonomous agents
don't have the plugin yet? install it then click "run inline in claude" again.
Store and fetch credentials securely at runtime via AgentLair Vault REST API. Use this skill when an agent needs to read an API key, store a secret, rotate credentials, or avoid putting secrets in openclaw.json. Credentials stay encrypted in the vault. Only AGENTLAIR_API_KEY lives in your environment. Use instead of environment variables or openclaw.json for third-party API keys, tokens, and secrets.
This skill lets agents and users store, retrieve, rotate, and revoke credentials at runtime without exposing secrets to disk, config files, or the process environment. Use it when an agent needs access to third-party API keys (Anthropic, OpenAI, Stripe, Slack, etc.), when a user asks to store or rotate a credential, or when setting up a new agent and need to provision secrets without writing them to openclaw.json. The vault holds all credentials encrypted. The agent only ever holds one token: AGENTLAIR_API_KEY. On fetch, the agent gets the plaintext credential from the vault API, uses it, and discards it (credential does not persist in memory or on disk).
https://agentlair.dev/v1/auth/keys (no email required). Store this as an env var or in secure config. This is the only credential that should live in your environment.https://agentlair.devhttps://agentlair.dev over HTTPSinputs: none (self-service, no auth required)
action: POST to the auth endpoint to generate a new API key.
curl -s -X POST https://agentlair.dev/v1/auth/keys \
-H "Content-Type: application/json" \
-d '{}'
outputs: JSON response containing api_key (string, format al_live_...), account_id, tier, and created_at.
{
"api_key": "al_live_...",
"account_id": "acc_...",
"tier": "free",
"created_at": "2026-03-27T..."
}
Store the api_key value as AGENTLAIR_API_KEY in your environment immediately. Do not share this key. If compromised, register a recovery email and revoke the key via the dashboard.
edge cases:
inputs:
AGENTLAIR_API_KEY (from step 1)key_name (string, 1-128 characters, alphanumeric + _, -, .): descriptive name for the credential (e.g., anthropic-key, stripe-live, slack-bot-token)credential_value (string): the actual API key, token, or secret to storemetadata (optional, JSON object, max 4 KB): human-readable labels and context (e.g., {"label": "Anthropic API key", "service": "anthropic"}). Never put secret values in metadata.action: PUT the credential to the vault endpoint.
curl -s -X PUT "https://agentlair.dev/v1/vault/KEYNAME" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "CREDENTIAL_VALUE", "metadata": {...}}'
Replace KEYNAME with your key name (e.g., anthropic-key) and CREDENTIAL_VALUE with the actual secret.
outputs:
key, stored: true, version: 1, created_at, updated_at.version (e.g., version: 2).{
"key": "anthropic-key",
"stored": true,
"version": 1,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
edge cases:
_, -, .), vault returns HTTP 400 (Bad Request).AGENTLAIR_API_KEY is invalid or expired, returns HTTP 401 (Unauthorized).inputs:
AGENTLAIR_API_KEYkey_name (string): name of the stored credential (e.g., anthropic-key)version (optional, integer): specific version to fetch. If omitted, fetches latest.action: GET the credential from the vault.
curl -s "https://agentlair.dev/v1/vault/KEYNAME" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
To fetch a specific version:
curl -s "https://agentlair.dev/v1/vault/KEYNAME?version=1" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
outputs: JSON response containing the credential in the ciphertext or value field (both return the same plaintext value), metadata, version info, and timestamps.
{
"key": "anthropic-key",
"ciphertext": "sk-ant-YOUR-KEY-HERE",
"value": "sk-ant-YOUR-KEY-HERE",
"metadata": {"label": "Anthropic API key", "service": "anthropic"},
"version": 1,
"latest_version": 1,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
Extract the ciphertext (or value) field. Use it as your credential in the next API call.
edge cases:
AGENTLAIR_API_KEY is invalid or revoked, returns HTTP 401.inputs:
AGENTLAIR_API_KEYaction: GET the vault root endpoint.
curl -s "https://agentlair.dev/v1/vault/" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
outputs: JSON response with array of keys (metadata only, no ciphertext or values), count, tier limits.
{
"keys": [
{
"key": "anthropic-key",
"version": 1,
"metadata": {"label": "Anthropic API key"},
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
],
"count": 1,
"limit": 10,
"tier": "free"
}
This endpoint never returns ciphertext or plaintext values, only metadata.
edge cases:
keys array is empty, count: 0.?offset=N&limit=M query params.AGENTLAIR_API_KEY is invalid, returns HTTP 401.inputs:
AGENTLAIR_API_KEYkey_name (string): name of the credential to rotatenew_credential_value (string): the new API key or tokenmetadata (optional): updated metadata (e.g., {"rotated_at": "2026-03-27"})action: PUT the new credential value to the same key endpoint. Vault auto-increments the version.
curl -s -X PUT "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ciphertext": "sk-ant-NEW-ROTATED-KEY", "metadata": {"label": "Anthropic API key", "rotated_at": "2026-03-27"}}'
outputs: JSON response with incremented version and updated timestamps.
{
"key": "anthropic-key",
"stored": true,
"version": 2,
"created_at": "2026-03-27T...",
"updated_at": "2026-03-27T..."
}
All agents or services fetching this key via GET will automatically receive the new value on their next call. No restarts or config changes required.
Old versions are retained (up to 3 on free tier, more on paid). Oldest version auto-prunes when limit is exceeded.
edge cases:
AGENTLAIR_API_KEY invalid, returns HTTP 401.inputs:
AGENTLAIR_API_KEYkey_name (string): name of the credential to deleteversion (optional, integer): specific version to delete. If omitted, deletes all versions.action: DELETE the credential from vault.
# Delete all versions of a key
curl -s -X DELETE "https://agentlair.dev/v1/vault/anthropic-key" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
To delete a specific version only:
curl -s -X DELETE "https://agentlair.dev/v1/vault/anthropic-key?version=1" \
-H "Authorization: Bearer $AGENTLAIR_API_KEY"
outputs: JSON response confirming deletion.
{"key": "anthropic-key", "deleted": true, "versions_removed": 2}
Once deleted, the key and all its versions are unrecoverable. If you need to re-add it, store it again.
edge cases:
AGENTLAIR_API_KEY invalid, returns HTTP 401.if AGENTLAIR_API_KEY is not set in the environment:
do not attempt any vault API calls. agent should raise an error ("AGENTLAIR_API_KEY not found in environment"). user must generate a key via step 1 and set it before proceeding.
if the vault returns HTTP 401 (Unauthorized):
the AGENTLAIR_API_KEY is invalid, expired, or revoked. user must generate a new key. if a recovery email was registered, user can attempt account recovery via dashboard.
if the vault returns HTTP 404 (Not Found) when fetching a credential: the key does not exist or the specific version was pruned. check that the key name is spelled correctly. if intentional deletion occurred, the agent must ask the user to re-store the credential.
if the vault returns HTTP 429 (Too Many Requests): the account has exceeded its daily rate limit (100 requests/day free tier). agent should stop issuing vault requests immediately. suggest to user: wait 60 seconds or upgrade to a paid tier for higher limits. do not retry immediately.
if the vault returns HTTP 413 (Payload Too Large): the credential value or metadata exceeds the tier limit (16 KB value, 4 KB metadata on free tier). agent should reject the store request and ask user to provide a smaller value.
if the vault returns HTTP 507 (Insufficient Storage): the account has hit the key limit for the tier (10 free, more on paid). agent should not attempt to store new keys. user must delete unused keys or upgrade tier.
if network request times out (no response after 10 seconds): retry the request up to 3 times with exponential backoff (wait 5s, then 10s, then 20s between retries). if all 3 retries fail, agent should report to user: "vault unreachable; check network connectivity and try again later."
if the user asks to store a credential but does not provide a value: agent should ask the user to provide the credential before calling the vault.
if the user asks to fetch a credential that does not exist: agent should check the list endpoint first (step 4) to confirm available keys. if the key truly does not exist, inform the user that the key is not stored and ask if they want to store it.
if a credential has multiple versions and the user does not specify which version to use:
fetch the latest version (omit the ?version= query param). display to the user which version is being used.
if the user requests client-side encryption (optional):
encrypt the credential locally before sending it to the vault. store the encrypted blob as the ciphertext. when fetching, decrypt the ciphertext locally using the same passphrase. (see examples in optional section below.)
key, stored, version, created_at, updated_atAGENTLAIR_API_KEYkey, ciphertext (or value), metadata, version, latest_version, created_at, updated_atciphertext is a plaintext string (the actual credential value)keys (array of metadata objects, no secrets), count, limit, tierkeys array has key (name), version, metadata, created_at, updated_at; no plaintext valueskey, stored, version (incremented), created_at, updated_atkey, deleted: true, versions_removed (count of deleted versions)AGENTLAIR_API_KEY invalid or expireduser knows the skill worked when:
keyname" and returns the HTTP 201 or 200 response. user can verify by listing keys (step 4) and seeing the new key in the output.stripe-key from vault; current balance: $1,234.56."