Vault Storage
How secrets are organized, classified, versioned, and managed — from simple API keys to complex multi-environment credential stores.
Namespaces
Every secret lives at a path — a forward-slash delimited string that forms the namespace hierarchy. Paths are arbitrary: you define the structure that makes sense for your project.
The most common pattern is environment/service/key-name — this maps naturally to scoped token patterns like secrets:read:production/gemini/*.
# Flat namespace (simple projects)
gemini-key
stripe-webhook
github-token
# Hierarchical namespaces (recommended for production)
production/gemini/GEMINI_API_KEY
production/stripe/STRIPE_SECRET_KEY
production/stripe/WEBHOOK_SECRET
production/postgres/DATABASE_URL
# Multi-environment pattern
staging/gemini/GEMINI_API_KEY
staging/stripe/STRIPE_SECRET_KEY
# Service-scoped namespaces
payments-service/stripe/STRIPE_SECRET_KEY
payments-service/stripe/WEBHOOK_SECRET
auth-service/google/CLIENT_SECRET
auth-service/jwt/SIGNING_KEY
# Agent-scoped namespaces
agents/summarizer/GEMINI_API_KEY
agents/researcher/TAVILY_API_KEY
agents/researcher/GEMINI_API_KEYNamespace tips
Use the environment as the top-level prefix (production/, staging/,development/). This lets you write scoped tokens that are strictly isolated per environment — a staging agent cannot accidentally read production secrets.
Secret types
The secret_typefield classifies a secret's nature. It is used for display, filtering, audit enrichment, and rotation-handler routing — not for access control.
| Type | Value | Examples |
|---|---|---|
| 🔑 | api_key | Gemini, Tavily, Replicate, Pinecone, and provider API keys |
| 🔄 | oauth_token | GitHub OAuth, Google refresh tokens, Slack bot tokens |
| 🗄️ | db_credential | PostgreSQL, MySQL, MongoDB, Redis connection strings |
| 🔐 | ssh_key | Private SSH keys for server access or git operations |
| 📜 | certificate | TLS certificates and other certificate material |
| 📦 | custom | Anything else — JWT signing keys, webhook secrets, etc. |
Access tiers
The access tier controls whether a token request for that secret requires human approval. Assign tiers based on the blast radius if the credential were compromised.
standard✅ No approval requiredLow-risk credentials. Token requests are fulfilled instantly.
Examples: Read-only API keys, public data sources, sandbox/dev keys, logging service tokens, non-sensitive webhook secrets.
sensitive⚠️ Approval required (configurable)Elevated risk. Approval can be required or waived per-token request.
Examples: Production API keys with write access, OAuth tokens, staging database connections, internal service-to-service credentials.
critical🚨 Approval always requiredMaximum protection. Every token request requires explicit approval.
Examples: Production database connection strings, payment processor secret keys, SSH private keys, root/admin credentials, KMS keys.
Default to sensitive, not standard
When in doubt, use sensitive. It's easy to relax a tier later — it's much harder to audit all the unapproved accesses after the fact.
Creating secrets
Python
import os
from agentsecretstore import AgentVault
async def provision_secrets():
async with AgentVault(agent_key=os.environ["ASS_AGENT_KEY"]) as vault:
# API key — sensitive tier, with lifecycle metadata for your scheduler.
gemini = await vault.set_secret(
"production/gemini/GEMINI_API_KEY",
"gemini-api-key-example",
secret_type="api_key",
access_tier="sensitive",
metadata={
"service": "gemini",
"team": "ml-platform",
"rotate_after_days": 90,
},
)
print(f"Stored Gemini key version {gemini.version}")
# Database credential — critical tier, requires approval
await vault.set_secret(
"production/postgres/DATABASE_URL",
"postgresql://user:pass@host:5432/db",
secret_type="db_credential",
access_tier="critical",
metadata={
"service": "postgres",
"env": "production",
"description": "Main production PostgreSQL connection string",
},
)
# OAuth token — sensitive tier, expires naturally
await vault.set_secret(
"production/github/OAUTH_TOKEN",
"ghp_placeholder",
secret_type="oauth_token",
access_tier="sensitive",
metadata={"provider": "github", "scopes": "repo,read:org"},
)TypeScript
import { AgentVault } from '@agentsecretstore/sdk';
async function provisionSecrets() {
const vault = new AgentVault({ agentKey: process.env.ASS_AGENT_KEY! });
const stored = await vault.setSecret(
'production/gemini/GEMINI_API_KEY',
'gemini-api-key-example',
{
secretType: 'api_key',
accessTier: 'sensitive',
metadata: {
service: 'gemini',
team: 'ml-platform',
rotate_after_days: 90,
},
},
);
console.log(`Stored version ${stored.version}`);
}
provisionSecrets().catch((error) => {
console.error(error);
process.exit(1);
});Secret versioning
Every time you store a replacement value, Agent Secret Store creates a new encrypted version and returns the active version number. The public SDKs expose the active version on reads and writes today; historical version browsing and rollback are not public SDK methods.
import os
from agentsecretstore import AgentVault
async def update_secret_version(new_key: str):
async with AgentVault(agent_key=os.environ["ASS_AGENT_KEY"]) as vault:
previous = await vault.get_secret_with_metadata(
"production/gemini/GEMINI_API_KEY"
)
print(f"Current version: {previous.version}")
updated = await vault.set_secret(
"production/gemini/GEMINI_API_KEY",
new_key,
secret_type="api_key",
access_tier=previous.access_tier,
metadata=previous.metadata,
)
print(f"Active version is now {updated.version}")Version metadata is available on reads
Use get_secret_with_metadata when an agent needs the active version, secret type, access tier, or metadata. Use get_secret when it only needs the plaintext value.
Metadata and tags
Every secret can carry arbitrary metadata. Store simple JSON fields such as service, owner, rotation cadence, or runbook links there; the dashboard and API return metadata alongside secret records without exposing plaintext values in list responses.
| Field | Type | Description |
|---|---|---|
| metadata | object | Arbitrary key-value pairs; e.g. { team: "platform", env: "prod" } |
| secret_type | string | One of api_key, oauth_token, db_credential, ssh_key, certificate, custom |
| access_tier | string | standard, sensitive, or critical |
| created_at | timestamp | ISO-8601 creation time (auto-set) |
| updated_at | timestamp | ISO-8601 last update time (auto-set) |
| version | integer | Current active version number |
.env bulk import
Migrating from .env files? The CLI can parse a standard .env file and create vault secrets in bulk, preserving the variable names as path suffixes. SDKs can use the same import endpoint by sending the file content.
CLI
# Method 1: Import directly from .env file
ass env import .env --namespace production
# Preview without writing
ass env import .env --namespace production --dry-runPython SDK
import os
from pathlib import Path
from agentsecretstore import AgentVault
async def import_env_file(env_path: str, namespace: str):
"""Import a .env file into the vault under a namespace."""
content = Path(env_path).read_text()
async with AgentVault(agent_key=os.environ["ASS_AGENT_KEY"]) as vault:
result = await vault.import_env(
content=content,
namespace=namespace,
secret_type="custom",
access_tier="standard",
)
print(f"Imported {result.imported_count} secrets")
for key in result.imported:
print(f" {namespace}/{key}")Imports create versions
Re-running an import for an existing key writes a new version. Scoped agent tokens can import only into namespaces covered by their secrets:write scope.
Lifecycle metadata
The current public write API does not enforce secret expiration timestamps. Store expiry, rotation owner, and runbook links in metadata, then let your scheduler or policy workflow act on those fields.
This keeps lifecycle intent close to the credential without pretending the vault will deny reads after a date until native expiration enforcement is exposed.
import os
from agentsecretstore import AgentVault
async def record_lifecycle_policy():
async with AgentVault(agent_key=os.environ["ASS_AGENT_KEY"]) as vault:
await vault.set_secret(
"production/github/DEPLOY_TOKEN",
"github-deploy-token-placeholder",
secret_type="oauth_token",
access_tier="sensitive",
metadata={
"expires_at": "2026-06-01T00:00:00Z",
"rotation_owner": "platform",
"rotation_runbook": "runbooks/github-deploy-token.md",
},
)Scoped Tokens →
Issue least-privilege tokens that cover specific namespace paths.
Secret Rotation →
Plan manual rotation and scheduler-owned automation.
Audit Trail →
Track every read, write, and token issuance against your secrets.
.env Migration Guide →
Step-by-step walkthrough for migrating from .env files.