axvault
v1.8.1
Published
Remote credential storage server for axkit
Maintainers
Readme
axvault
Remote credential storage server for a╳kit.
Quick start
export AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!"
axvault init
axvault serveIn another shell, create an API key:
axvault key create --name "Admin" --read "*" --write "*" --grant "*"Add --verbose to commands like init, serve, key revoke, key update, and
credential delete to see status output.
Pipeline examples
Extract key IDs
axvault key list | tail -n +2 | cut -f1Count credentials by type
axvault credential list | tail -n +2 | cut -f3 | sort | uniq -c | sort -rnList credential paths for a single agent
axvault credential list | tail -n +2 | awk -F'\t' '$1 == "claude" {print $1 "/" $2}'Agent Rule
Add to your CLAUDE.md or AGENTS.md:
# Rule: `axvault` Usage
Run `npx -y axvault --help` to learn available options.
Use `axvault` when you need to initialize the vault database, manage API keys,
or list/delete stored credentials. It outputs TSV tables for list commands, so
you can pipe them into standard Unix tools.Configuration
Environment Variables
| Variable | Description | Default |
| ---------------------------- | -------------------------------------------------------------------- | ------------------- |
| AXVAULT_PORT | Port to listen on | 3847 |
| AXVAULT_HOST | Host to bind to | 127.0.0.1 |
| AXVAULT_DB_PATH | Database file path | ./data/axvault.db |
| AXVAULT_ENCRYPTION_KEY | Encryption key (min 32 chars, required) | — |
| AXVAULT_REFRESH_THRESHOLD | Refresh credentials expiring within this many seconds (0 to disable) | 3600 |
| AXVAULT_REFRESH_TIMEOUT_MS | Timeout for refresh operations in milliseconds | 30000 |
CLI Flags
The serve command accepts flags that override environment variables:
axvault serve \
--port 8080 \
--host 0.0.0.0 \
--db-path /data/vault.db \
--refresh-threshold 7200 \
--refresh-timeout 60000Setting --refresh-threshold 0 disables automatic credential refresh.
API Keys
API keys control access to the credential API. Each key has configurable permissions:
- Read: retrieve credentials
- Write: store and delete credentials
- Grant: delegate access to other keys (enforcement coming in future release)
Create an API Key
# Full access (read, write, and grant)
axvault key create --name "Admin" --read "*" --write "*" --grant "*"
# Read/write access only
axvault key create --name "CI Pipeline" --read "*" --write "*"
# Restricted access
axvault key create --name "Claude Reader" --read "claude/work,claude/ci"
axvault key create --name "Deploy Script" --write "claude/prod,codex/prod"
# Grant-only key (for delegation, does not allow direct read/write)
axvault key create --name "Issuer" --grant "claude/work,claude/ci"The command outputs metadata to stderr and the secret key to stdout for easy piping:
# stderr (visible in terminal):
Created API key: CI Pipeline
ID: k_a1b2c3d4e5f6
Read access: *
Write access: *
Grant access: (none)
Save this key securely - it cannot be retrieved later.
# stdout (can be piped):
axv_sk_0123456789abcdef0123456789abcdefTo copy directly to clipboard: axvault key create --name "My Key" --read "*" | pbcopy
List Keys
axvault key listUpdate a Key
Modify an existing key's permissions:
# Add read access for new credentials
axvault key update k_a1b2c3d4e5f6 --add-read "gemini/prod"
# Remove write access
axvault key update k_a1b2c3d4e5f6 --remove-write "claude/test"
# Add grant permissions
axvault key update k_a1b2c3d4e5f6 --add-grant "claude/work"
# Multiple changes at once
axvault key update k_a1b2c3d4e5f6 --add-read "codex/ci" --remove-write "claude/dev"Revoke a Key
axvault key revoke k_a1b2c3d4e5f6 --forceThis command requires --force or --yes to confirm.
Container Deployments
Running the Container
The image uses an external UID pattern—no user is baked into the image. Always specify a non-root user with -u/--user to limit container privileges:
Security note: Without
-u/--user, the container runs as root. For Kubernetes, setrunAsUser: 1000andrunAsNonRoot: truein your SecurityContext.
# Docker
docker run -d \
--name axvault \
-p 3847:3847 \
-u 1000:1000 \
-e AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!" \
-v /srv/axvault/data:/data \
registry.j4k.dev/axvault:latest
# Podman
podman run -d \
--name axvault \
-p 3847:3847 \
--user 1000:1000 \
-e AXVAULT_ENCRYPTION_KEY="your-secret-key-minimum-32-chars!" \
-v /srv/axvault/data:/data:Z \
registry.j4k.dev/axvault:latestVolume Ownership
The data volume must be owned by the UID/GID the container runs as:
# Create directory and set ownership before first run
sudo mkdir -p /srv/axvault/data
sudo chown 1000:1000 /srv/axvault/dataFor rootless Podman, use your user's UID or let Podman handle mapping automatically.
Quadlet (systemd)
Create /etc/containers/systemd/axvault.container:
[Unit]
Description=axvault credential server
[Container]
Image=registry.j4k.dev/axvault:latest
PublishPort=3847:3847
User=1000
Group=1000
Environment=AXVAULT_ENCRYPTION_KEY=your-secret-key-minimum-32-chars!
Volume=/srv/axvault/data:/data:Z
[Service]
Restart=always
[Install]
WantedBy=multi-user.targetThen reload and start:
sudo systemctl daemon-reload
sudo systemctl start axvaultManaging Keys in Containers
Exec into the container to manage API keys:
# Podman
sudo podman exec axvault node /app/node_modules/axvault/bin/axvault key create --name "My Key" --read "*" --write "*"
# Docker
docker exec axvault node /app/node_modules/axvault/bin/axvault key create --name "My Key" --read "*" --write "*"Credentials API
Store a Credential
curl -X PUT https://vault.example.com/api/v1/credentials/claude/prod \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{
"type": "oauth-credentials",
"provider": "anthropic",
"data": {"access_token": "...", "refresh_token": "..."},
"expiresAt": "2025-12-31T23:59:59Z"
}'The type field is required and must be one of:
"oauth-credentials"— Full OAuth with refresh_token (eligible for auto-refresh)"oauth-token"— Long-lived OAuth token likeCLAUDE_CODE_OAUTH_TOKEN(static)"api-key"— API key (static)
The provider field is optional and stores the provider name for multi-provider agents. For example, OpenCode credentials can specify "anthropic", "openai", "gemini", or "opencode" to distinguish which provider the credential is for.
Retrieve a Credential
curl https://vault.example.com/api/v1/credentials/claude/prod \
-H "Authorization: Bearer <api_key>"Delete a Credential
curl -X DELETE https://vault.example.com/api/v1/credentials/claude/prod \
-H "Authorization: Bearer <api_key>"Auto-Refresh
axvault automatically refreshes oauth-credentials type credentials that are near expiration when they are retrieved. This behavior is controlled by the refresh threshold setting. Only credentials containing a refresh token field are eligible for auto-refresh. Supported field names: refresh_token (standard OAuth), refreshToken (Claude), refresh (OpenCode).
Access Control Note
Auto-refresh is a server-side maintenance operation that occurs transparently during credential retrieval. Read-only API keys can trigger refresh because:
- The refresh uses the credential's own refresh token (already authorized by the token owner)
- The credential's identity and ownership remain unchanged
- Only token values and expiry timestamps are updated
- This prevents wasteful repeated refreshes and rate limit issues
This follows the pattern used by credential vaults like HashiCorp Vault, where credential maintenance is handled transparently on reads.
Response Headers
When retrieving credentials, the response may include these headers:
| Header | Value | Description |
| -------------------------- | ------ | ------------------------------------------------------------ |
| X-Axvault-Refreshed | true | Credential was successfully refreshed during this request |
| X-Axvault-Refresh-Failed | true | Refresh was attempted but failed; stale credentials returned |
When X-Axvault-Refresh-Failed is present, the response still returns HTTP 200 with the existing (potentially expired) credentials. Error details are logged to the audit log.
CI/CD Integration
With axrun (Recommended)
Use the --vault-credential flag to fetch credentials directly:
- name: Run Claude Review
env:
AXVAULT: ${{ secrets.AXVAULT }}
run: |
axrun --agent claude --vault-credential ci-oauth-token \
--prompt "Review this PR"With axauth (Explicit Fetch)
For more control, fetch credentials separately:
- name: Fetch credentials
run: |
axauth vault fetch --agent claude --name ci --env \
| sed 's/^export //' >> $GITHUB_ENV
env:
AXVAULT: ${{ secrets.AXVAULT }}
- name: Run Claude
run: axrun --agent claude --prompt "Hello"Required Secret
Store AXVAULT as a JSON object in your repository secrets:
{ "url": "https://vault.example.com", "apiKey": "axv_sk_..." }Alternatively, use separate environment variables:
| Variable | Description |
| ----------------- | ------------------------ |
| AXVAULT_URL | Vault server URL |
| AXVAULT_API_KEY | API key with read access |
Troubleshooting
Common Errors
| Error | Cause | Solution |
| ---------------- | ------------------------------------------ | -------------------------------------------------------------------------------- |
| not-configured | Missing AXVAULT_URL or AXVAULT_API_KEY | Set both environment variables or use AXVAULT JSON |
| unauthorized | Invalid API key | Verify key with axvault key list, create new if needed |
| forbidden | No access to credential | Add credential to key's read access: axvault key update <id> --add-read <path> |
| not-found | Credential doesn't exist | Store credential first: axauth vault push --agent <agent> --name <name> |
| unreachable | Network issue or server down | Check vault URL, verify server is running |
Debugging
Test vault connectivity:
curl -I $AXVAULT_URL/api/v1/healthVerify API key permissions:
axvault key list # on serverCheck credential exists:
curl -H "Authorization: Bearer $AXVAULT_API_KEY" \ $AXVAULT_URL/api/v1/credentials
License
MIT
