@agentsecretstore/cli
v1.0.0
Published
CLI for Agent Secret Store — the secure vault built for AI agents
Maintainers
Readme
ass — Agent Secret Store CLI
The secure vault built for AI agents — controlled from your terminal.
Stop putting secrets in .env files. Stop copy-pasting API keys into CI/CD dashboards.
Store every credential in an envelope-encrypted vault, retrieve it with one command, and get a complete audit trail for free.
# 5 lines to your first secret
brew install agent-secret-store/tap/ass
ass login
ass secrets set production/OPENAI_API_KEY sk-proj-...
export OPENAI_API_KEY=$(ass secrets get production/OPENAI_API_KEY)
ass audit --namespace productionContents
- Install
- Quick Start
- Configuration
- Command Reference
- CI/CD Integration
- Piping & Scripting
- Security Model
- Contributing
Install
Homebrew (macOS / Linux) — recommended
brew install agent-secret-store/tap/assnpm (all platforms)
npm install -g @agentsecretstore/cliVerify the installation:
ass --version
# ass/1.0.0 darwin-arm64 node-v22.x.xnpx — run without installing
npx @agentsecretstore/cli secrets get production/MY_KEYDirect binary download
Download the pre-compiled binary for your platform from the latest GitHub release:
| Platform | Binary |
|----------|--------|
| macOS arm64 | ass-macos-arm64 |
| macOS x64 | ass-macos-x64 |
| Linux x64 | ass-linux-x64 |
| Windows x64 | ass-win-x64.exe |
# macOS arm64 example
curl -Lo ass \
https://github.com/Agent-Secret-Store/agent-secret-store/releases/latest/download/ass-macos-arm64
chmod +x ass
sudo mv ass /usr/local/bin/
ass --versionVerify with SHA256 checksums published in each release:
curl -sL https://github.com/Agent-Secret-Store/agent-secret-store/releases/latest/download/checksums.txt \
| sha256sum --check --ignore-missingQuick Start
1. Get your API key at agentsecretstore.com/signup.
2. Authenticate:
ass login
# Paste your API key when prompted — it is stored securely in ~/.assrc3. Store a secret:
ass secrets set production/OPENAI_API_KEY sk-proj-abc1234. Retrieve it:
ass secrets get production/OPENAI_API_KEY5. Use it in a script:
export OPENAI_API_KEY=$(ass secrets get production/OPENAI_API_KEY)That's it. The secret never lives in .env, never appears in git log, and every access is recorded in the audit trail.
Configuration
ass reads configuration from three sources, in priority order:
| Priority | Source | Description |
|----------|--------|-------------|
| 1 | CLI flags | --api-key, --api-url |
| 2 | Environment variables | ASS_AGENT_KEY, ASS_API_URL |
| 3 | ~/.assrc | Written by ass login |
Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| ASS_AGENT_KEY | — | Your agent key (ask_agent_... or ask_live_...) |
| ASS_API_URL | https://api.agentsecretstore.com | Override API endpoint |
| ASS_NO_COLOR | — | Disable colour output (1 to enable) |
~/.assrc
Written automatically by ass login. Plain key=value format:
ASS_AGENT_KEY=ask_live_...
ASS_API_URL=https://api.agentsecretstore.comTip: In team environments or CI, prefer
ASS_AGENT_KEYas an environment variable rather than committing an.assrcfile.
Global Flags
These flags work with every command:
--api-key <key> Override ASS_AGENT_KEY
--api-url <url> Override ASS_API_URL
--json Machine-readable JSON output
--no-color Disable colour output
--quiet Suppress non-essential output (good for scripts)
--verbose Extra debugging output
--version Print version and exit
--help, -h Print help for the current commandCommand Reference
auth
ass login
Authenticate interactively. Prompts for your API key and writes it to ~/.assrc.
ass login
# ┌ Agent Secret Store
# │
# ◆ API Key (will not be stored in shell history):
# │ ▏ass logout
Remove saved credentials from ~/.assrc.
ass logout
# ✓ Logged out. Credentials removed from ~/.assrcass whoami
Print the currently authenticated identity and tenant.
ass whoami
# ┌ Authenticated
# │ Agent: deploy-bot
# │ Tenant: acme-corp
# │ Plan: Pro
# └ass status
Check connectivity to the vault and print service health.
ass status
# ┌ Agent Secret Store
# │ API: https://api.agentsecretstore.com ✓ reachable
# │ Auth: ✓ valid
# │ Plan: Pro (expires 2027-03-01)
# │ Secrets: 42 across 3 namespaces
# └secrets
The core command group. All secret operations are logged to the audit trail.
ass secrets get <namespace/key>
Retrieve the value of a secret.
# Print the value
ass secrets get production/OPENAI_API_KEY
# Capture in a variable (value never appears in shell history)
KEY=$(ass secrets get production/OPENAI_API_KEY)
# JSON output with metadata
ass secrets get production/OPENAI_API_KEY --json
# {
# "key": "OPENAI_API_KEY",
# "namespace": "production",
# "value": "sk-proj-...",
# "secret_type": "api_key",
# "access_tier": "standard",
# "retrieved_at": "2026-03-14T13:00:00Z"
# }Tip: Always capture secrets into variables, never let them appear as command arguments where they'd show up in
ps aux.
ass secrets set <namespace/key> [value]
Store or update a secret. If [value] is omitted, reads from stdin — this keeps the secret out of your shell history entirely.
# Inline value (appears in shell history — avoid for sensitive secrets)
ass secrets set staging/DEBUG_KEY abc123
# From stdin — RECOMMENDED for sensitive values
echo "sk-proj-..." | ass secrets set production/OPENAI_API_KEY
# or
read -rs SECRET && echo "$SECRET" | ass secrets set production/OPENAI_API_KEY
# From a file
ass secrets set production/TLS_CERT < cert.pem
# With explicit type and access tier
ass secrets set production/DB_PASSWORD \
--type db_credential \
--tier sensitive
# → Sensitive-tier secrets require human approval before agents can read them
# Critical tier (requires approval for both read AND write)
ass secrets set production/ROOT_CA_PRIVATE_KEY \
--type ssh_key \
--tier critical--type options:
| Value | When to use |
|-------|-------------|
| api_key | Third-party API keys (OpenAI, Stripe, Twilio, etc.) |
| oauth_token | OAuth access/refresh tokens |
| db_credential | Database connection strings and passwords |
| ssh_key | SSH private keys and certificates |
| certificate | TLS certificates and PEM bundles |
| custom | Everything else |
--tier options:
| Value | Behaviour |
|-------|-----------|
| standard | Agent can read immediately — no approval needed |
| sensitive | Human approval required for agent reads |
| critical | Human approval required for reads AND writes |
ass secrets list [namespace]
List all secrets in a namespace. Values are never shown.
# List all secrets in the production namespace
ass secrets list production
# ┌─────────────────────────────────────────────────────────┐
# │ Namespace: production 3 secrets │
# ├──────────────────┬──────────────┬──────────┬────────────┤
# │ Key │ Type │ Tier │ Updated │
# ├──────────────────┼──────────────┼──────────┼────────────┤
# │ DATABASE_URL │ db_credential│ sensitive│ 2026-03-10 │
# │ OPENAI_API_KEY │ api_key │ standard │ 2026-03-12 │
# │ STRIPE_KEY │ api_key │ sensitive│ 2026-03-14 │
# └──────────────────┴──────────────┴──────────┴────────────┘
# Nested namespace
ass secrets list staging/openai
# JSON for scripting
ass secrets list production --json | jq '[.secrets[].key]'ass secrets delete <namespace/key>
Permanently delete a secret. Irreversible.
ass secrets delete staging/OLD_API_KEY
# ◇ Delete staging/OLD_API_KEY? This cannot be undone.
# yes / no
# Skip confirmation in scripts
ass secrets delete staging/OLD_API_KEY --yesass secrets rotate <namespace/key>
Trigger rotation for a secret. For supported types (API keys with rotation APIs configured), the vault generates a new value. For others, the secret is flagged rotation-pending and you are notified.
ass secrets rotate production/STRIPE_KEY
# ✓ Rotation triggered for production/STRIPE_KEY
# The new value will be available within ~30 seconds.
# Downstream notification webhooks have been fired.env
Bulk import and export secrets as .env files.
ass env import <file> --namespace <ns>
Import all KEY=VALUE pairs from a .env file into a vault namespace.
# Import entire .env.production file
ass env import .env.production --namespace production
# Importing 3 secrets into production:
# ✓ OPENAI_API_KEY
# ✓ DATABASE_URL
# ✓ STRIPE_KEY
# Done. You can now delete .env.production safely.
# Dry run — preview without writing
ass env import .env.production --namespace production --dry-run
# Don't overwrite existing secrets
ass env import .env.production --namespace production --no-overwrite
# Set access tier for all imported secrets
ass env import .env.production --namespace production --tier sensitiveass env export <namespace> [--out <file>]
Export all secrets in a namespace as a .env file.
# Print to stdout
ass env export production
# Write to a file
ass env export production --out .env.production
# Export only specific keys
ass env export production --keys OPENAI_API_KEY,DATABASE_URL
# Export for use in Docker
ass env export production --format docker-env > docker.env
docker run --env-file docker.env my-imageagents
Manage agent identities on your tenant.
# List all agents
ass agents list
# Create a new agent
ass agents create --name "ci-deploy" --description "GitHub Actions deployment agent"
# ✓ Agent created
# ID: ci-deploy
# Key: ask_agent_... ← SAVE THIS — shown once
# Revoke an agent (all its tokens are immediately invalidated)
ass agents revoke ci-deployapprovals
Review and act on agent secret-access requests.
# List pending requests
ass approvals list
# ┌────────────────────────────────────────────────────────────────────────┐
# │ Pending Approvals 2 total │
# ├────────────────┬──────────────┬───────────────────────────────────────┤
# │ ID │ Agent │ Request │
# ├────────────────┼──────────────┼───────────────────────────────────────┤
# │ apr_abc123 │ deploy-bot │ READ production/ROOT_CA_KEY │
# │ apr_def456 │ scraper-bot │ READ production/DATABASE_URL │
# └────────────────┴──────────────┴───────────────────────────────────────┘
# Approve with reason (reason is logged to audit trail)
ass approvals approve apr_abc123 --reason "Scheduled cert renewal — pipeline verified"
# Deny with reason
ass approvals deny apr_def456 --reason "Unexpected access — investigate scraper-bot"
# Approve all pending (use with caution)
ass approvals approve --all --reason "Batch approval for deployment"tokens
Manage scoped access tokens.
# List active tokens for your agent
ass tokens list
# Create a token with specific scopes
ass tokens create \
--scopes "secrets:read:production/*" \
--ttl 15m
# ✓ Token issued (expires in 15 minutes)
# Bearer eyJhbGc...
# Create a write-scoped token
ass tokens create \
--scopes "secrets:write:staging/DEPLOY_KEY" \
--ttl 5m
# Revoke a token immediately
ass tokens revoke tok_abc123audit
Query the immutable audit trail. Every secret read, write, rotation, and approval action is permanently logged.
# Last 50 events (default)
ass audit
# ┌──────────────────────────────────────────────────────────────────────────┐
# │ Audit Trail production · last 50 events │
# ├─────────────────────┬─────────────┬────────────────────┬────────────────┤
# │ Timestamp │ Agent │ Action │ Resource │
# ├─────────────────────┼─────────────┼────────────────────┼────────────────┤
# │ 2026-03-14 13:00:02 │ deploy-bot │ secret.read │ OPENAI_API_KEY │
# │ 2026-03-14 12:45:11 │ scraper-bot │ secret.read.denied │ DATABASE_URL │
# │ 2026-03-14 12:30:00 │ will │ secret.write │ STRIPE_KEY │
# └─────────────────────┴─────────────┴────────────────────┴────────────────┘
# Filter by namespace
ass audit --namespace production
# Filter by agent
ass audit --agent deploy-bot
# Time range
ass audit --since 2026-03-01 --until 2026-03-14
# Filter by action type
ass audit --action secret.read
# JSON for log aggregation / SIEM
ass audit --namespace production --json \
| jq '.events[] | select(.action == "secret.read.denied")'CI/CD Integration
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
jobs:
deploy:
runs-on: ubuntu-latest
env:
ASS_AGENT_KEY: ${{ secrets.ASS_AGENT_KEY }}
steps:
- uses: actions/checkout@v4
- name: Install ass CLI
run: npm install -g @agentsecretstore/cli
- name: Load secrets from vault
run: |
# Secrets go to $GITHUB_ENV — masked in logs automatically
echo "OPENAI_API_KEY=$(ass secrets get production/OPENAI_API_KEY)" >> "$GITHUB_ENV"
echo "DATABASE_URL=$(ass secrets get production/DATABASE_URL)" >> "$GITHUB_ENV"
echo "STRIPE_KEY=$(ass secrets get production/STRIPE_KEY)" >> "$GITHUB_ENV"
- name: Deploy
run: ./scripts/deploy.shNote: GitHub Actions masks values added to
$GITHUB_ENVby matching against registered secrets. StoreASS_AGENT_KEYas a repository or organisation secret — the CLI handles retrieving all other secrets dynamically.
GitLab CI
# .gitlab-ci.yml
deploy:
stage: deploy
image: node:22-alpine
before_script:
- npm install -g @agentsecretstore/cli
- |
export OPENAI_API_KEY=$(ass secrets get production/OPENAI_API_KEY)
export DATABASE_URL=$(ass secrets get production/DATABASE_URL)
script:
- ./scripts/deploy.sh
variables:
ASS_AGENT_KEY: $ASS_AGENT_KEY # set in GitLab CI/CD variablesDocker / docker compose
# Pass secrets to a container at runtime — never bake them into images
docker run \
-e OPENAI_API_KEY="$(ass secrets get production/OPENAI_API_KEY)" \
-e DATABASE_URL="$(ass secrets get production/DATABASE_URL)" \
my-image:latestWith docker compose:
# Export a namespace to a temporary .env file, run compose, then delete it
ass env export production --out /tmp/.env.production
docker compose --env-file /tmp/.env.production up
rm /tmp/.env.productionKubernetes / Helm
# Create a Kubernetes secret from vault contents
kubectl create secret generic app-secrets \
--from-literal=OPENAI_API_KEY="$(ass secrets get production/OPENAI_API_KEY)" \
--from-literal=DATABASE_URL="$(ass secrets get production/DATABASE_URL)"Or use a Helm pre-install hook:
# templates/secrets-job.yaml (runs before app deployment)
apiVersion: batch/v1
kind: Job
metadata:
name: populate-secrets
annotations:
helm.sh/hook: pre-install,pre-upgrade
spec:
template:
spec:
containers:
- name: populate
image: node:22-alpine
command:
- sh
- -c
- |
npm install -g @agentsecretstore/cli
kubectl create secret generic app-secrets \
--from-literal=OPENAI_API_KEY="$(ass secrets get production/OPENAI_API_KEY)" \
--save-config --dry-run=client -o yaml | kubectl apply -f -
env:
- name: ASS_AGENT_KEY
valueFrom:
secretKeyRef:
name: ass-credentials
key: ASS_AGENT_KEYPiping & Scripting
ass is designed to be composable. Use --quiet or --json to suppress decorative output.
Pipe secret value directly to another program
# Feed a certificate to curl without writing it to disk
ass secrets get production/TLS_CERT | curl \
--cert /dev/stdin \
https://internal.example.com/api
# Decode and pipe a base64-encoded key
ass secrets get production/BASE64_KEY | base64 -d > /tmp/key.bin
# Use in a heredoc
psql "$(ass secrets get production/DATABASE_URL)" << 'SQL'
SELECT COUNT(*) FROM users WHERE created_at > NOW() - INTERVAL '7 days';
SQLBulk operations with jq
# List all secrets, then retrieve values for specific keys
ass secrets list production --json \
| jq -r '.secrets[].key' \
| grep "OPENAI\|ANTHROPIC" \
| while read key; do
echo "$key=$(ass secrets get "production/$key")"
done >> .env.ai
# Find all sensitive-tier secrets
ass secrets list production --json \
| jq '.secrets[] | select(.access_tier == "sensitive") | .key'
# Count secrets per namespace
ass secrets list --json \
| jq '.namespaces | to_entries[] | {namespace: .key, count: (.value | length)}'Audit log analysis
# Alert on denied access (send to Slack webhook)
DENIED=$(ass audit --namespace production --action secret.read.denied --since 1h --json \
| jq '.events | length')
if [ "$DENIED" -gt 0 ]; then
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{\"text\": \"⚠️ ${DENIED} denied secret access attempt(s) in the last hour\"}"
fi
# Export audit log to CSV for compliance
ass audit --since 2026-01-01 --json \
| jq -r '.events[] | [.timestamp, .agent_id, .action, .resource] | @csv' \
> audit-q1-2026.csvSecret rotation script
#!/usr/bin/env bash
# scripts/rotate-api-key.sh
set -euo pipefail
NAMESPACE="${1:?Usage: $0 <namespace> <key>}"
KEY="${2:?Usage: $0 <namespace> <key>}"
echo "Rotating ${NAMESPACE}/${KEY}..."
ass secrets rotate "${NAMESPACE}/${KEY}"
# Wait for rotation to complete
sleep 5
# Verify the new key works (example: OpenAI)
NEW_KEY=$(ass secrets get "${NAMESPACE}/${KEY}")
HTTP_STATUS=$(curl -sS -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${NEW_KEY}" \
https://api.openai.com/v1/models)
if [ "$HTTP_STATUS" = "200" ]; then
echo "✓ Rotation successful — new key is valid"
else
echo "✗ New key validation failed (HTTP ${HTTP_STATUS}) — investigate immediately"
exit 1
fiSecurity Model
Envelope Encryption
Every secret is encrypted with a unique per-secret Data Encryption Key (DEK).
Every DEK is encrypted with your tenant's Key Encryption Key (KEK), managed in GCP KMS with HSM backing.
Secret value ──AES-256-GCM──▶ Encrypted secret (Cloud SQL)
│
DEK (per-secret)
│
AES-256-GCM
│
KEK (per-tenant, GCP KMS HSM)Scoped Token Flow
ass never uses your raw agent key for secret operations. On every secrets get:
asssends your agent key to/v1/tokensand requests a minimum-scope token
(e.g.,secrets:read:production/OPENAI_API_KEY)- The API returns a short-lived signed JWT (15m default)
assuses that token for the actual secret retrieval- The token is cached in-memory for its lifetime; your agent key never leaves the auth step
- The access is logged to the immutable audit trail
What ass Does NOT Do
- Store secret values on disk
- Log secret values anywhere
- Send your agent key to any domain other than
api.agentsecretstore.com - Cache secret values between invocations
Contributing
Contributions welcome! The CLI is in cli/src/ and written in TypeScript.
Development Setup
git clone https://github.com/Agent-Secret-Store/agent-secret-store
cd agent-secret-store/cli
npm install
npm run dev # watch mode
npm run build # production build
npm run typecheck # type check without building
npm run test # run testsProject Structure
cli/src/
├── commands/
│ ├── agents/ # ass agents list|create|revoke
│ ├── approvals/ # ass approvals list|approve|deny
│ ├── audit/ # ass audit
│ ├── env/ # ass env import|export
│ ├── secrets/ # ass secrets get|set|list|delete|rotate
│ └── tokens/ # ass tokens list|create|revoke
├── init.ts # ass login
├── login.ts # auth helper
├── logout.ts
├── open.ts # ass open (browser)
├── status.ts # ass status
└── whoami.tsAdding a Command
- Create
cli/src/commands/mygroup/mycommand.ts - Export a
Commandfromcommander - Register it in
cli/src/commands/mygroup/index.ts - Add tests in
cli/src/commands/mygroup/__tests__/
Testing
# Unit tests
npm run test
# Integration (requires ASS_AGENT_KEY pointing to a test tenant)
ASS_AGENT_KEY=ask_live_test_... npm run test:integrationRelease
Releases are automated via the release-cli GitHub Actions workflow.
Bump the version, tag, and push:
npm version patch # or minor / major
git push --follow-tagsThe workflow builds binaries, creates a GitHub release, publishes to npm, and updates the Homebrew tap automatically.
