Vultrino
A credential proxy for the AI era — enabling AI agents to use credentials without seeing them.
What is Vultrino?
Vultrino is a secure credential proxy that allows AI agents, LLMs, and automated systems to use credentials without ever exposing them. Instead of giving your AI agent direct access to secrets, you give it access to Vultrino, which performs authenticated actions on behalf of the agent — whether that’s making API calls, signing data with PGP keys, or any other credential-based operation.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AI Agent │────▶│ Vultrino │────▶│ External API │
│ (Claude, etc) │ │ (uses secrets) │ │ or Operation │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ "Use my-credential" │ Injects auth, signs data, etc.
│ │
▼ ▼
Never sees secrets Performs secure actions
Key Features
- Credential Isolation — AI agents never see actual secrets or private keys
- Role-Based Access Control — Fine-grained permissions for different applications
- Multiple Credential Types — API keys, Basic Auth, PGP keys, and more
- Plugin System — Extend with custom credential types and actions via WASM plugins
- MCP Integration — Native Model Context Protocol support for LLM tools
- Web UI — Clean admin interface for managing credentials and keys
- Encrypted Storage — AES-256-GCM encryption with Argon2 key derivation
- Policy Engine — URL patterns, method restrictions, rate limiting
- Audit Logging — Track all credential usage
Use Cases
AI Agent Security
Give Claude, GPT, or other AI agents the ability to call APIs without exposing credentials. The agent requests actions through Vultrino, which handles authentication transparently.
Team Credential Management
Centralize API credentials for your team. Create scoped API keys for different applications with specific permissions.
Development Environments
Safely share credentials across development, staging, and production without exposing secrets in code or environment variables.
Quick Example
# Add a credential
vultrino add --alias github-api --key ghp_your_token_here
# Make an authenticated request
vultrino request github-api https://api.github.com/user
# Or use with AI agents via MCP
vultrino serve --mcp
Components
| Component | Description |
|---|---|
| CLI | Command-line interface for all operations |
| Web UI | Browser-based admin dashboard |
| HTTP Proxy | Makes authenticated requests on behalf of agents |
| MCP Server | Model Context Protocol server for LLM integration |
Next Steps
- Installation — Get Vultrino running
- Quick Start — Add your first credential
- Using with AI Agents — Configure LLM integration
- Plugin System — Extend with custom credential types
Installation
Requirements
- Rust 1.75 or later (for building from source)
- OpenSSL development libraries (on Linux)
From Source (Recommended)
# Clone the repository
git clone https://github.com/vultrino/vultrino.git
cd vultrino
# Build in release mode
cargo build --release
# The binary will be at target/release/vultrino
# Optionally, copy to your PATH
cp target/release/vultrino /usr/local/bin/
Using Cargo
cargo install vultrino
Pre-built Binaries
Download pre-built binaries from the GitHub Releases page.
macOS
# Intel
curl -L https://github.com/vultrino/vultrino/releases/latest/download/vultrino-x86_64-apple-darwin.tar.gz | tar xz
sudo mv vultrino /usr/local/bin/
# Apple Silicon
curl -L https://github.com/vultrino/vultrino/releases/latest/download/vultrino-aarch64-apple-darwin.tar.gz | tar xz
sudo mv vultrino /usr/local/bin/
Linux
# x86_64
curl -L https://github.com/vultrino/vultrino/releases/latest/download/vultrino-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv vultrino /usr/local/bin/
# ARM64
curl -L https://github.com/vultrino/vultrino/releases/latest/download/vultrino-aarch64-unknown-linux-gnu.tar.gz | tar xz
sudo mv vultrino /usr/local/bin/
Verify Installation
vultrino --version
# vultrino 0.1.0
Next Steps
Continue to Quick Start to initialize Vultrino and add your first credential.
Quick Start
This guide will get you up and running with Vultrino in under 5 minutes.
1. Initialize Vultrino
First, initialize the configuration and set up your admin account:
vultrino init
You’ll be prompted to:
- Set a storage password (encrypts your credentials at rest)
- Create an admin username for the web UI
- Set an admin password for the web UI
Tip: Set the
VULTRINO_PASSWORDenvironment variable to avoid password prompts:export VULTRINO_PASSWORD="your-secure-password"
2. Add Your First Credential
Add an API key credential:
vultrino add --alias github-api --key ghp_your_github_token
Add a Basic Auth credential:
vultrino add --alias my-service --type basic_auth --username admin --password secret123
3. List Your Credentials
vultrino list
Output:
ALIAS TYPE ID DESCRIPTION
github-api api_key a1b2c3d4-... -
my-service basic_auth e5f6g7h8-... -
4. Make an Authenticated Request
Use the request command to make API calls with your stored credentials:
vultrino request github-api https://api.github.com/user
The credential is automatically injected — you never need to expose the actual token.
5. Start the Web UI
Launch the admin dashboard:
vultrino web
Open http://127.0.0.1:7879 and log in with your admin credentials.
6. Use with AI Agents (MCP)
Start the MCP server for LLM integration:
vultrino serve --mcp
Configure your AI agent (Claude, etc.) to use Vultrino as an MCP server. See Using with AI Agents for detailed setup instructions.
Common Commands
| Command | Description |
|---|---|
vultrino init | Initialize configuration |
vultrino add | Add a credential |
vultrino list | List all credentials |
vultrino remove <alias> | Remove a credential |
vultrino request <alias> <url> | Make authenticated request |
vultrino web | Start web UI |
vultrino serve --mcp | Start MCP server |
vultrino role list | List roles |
vultrino key create | Create API key |
Next Steps
- Configuration — Customize Vultrino settings
- Managing Credentials — Advanced credential management
- Roles & API Keys — Set up access control
- Using with AI Agents — MCP integration guide
Configuration
Vultrino uses a TOML configuration file located at:
- macOS:
~/Library/Application Support/vultrino/config.toml - Linux:
~/.config/vultrino/config.toml - Windows:
%APPDATA%\vultrino\config.toml
Default Configuration
# Vultrino Configuration
[server]
bind = "127.0.0.1:7878"
mode = "local"
[storage]
backend = "file"
[storage.file]
path = "~/.local/share/vultrino/credentials.enc"
[logging]
level = "info"
# audit_file = "~/.local/share/vultrino/audit.log"
[mcp]
enabled = true
transport = "stdio"
Configuration Options
Server Section
[server]
bind = "127.0.0.1:7878" # Address for HTTP proxy
mode = "local" # "local" or "server"
| Option | Description | Default |
|---|---|---|
bind | Address and port for the HTTP proxy | 127.0.0.1:7878 |
mode | Deployment mode (local or server) | local |
Storage Section
[storage]
backend = "file" # Storage backend: "file", "keychain", or "vault"
[storage.file]
path = "~/.local/share/vultrino/credentials.enc"
| Option | Description | Default |
|---|---|---|
backend | Storage backend type | file |
path | Path to encrypted credentials file | OS-specific |
Logging Section
[logging]
level = "info" # Log level: error, warn, info, debug, trace
# audit_file = "~/.local/share/vultrino/audit.log" # Optional audit log
| Option | Description | Default |
|---|---|---|
level | Logging verbosity | info |
audit_file | Path to audit log (optional) | disabled |
MCP Section
[mcp]
enabled = true
transport = "stdio" # "stdio" or "http"
| Option | Description | Default |
|---|---|---|
enabled | Enable MCP server | true |
transport | Transport method | stdio |
Environment Variables
| Variable | Description |
|---|---|
VULTRINO_PASSWORD | Storage encryption password (avoids prompts) |
VULTRINO_CONFIG | Path to config file |
RUST_LOG | Override log level (e.g., vultrino=debug) |
Policy Configuration
Policies control which requests are allowed for each credential:
[[policies]]
name = "github-readonly"
credential_pattern = "github-*" # Glob pattern for credential aliases
default_action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.github.com/*" }
action = "allow"
[[policies.rules]]
condition = { method_match = ["POST", "PUT", "DELETE"] }
action = "deny"
See Policy Configuration for detailed policy options.
Using a Custom Config File
vultrino --config /path/to/config.toml list
Regenerating Configuration
To reset to defaults:
vultrino init --force
Warning: This will overwrite your existing configuration and require re-entering admin credentials.
Deployment Overview
Vultrino can be deployed in several ways depending on your needs:
Deployment Options
| Method | Best For | Complexity |
|---|---|---|
| Local Development | Personal use, testing | Simple |
| VPS / Server | Team deployment, production | Moderate |
| Cloudflare Workers | Edge deployment, serverless | Moderate |
| Docker | Containerized environments | Simple |
Architecture Considerations
Single Binary
Vultrino is distributed as a single binary with no external dependencies. This includes:
- CLI commands
- HTTP proxy server
- Web UI (embedded)
- MCP server
Storage
Credentials are stored encrypted using AES-256-GCM. Storage options:
- File (default) — Encrypted JSON file on disk
- Keychain — OS keychain integration (coming soon)
- Vault — HashiCorp Vault integration (coming soon)
Network Security
Local Mode (default):
- Binds to
127.0.0.1only - No external access
- Suitable for single-machine use
Server Mode:
- Can bind to all interfaces (
0.0.0.0) - Requires additional security measures
- Use with TLS termination proxy (nginx, Caddy)
Security Recommendations
- Always use HTTPS in production — Use a reverse proxy with TLS
- Restrict network access — Firewall rules, VPN, or private network
- Use strong passwords — Both storage and admin passwords
- Enable audit logging — Track credential usage
- Rotate API keys — Set expiration on Vultrino API keys
- Principle of least privilege — Create scoped roles for each application
Quick Comparison
┌─────────────────────────────────────────────────────────────────┐
│ LOCAL │
│ vultrino serve │
│ ├── Best for: Personal use, development │
│ ├── Security: Localhost only │
│ └── Setup: Minimal │
├─────────────────────────────────────────────────────────────────┤
│ VPS/SERVER │
│ vultrino serve + nginx/caddy │
│ ├── Best for: Team use, production │
│ ├── Security: TLS, firewall, auth │
│ └── Setup: Moderate (systemd, reverse proxy) │
├─────────────────────────────────────────────────────────────────┤
│ CLOUDFLARE │
│ Cloudflare Workers + KV/Durable Objects │
│ ├── Best for: Edge deployment, global access │
│ ├── Security: Cloudflare's infrastructure │
│ └── Setup: Moderate (requires adaptation) │
├─────────────────────────────────────────────────────────────────┤
│ DOCKER │
│ docker run vultrino │
│ ├── Best for: Containerized environments │
│ ├── Security: Container isolation │
│ └── Setup: Simple (docker-compose) │
└─────────────────────────────────────────────────────────────────┘
Next Steps
Choose your deployment method:
- Local Development — Start here for testing
- VPS / Server — Production deployment
- Cloudflare Workers — Edge/serverless deployment
- Docker — Container deployment
Local Development
The simplest way to run Vultrino — perfect for personal use and development.
Quick Setup
# 1. Initialize
vultrino init
# 2. Add credentials
vultrino add --alias github-api --key ghp_xxx
# 3. Start services
vultrino web & # Web UI on :7879
vultrino serve --mcp # MCP server (stdio)
Running Components
Web UI Only
export VULTRINO_PASSWORD="your-password"
vultrino web
# Access at http://127.0.0.1:7879
MCP Server Only
For AI agent integration:
export VULTRINO_PASSWORD="your-password"
vultrino serve --mcp
HTTP Proxy
export VULTRINO_PASSWORD="your-password"
vultrino serve
# Proxy on http://127.0.0.1:7878
Configuration for Local Use
The default configuration is optimized for local development:
[server]
bind = "127.0.0.1:7878" # Localhost only
mode = "local"
[storage]
backend = "file"
Using with Claude Desktop
Add to your Claude Desktop MCP configuration (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"vultrino": {
"command": "/path/to/vultrino",
"args": ["serve", "--mcp"],
"env": {
"VULTRINO_PASSWORD": "your-password"
}
}
}
}
Running as Background Process
macOS (launchd)
Create ~/Library/LaunchAgents/dev.vultrino.web.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>dev.vultrino.web</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/vultrino</string>
<string>web</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>VULTRINO_PASSWORD</key>
<string>your-password</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
Load it:
launchctl load ~/Library/LaunchAgents/dev.vultrino.web.plist
Linux (systemd user service)
Create ~/.config/systemd/user/vultrino-web.service:
[Unit]
Description=Vultrino Web UI
After=network.target
[Service]
Type=simple
Environment="VULTRINO_PASSWORD=your-password"
ExecStart=/usr/local/bin/vultrino web
Restart=always
[Install]
WantedBy=default.target
Enable and start:
systemctl --user enable vultrino-web
systemctl --user start vultrino-web
Tips
- Store password in keychain — Use OS keychain to avoid plaintext passwords
- Use aliases — Add
alias vreq='vultrino request'to your shell - Tab completion — Generate with
vultrino completions bash > /etc/bash_completion.d/vultrino
Troubleshooting
“Device not configured” error
The password prompt requires a terminal. Set VULTRINO_PASSWORD environment variable instead.
“Address already in use”
Another process is using the port. Check with:
lsof -i :7879
Credentials not loading
Ensure you’re using the same VULTRINO_PASSWORD that was used when creating credentials.
VPS / Server Deployment
Deploy Vultrino on a VPS or dedicated server for team access and production use.
Prerequisites
- Linux server (Ubuntu 22.04+ recommended)
- Domain name (optional but recommended)
- TLS certificate (Let’s Encrypt)
Installation
# Download latest release
curl -L https://github.com/vultrino/vultrino/releases/latest/download/vultrino-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv vultrino /usr/local/bin/
# Create vultrino user
sudo useradd -r -s /bin/false vultrino
# Create directories
sudo mkdir -p /etc/vultrino /var/lib/vultrino
sudo chown vultrino:vultrino /var/lib/vultrino
Configuration
Create /etc/vultrino/config.toml:
[server]
bind = "127.0.0.1:7878"
mode = "server"
[storage]
backend = "file"
[storage.file]
path = "/var/lib/vultrino/credentials.enc"
[logging]
level = "info"
audit_file = "/var/log/vultrino/audit.log"
[mcp]
enabled = true
transport = "stdio"
Systemd Services
Web UI Service
Create /etc/systemd/system/vultrino-web.service:
[Unit]
Description=Vultrino Web UI
After=network.target
[Service]
Type=simple
User=vultrino
Group=vultrino
Environment="VULTRINO_PASSWORD=your-secure-password"
Environment="VULTRINO_CONFIG=/etc/vultrino/config.toml"
ExecStart=/usr/local/bin/vultrino web --bind 127.0.0.1:7879
Restart=always
RestartSec=5
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/vultrino
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
HTTP Proxy Service
Create /etc/systemd/system/vultrino-proxy.service:
[Unit]
Description=Vultrino HTTP Proxy
After=network.target
[Service]
Type=simple
User=vultrino
Group=vultrino
Environment="VULTRINO_PASSWORD=your-secure-password"
Environment="VULTRINO_CONFIG=/etc/vultrino/config.toml"
ExecStart=/usr/local/bin/vultrino serve --bind 127.0.0.1:7878
Restart=always
RestartSec=5
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/vultrino
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable vultrino-web vultrino-proxy
sudo systemctl start vultrino-web vultrino-proxy
Reverse Proxy Setup
Nginx
Install nginx and certbot:
sudo apt install nginx certbot python3-certbot-nginx
Create /etc/nginx/sites-available/vultrino:
server {
listen 80;
server_name vultrino.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name vultrino.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/vultrino.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vultrino.yourdomain.com/privkey.pem;
# Web UI
location / {
proxy_pass http://127.0.0.1:7879;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API/Proxy endpoint (optional)
location /proxy/ {
proxy_pass http://127.0.0.1:7878/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Enable and get certificate:
sudo ln -s /etc/nginx/sites-available/vultrino /etc/nginx/sites-enabled/
sudo certbot --nginx -d vultrino.yourdomain.com
sudo systemctl reload nginx
Caddy (Alternative)
Create /etc/caddy/Caddyfile:
vultrino.yourdomain.com {
reverse_proxy /proxy/* 127.0.0.1:7878
reverse_proxy * 127.0.0.1:7879
}
Firewall Configuration
# Allow only HTTPS
sudo ufw allow 443/tcp
sudo ufw allow 22/tcp # SSH
sudo ufw enable
Initialize Credentials
# Set password
export VULTRINO_PASSWORD="your-secure-password"
# Initialize (as vultrino user)
sudo -u vultrino VULTRINO_PASSWORD="$VULTRINO_PASSWORD" vultrino init
# Add credentials
sudo -u vultrino VULTRINO_PASSWORD="$VULTRINO_PASSWORD" vultrino add --alias github-api --key ghp_xxx
Security Checklist
- Strong storage password (32+ characters)
- Strong admin password
- TLS enabled (HTTPS only)
- Firewall configured
- Audit logging enabled
- Regular backups of
/var/lib/vultrino/ - API keys have expiration dates
- Roles use principle of least privilege
Monitoring
Check service status
sudo systemctl status vultrino-web vultrino-proxy
View logs
sudo journalctl -u vultrino-web -f
sudo tail -f /var/log/vultrino/audit.log
Health check
curl -s http://127.0.0.1:7879/login | head -1
Backup & Restore
Backup
sudo tar -czf vultrino-backup-$(date +%Y%m%d).tar.gz \
/etc/vultrino \
/var/lib/vultrino
Restore
sudo tar -xzf vultrino-backup-YYYYMMDD.tar.gz -C /
sudo systemctl restart vultrino-web vultrino-proxy
Cloudflare Workers Deployment
Note: Cloudflare Workers deployment requires adapting Vultrino to the Workers runtime. This guide covers the architecture and approach.
Overview
Deploying Vultrino to Cloudflare Workers provides:
- Global edge deployment
- Serverless scaling
- Cloudflare’s security infrastructure
- Low-latency access worldwide
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────────────────────┐ │
│ │ Workers │ │ Durable Objects │ │
│ │ (HTTP API) │────▶│ (Session State, Credentials) │ │
│ └─────────────────┘ └─────────────────────────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌─────────────────────────────────┐ │
│ │ │ KV │ │
│ │ │ (Encrypted Credential Storage) │ │
│ │ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ External APIs │ │
│ │ (via fetch) │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Components Mapping
| Vultrino Component | Cloudflare Equivalent |
|---|---|
| Encrypted file storage | KV with encryption |
| Session management | Durable Objects |
| HTTP handlers | Workers |
| Static assets | Workers Sites or R2 |
Implementation Approach
1. Create Worker Project
npm create cloudflare@latest vultrino-edge -- --template hello-world
cd vultrino-edge
2. Configure wrangler.toml
name = "vultrino"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "CREDENTIALS"
id = "your-kv-namespace-id"
[[durable_objects.bindings]]
name = "SESSIONS"
class_name = "SessionDO"
[[migrations]]
tag = "v1"
new_classes = ["SessionDO"]
3. Implement Core Logic
// src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
type Bindings = {
CREDENTIALS: KVNamespace;
SESSIONS: DurableObjectNamespace;
ENCRYPTION_KEY: string;
};
const app = new Hono<{ Bindings: Bindings }>();
// Middleware
app.use('*', cors());
// Login
app.post('/login', async (c) => {
const { username, password } = await c.req.json();
// Verify credentials, create session
// ...
});
// Execute request with credential
app.post('/v1/execute', async (c) => {
const { credential, action, params } = await c.req.json();
// Get encrypted credential from KV
const encryptedCred = await c.env.CREDENTIALS.get(credential);
if (!encryptedCred) {
return c.json({ error: 'Credential not found' }, 404);
}
// Decrypt credential
const cred = await decrypt(encryptedCred, c.env.ENCRYPTION_KEY);
// Execute request with injected auth
const response = await executeWithCredential(cred, params);
return c.json(response);
});
export default app;
4. Credential Encryption
Use Web Crypto API for encryption:
async function encrypt(data: string, key: string): Promise<string> {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
'raw',
encoder.encode(key),
'PBKDF2',
false,
['deriveBits', 'deriveKey']
);
const derivedKey = await crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: encoder.encode('vultrino-salt'),
iterations: 100000,
hash: 'SHA-256',
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
derivedKey,
encoder.encode(data)
);
// Return iv + ciphertext as base64
return btoa(String.fromCharCode(...iv, ...new Uint8Array(encrypted)));
}
5. Deploy
# Set secrets
wrangler secret put ENCRYPTION_KEY
wrangler secret put ADMIN_PASSWORD_HASH
# Deploy
wrangler deploy
Limitations
| Feature | Status | Notes |
|---|---|---|
| HTTP Proxy | ✅ | Via fetch() |
| Web UI | ✅ | Workers Sites |
| MCP Server | ❌ | Requires stdio (not available) |
| File Storage | ⚠️ | Use KV instead |
| OS Keychain | ❌ | Not available |
Security Considerations
- Secrets Management — Use Wrangler secrets for encryption keys
- KV Encryption — Always encrypt credentials before storing in KV
- Access Control — Use Cloudflare Access for additional auth layer
- Audit Logging — Log to Workers Analytics or external service
Alternative: Hybrid Deployment
For MCP support, consider a hybrid approach:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AI Agent │────▶│ Local Vultrino │────▶│ Cloudflare │
│ (MCP) │ │ (MCP Server) │ │ Vultrino Edge │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
Syncs credentials
from edge storage
This allows:
- MCP support via local instance
- Centralized credential management on edge
- Web UI accessible globally
Docker Deployment
Run Vultrino in containers for easy deployment and isolation.
Quick Start
docker run -d \
--name vultrino \
-p 7879:7879 \
-e VULTRINO_PASSWORD=your-secure-password \
-v vultrino-data:/data \
ghcr.io/vultrino/vultrino:latest web
Dockerfile
FROM rust:1.75-slim as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/vultrino /usr/local/bin/
# Create non-root user
RUN useradd -r -s /bin/false vultrino
USER vultrino
WORKDIR /data
VOLUME ["/data"]
EXPOSE 7878 7879
ENTRYPOINT ["vultrino"]
CMD ["web"]
Docker Compose
Create docker-compose.yml:
version: '3.8'
services:
vultrino-web:
image: ghcr.io/vultrino/vultrino:latest
command: web --bind 0.0.0.0:7879
ports:
- "7879:7879"
environment:
- VULTRINO_PASSWORD=${VULTRINO_PASSWORD}
volumes:
- vultrino-data:/data
- ./config.toml:/etc/vultrino/config.toml:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:7879/login"]
interval: 30s
timeout: 10s
retries: 3
vultrino-proxy:
image: ghcr.io/vultrino/vultrino:latest
command: serve --bind 0.0.0.0:7878
ports:
- "7878:7878"
environment:
- VULTRINO_PASSWORD=${VULTRINO_PASSWORD}
volumes:
- vultrino-data:/data
- ./config.toml:/etc/vultrino/config.toml:ro
restart: unless-stopped
volumes:
vultrino-data:
Create .env:
VULTRINO_PASSWORD=your-secure-password
Run:
docker-compose up -d
With Traefik (Reverse Proxy)
version: '3.8'
services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
vultrino:
image: ghcr.io/vultrino/vultrino:latest
command: web --bind 0.0.0.0:7879
environment:
- VULTRINO_PASSWORD=${VULTRINO_PASSWORD}
volumes:
- vultrino-data:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.vultrino.rule=Host(`vultrino.yourdomain.com`)"
- "traefik.http.routers.vultrino.entrypoints=websecure"
- "traefik.http.routers.vultrino.tls.certresolver=letsencrypt"
- "traefik.http.services.vultrino.loadbalancer.server.port=7879"
volumes:
vultrino-data:
letsencrypt:
Kubernetes
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: vultrino
spec:
replicas: 1
selector:
matchLabels:
app: vultrino
template:
metadata:
labels:
app: vultrino
spec:
containers:
- name: vultrino-web
image: ghcr.io/vultrino/vultrino:latest
args: ["web", "--bind", "0.0.0.0:7879"]
ports:
- containerPort: 7879
env:
- name: VULTRINO_PASSWORD
valueFrom:
secretKeyRef:
name: vultrino-secrets
key: password
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: vultrino-pvc
---
apiVersion: v1
kind: Service
metadata:
name: vultrino
spec:
selector:
app: vultrino
ports:
- port: 80
targetPort: 7879
---
apiVersion: v1
kind: Secret
metadata:
name: vultrino-secrets
type: Opaque
stringData:
password: your-secure-password
Building the Image
# Clone repository
git clone https://github.com/vultrino/vultrino.git
cd vultrino
# Build image
docker build -t vultrino:local .
# Run
docker run -d \
--name vultrino \
-p 7879:7879 \
-e VULTRINO_PASSWORD=test \
-v vultrino-data:/data \
vultrino:local web
Environment Variables
| Variable | Description | Required |
|---|---|---|
VULTRINO_PASSWORD | Storage encryption password | Yes |
VULTRINO_CONFIG | Path to config file | No |
RUST_LOG | Log level | No |
Volumes
| Path | Description |
|---|---|
/data | Credential storage and state |
/etc/vultrino/config.toml | Configuration file (optional) |
Initializing in Docker
# Initialize (creates admin credentials)
docker run -it --rm \
-v vultrino-data:/data \
ghcr.io/vultrino/vultrino:latest \
init
# Add a credential
docker run -it --rm \
-e VULTRINO_PASSWORD=your-password \
-v vultrino-data:/data \
ghcr.io/vultrino/vultrino:latest \
add --alias github-api --key ghp_xxx
Health Checks
# Check web UI
curl -f http://localhost:7879/login
# Check proxy
curl -f http://localhost:7878/health
CLI Reference
The Vultrino CLI provides complete control over credential management, server operations, and administration.
Global Options
vultrino [OPTIONS] <COMMAND>
Options:
-c, --config <FILE> Path to config file
-v, --verbose Enable verbose output
-h, --help Print help
-V, --version Print version
Environment Variables
| Variable | Description |
|---|---|
VULTRINO_PASSWORD | Storage encryption password (required) |
VULTRINO_CONFIG | Path to config file |
RUST_LOG | Log level (trace, debug, info, warn, error) |
Commands
init
Initialize a new Vultrino instance.
vultrino init [OPTIONS]
Options:
--force Overwrite existing configuration
This command:
- Creates the credentials storage file
- Prompts for admin username and password
- Sets up initial configuration
Example:
export VULTRINO_PASSWORD="your-secure-password"
vultrino init
# Enter admin username: admin
# Enter admin password: ********
add
Add a new credential to storage.
vultrino add [OPTIONS]
Options:
-a, --alias <ALIAS> Human-readable name (required)
-t, --type <TYPE> Credential type [default: api_key]
-k, --key <KEY> API key or secret value
-u, --username <USERNAME> Username (for basic auth)
-p, --password <PASSWORD> Password (for basic auth)
--description <DESC> Optional description
Credential Types:
api_key— API key or tokenbasic_auth— Username and passwordoauth2— OAuth2 credentials (client ID, secret, tokens)private_key— SSH or signing key
Examples:
# Add an API key
vultrino add --alias github-api --key ghp_xxx...
# Add basic auth credentials
vultrino add --alias jira-api --type basic_auth \
--username user@example.com --password secret123
# Add with description
vultrino add --alias stripe-api --key sk_live_xxx \
--description "Production Stripe key"
list
List all stored credentials.
vultrino list [OPTIONS]
Options:
--json Output as JSON
Example:
vultrino list
# ID Alias Type Created
# 550e8400-e29b-41d4-a716-446655440000 github-api api_key 2024-01-15
# 6ba7b810-9dad-11d1-80b4-00c04fd430c8 stripe-api api_key 2024-01-16
get
Get details about a specific credential.
vultrino get <ALIAS>
Options:
--show-secret Display the secret value (use with caution)
Example:
vultrino get github-api
# Alias: github-api
# Type: api_key
# Created: 2024-01-15T10:30:00Z
# Description: GitHub personal access token
delete
Remove a credential from storage.
vultrino delete <ALIAS>
Options:
--force Skip confirmation prompt
Example:
vultrino delete old-api-key
# Are you sure you want to delete 'old-api-key'? [y/N] y
# Deleted credential: old-api-key
serve
Start the HTTP proxy server.
vultrino serve [OPTIONS]
Options:
-b, --bind <ADDR> Bind address [default: 127.0.0.1:7878]
--mcp Start as MCP server (stdio transport)
Examples:
# Start HTTP proxy
vultrino serve
# Listening on http://127.0.0.1:7878
# Start on custom port
vultrino serve --bind 0.0.0.0:8080
# Start MCP server for AI agents
vultrino serve --mcp
web
Start the web administration UI.
vultrino web [OPTIONS]
Options:
-b, --bind <ADDR> Bind address [default: 127.0.0.1:7879]
Example:
vultrino web
# Web UI available at http://127.0.0.1:7879
request
Make an authenticated HTTP request.
vultrino request [OPTIONS] <URL>
Options:
-c, --credential <ALIAS> Credential to use (required)
-X, --method <METHOD> HTTP method [default: GET]
-H, --header <HEADER> Additional headers (can be repeated)
-d, --data <DATA> Request body
--json Output response as JSON
Examples:
# Simple GET request
vultrino request -c github-api https://api.github.com/user
# POST with JSON body
vultrino request -c stripe-api \
-X POST \
-H "Content-Type: application/json" \
-d '{"amount": 1000, "currency": "usd"}' \
https://api.stripe.com/v1/charges
# With verbose output
vultrino request -c api-key https://api.example.com/data --json
role
Manage RBAC roles.
vultrino role <SUBCOMMAND>
Subcommands:
create Create a new role
list List all roles
get Get role details
delete Delete a role
Create a role:
vultrino role create <NAME> [OPTIONS]
Options:
-p, --permissions <PERMS> Comma-separated: read,write,update,delete,execute
-s, --scopes <SCOPES> Credential patterns (glob): "github-*,stripe-*"
-d, --description <DESC> Role description
Examples:
# Create read-only role
vultrino role create readonly --permissions read
# Create role for GitHub credentials only
vultrino role create github-executor \
--permissions read,execute \
--scopes "github-*" \
--description "Execute requests with GitHub credentials"
# List roles
vultrino role list
# Delete role
vultrino role delete old-role
key
Manage API keys for programmatic access.
vultrino key <SUBCOMMAND>
Subcommands:
create Create a new API key
list List all API keys
revoke Revoke an API key
Create an API key:
vultrino key create <NAME> [OPTIONS]
Options:
-r, --role <ROLE> Role to assign (required)
-e, --expires <DURATION> Expiration (e.g., "30d", "1y")
Examples:
# Create key with role
vultrino key create my-app --role github-executor
# Created API key: vk_a1b2c3d4e5f6...
# (Save this key - it won't be shown again)
# Create key with expiration
vultrino key create temp-key --role readonly --expires 7d
# List keys
vultrino key list
# Revoke key
vultrino key revoke vk_a1b2c3d4
completions
Generate shell completions.
vultrino completions <SHELL>
Shells:
bash, zsh, fish, powershell
Examples:
# Bash
vultrino completions bash > /etc/bash_completion.d/vultrino
# Zsh
vultrino completions zsh > ~/.zfunc/_vultrino
# Fish
vultrino completions fish > ~/.config/fish/completions/vultrino.fish
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| 3 | Credential not found |
| 4 | Permission denied |
| 5 | Storage error |
Tips
Use Aliases
Add to your shell profile:
alias vreq='vultrino request'
alias vcred='vultrino list'
Store Password Securely
On macOS, use Keychain:
security add-generic-password -a vultrino -s vultrino -w "your-password"
export VULTRINO_PASSWORD=$(security find-generic-password -a vultrino -s vultrino -w)
On Linux, use a secrets manager or environment file:
# ~/.vultrino-env (chmod 600)
export VULTRINO_PASSWORD="your-password"
# In shell profile
source ~/.vultrino-env
Web UI
The Vultrino Web UI provides a browser-based interface for managing credentials, roles, and API keys.
Overview
The web interface offers:
- Dashboard with usage statistics
- Credential management (add, view, delete)
- Role-based access control configuration
- API key generation and management
- Audit log viewing
Starting the Web UI
export VULTRINO_PASSWORD="your-password"
vultrino web
# Web UI available at http://127.0.0.1:7879
Custom bind address:
vultrino web --bind 0.0.0.0:8080
Authentication
The web UI requires authentication with the admin credentials set during vultrino init.
Login
Navigate to http://127.0.0.1:7879 and enter:
- Username: The admin username set during init
- Password: The admin password set during init
Sessions expire after 24 hours of inactivity.
Changing Admin Password
Currently, to change the admin password:
-
Delete the admin configuration:
rm ~/.vultrino/admin.json -
Reinitialize:
vultrino init
Pages
Dashboard
The main dashboard displays:
- Total credentials stored
- Number of roles configured
- Active API keys
- Recent audit activity
Credentials
List View (/credentials)
- Shows all stored credentials
- Displays alias, type, and creation date
- Credentials are never shown in the UI
Add Credential (/credentials/new)
- Form to add new credentials
- Supported types: API Key, Basic Auth
- Optional description field
Delete Credential
- Click delete button on credential row
- Confirmation required
Roles
List View (/roles)
- Shows all configured roles
- Displays permissions and credential scopes
Create Role (/roles/new)
- Name and description
- Permission checkboxes:
- Read — List credentials
- Write — Create credentials
- Update — Modify credentials
- Delete — Remove credentials
- Execute — Use credentials for requests
- Credential scopes (glob patterns)
API Keys
List View (/keys)
- Shows all API keys (prefix only)
- Displays assigned role and expiration
- Shows last used timestamp
Create Key (/keys/new)
- Key name for identification
- Role selection dropdown
- Optional expiration date
Revoke Key
- Click revoke button on key row
- Immediate revocation, no confirmation
Audit Log
View (/audit)
- Recent credential usage events
- Shows timestamp, action, credential used
- IP address and user agent when available
Configuration
Session Settings
Configure in config.toml:
[web]
session_timeout = 86400 # 24 hours in seconds
cookie_secure = true # Require HTTPS for cookies
Binding
For production, always bind to localhost and use a reverse proxy:
[web]
bind = "127.0.0.1:7879"
Security Considerations
HTTPS
The web UI should always be accessed over HTTPS in production. Use a reverse proxy like nginx or Caddy for TLS termination.
Session Security
- Sessions are stored server-side
- Session IDs are cryptographically random
- Cookies are HTTP-only and secure (when behind HTTPS)
CSRF Protection
Forms include CSRF tokens to prevent cross-site request forgery.
Rate Limiting
Login attempts are rate-limited to prevent brute force attacks.
Screenshots
Login Page
┌─────────────────────────────────────────┐
│ Vultrino │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Username │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Password │ │
│ └─────────────────────────────────┘ │
│ │
│ [ Sign in ] │
│ │
└─────────────────────────────────────────┘
Dashboard
┌─────────────────────────────────────────────────────────────┐
│ Vultrino Credentials Roles API Keys Audit [Logout] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 5 │ │ 3 │ │ 2 │ │ 127 │ │
│ │ Creds │ │ Roles │ │ Keys │ │ Requests│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ Recent Activity │
│ ─────────────────────────────────────────────────── │
│ 10:30 github-api GET /user │
│ 10:28 stripe-api POST /v1/charges │
│ 10:25 github-api GET /repos │
│ │
└─────────────────────────────────────────────────────────────┘
Credentials List
┌─────────────────────────────────────────────────────────────┐
│ Credentials [ + New Credential]│
├─────────────────────────────────────────────────────────────┤
│ │
│ Alias Type Created Actions │
│ ───────────────────────────────────────────────────── │
│ github-api api_key Jan 15, 2024 [Delete] │
│ stripe-api api_key Jan 16, 2024 [Delete] │
│ jira-api basic_auth Jan 17, 2024 [Delete] │
│ │
└─────────────────────────────────────────────────────────────┘
Troubleshooting
“Invalid credentials” error
- Verify username and password match those set during
vultrino init - Check that admin.json exists in the data directory
Session expires immediately
- Ensure cookies are enabled in your browser
- If using HTTPS, verify
cookie_securematches your setup
Cannot access from external machine
- By default, the web UI binds to localhost only
- Use a reverse proxy to expose it securely
- Never bind directly to 0.0.0.0 in production without TLS
Blank page or errors
- Check browser console for JavaScript errors
- Verify the server is running:
curl http://127.0.0.1:7879/login - Check server logs for errors
HTTP Proxy
The Vultrino HTTP Proxy automatically injects credentials into outgoing HTTP requests, allowing applications to make authenticated API calls without knowing the actual secrets.
Overview
The proxy:
- Accepts HTTP requests with a credential alias header
- Looks up the credential from encrypted storage
- Injects appropriate authentication (API key, Bearer token, Basic auth)
- Forwards the request to the target URL
- Returns the response to the client
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Client │────▶│ Vultrino │────▶│ Target │
│ (no creds) │ │ Proxy │ │ API │
└──────────────┘ └──────────────┘ └──────────────┘
Injects auth header
Starting the Proxy
export VULTRINO_PASSWORD="your-password"
vultrino serve
# Proxy listening on http://127.0.0.1:7878
Custom bind address:
vultrino serve --bind 0.0.0.0:8080
Making Requests
Using X-Vultrino-Credential Header
The simplest method — specify the credential alias in a header:
curl -H "X-Vultrino-Credential: github-api" \
http://localhost:7878/https://api.github.com/user
The proxy will:
- Extract the credential alias from
X-Vultrino-Credential - Look up the credential in storage
- Strip the header
- Add the appropriate auth header (e.g.,
Authorization: Bearer ghp_xxx) - Forward to
https://api.github.com/user
With API Key Authentication
When using RBAC, include your Vultrino API key:
curl -H "Authorization: Bearer vk_your_api_key" \
-H "X-Vultrino-Credential: github-api" \
http://localhost:7878/https://api.github.com/user
URL Formats
Full URL in Path
http://localhost:7878/https://api.github.com/user
http://localhost:7878/https://api.stripe.com/v1/charges
Standard HTTP Proxy
Configure your HTTP client to use Vultrino as a proxy:
export HTTP_PROXY=http://localhost:7878
export HTTPS_PROXY=http://localhost:7878
curl -H "X-Vultrino-Credential: github-api" \
https://api.github.com/user
Authentication Injection
Vultrino automatically formats authentication based on credential type:
API Key
For api_key credentials, injects as Bearer token:
Authorization: Bearer <key>
Basic Auth
For basic_auth credentials, injects Base64-encoded credentials:
Authorization: Basic <base64(username:password)>
Custom Header
Some APIs expect the key in a custom header. Configure in the credential metadata:
vultrino add --alias custom-api --key xxx \
--metadata '{"header_name": "X-API-Key"}'
Results in:
X-API-Key: xxx
Request Flow
-
Receive Request
- Parse target URL from path or proxy request
- Extract
X-Vultrino-Credentialheader - Extract
Authorizationheader (if RBAC enabled)
-
Authenticate
- Validate API key (if provided)
- Check role permissions
- Verify credential scope access
-
Policy Check
- Evaluate policies for the credential
- Check URL patterns, methods, rate limits
- Deny if any policy fails
-
Credential Injection
- Decrypt credential from storage
- Format appropriate auth header
- Remove Vultrino-specific headers
-
Forward Request
- Send to target URL
- Stream response back to client
- Log audit entry
Configuration
[server]
bind = "127.0.0.1:7878"
mode = "local" # "local" or "server"
timeout = 30 # Request timeout in seconds
max_body_size = 10485760 # 10MB max request body
[proxy]
strip_headers = [ # Headers to remove before forwarding
"X-Vultrino-Credential",
"X-Vultrino-Policy",
]
inject_headers = { # Headers to add to all requests
"X-Forwarded-By" = "vultrino"
}
Error Responses
400 Bad Request
Missing credential header:
{
"error": "missing_credential",
"message": "X-Vultrino-Credential header is required"
}
401 Unauthorized
Invalid or expired API key:
{
"error": "unauthorized",
"message": "Invalid or expired API key"
}
403 Forbidden
Permission denied by RBAC or policy:
{
"error": "forbidden",
"message": "Permission denied for credential 'github-api'"
}
404 Not Found
Credential not found:
{
"error": "not_found",
"message": "Credential 'unknown-api' not found"
}
502 Bad Gateway
Target server error:
{
"error": "upstream_error",
"message": "Failed to connect to target server"
}
Language Examples
curl
curl -H "X-Vultrino-Credential: github-api" \
http://localhost:7878/https://api.github.com/user
Python
import requests
response = requests.get(
"http://localhost:7878/https://api.github.com/user",
headers={"X-Vultrino-Credential": "github-api"}
)
print(response.json())
JavaScript (Node.js)
const response = await fetch(
"http://localhost:7878/https://api.github.com/user",
{
headers: {
"X-Vultrino-Credential": "github-api"
}
}
);
const data = await response.json();
Go
req, _ := http.NewRequest("GET",
"http://localhost:7878/https://api.github.com/user", nil)
req.Header.Set("X-Vultrino-Credential", "github-api")
client := &http.Client{}
resp, _ := client.Do(req)
Rust
#![allow(unused)]
fn main() {
let client = reqwest::Client::new();
let response = client
.get("http://localhost:7878/https://api.github.com/user")
.header("X-Vultrino-Credential", "github-api")
.send()
.await?;
}
Performance
Connection Pooling
The proxy maintains connection pools to frequently accessed backends, reducing latency for repeated requests.
Streaming
Large responses are streamed directly to the client without buffering the entire body in memory.
Caching
The proxy does not cache responses. Implement caching in your application or use a dedicated cache layer.
Security
Network Binding
Always bind to localhost in production. Use a reverse proxy (nginx, Caddy) for external access.
TLS
The proxy can make requests to HTTPS endpoints. For production, also put the proxy itself behind TLS.
Audit Logging
All requests are logged with:
- Timestamp
- Credential alias used
- Target URL
- HTTP method
- Source IP
- Response status
Audit logs never include actual credential values.
MCP Server
The Model Context Protocol (MCP) server allows AI agents to make authenticated API requests without accessing the actual credentials.
Overview
MCP is a protocol for AI assistants to interact with external tools and services. Vultrino’s MCP server provides tools for:
- Listing available credentials (by alias only)
- Making authenticated HTTP requests
- Managing credentials (with proper permissions)
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ AI Agent │────▶│ Vultrino MCP │────▶│ External │
│ (Claude, etc.) │ │ Server │ │ APIs │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│ │
│ "Use github-api │ Credential never
│ to fetch user" │ exposed to agent
│ │
Starting the MCP Server
export VULTRINO_PASSWORD="your-password"
vultrino serve --mcp
The MCP server uses stdio transport, communicating via stdin/stdout.
Configuring AI Clients
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"vultrino": {
"command": "/path/to/vultrino",
"args": ["serve", "--mcp"],
"env": {
"VULTRINO_PASSWORD": "your-password"
}
}
}
}
Claude Code (CLI)
Add to your MCP configuration:
{
"mcpServers": {
"vultrino": {
"command": "vultrino",
"args": ["serve", "--mcp"],
"env": {
"VULTRINO_PASSWORD": "your-password"
}
}
}
}
Generic MCP Client
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "vultrino",
args: ["serve", "--mcp"],
env: {
VULTRINO_PASSWORD: "your-password"
}
});
const client = new Client({
name: "my-ai-app",
version: "1.0.0"
});
await client.connect(transport);
Available Tools
list_credentials
List all available credential aliases.
Input: None
Output:
{
"credentials": [
{
"alias": "github-api",
"type": "api_key",
"description": "GitHub personal access token"
},
{
"alias": "stripe-api",
"type": "api_key",
"description": "Stripe API key"
}
]
}
Example prompt:
“What credentials are available?”
http_request
Make an authenticated HTTP request.
Input:
{
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user",
"headers": {
"Accept": "application/json"
},
"body": null
}
Output:
{
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"login\": \"username\", ...}"
}
Example prompts:
“Use github-api to get my user profile” “Make a POST request to Stripe to create a customer using stripe-api”
add_credential
Add a new credential (requires write permission).
Input:
{
"alias": "new-api",
"type": "api_key",
"key": "secret-key-value",
"description": "Optional description"
}
Output:
{
"success": true,
"id": "550e8400-e29b-41d4-a716-446655440000"
}
delete_credential
Delete a credential (requires delete permission).
Input:
{
"alias": "old-api"
}
Output:
{
"success": true
}
Security Model
Credential Isolation
The MCP server never exposes actual credential values to the AI agent. The agent only sees:
- Credential aliases
- Credential types
- Descriptions
Permission Checks
If RBAC is enabled, the MCP server checks:
- API key validity (from session or configuration)
- Role permissions (read, execute, write, delete)
- Credential scope restrictions
Audit Trail
All MCP tool calls are logged:
2024-01-15T10:30:00Z MCP http_request credential=github-api url=https://api.github.com/user
Tool Descriptions
The MCP server provides rich tool descriptions to help AI agents understand capabilities:
{
"name": "http_request",
"description": "Make an authenticated HTTP request using a stored credential. The credential's actual value is never exposed - only the alias is needed.",
"inputSchema": {
"type": "object",
"properties": {
"credential": {
"type": "string",
"description": "Alias of the credential to use for authentication"
},
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "PATCH", "DELETE"],
"description": "HTTP method"
},
"url": {
"type": "string",
"description": "Target URL"
},
"headers": {
"type": "object",
"description": "Additional headers to include"
},
"body": {
"type": "string",
"description": "Request body (for POST, PUT, PATCH)"
}
},
"required": ["credential", "method", "url"]
}
}
Example Conversations
Listing and Using Credentials
User: “What API credentials do I have available?”
AI Agent: calls list_credentials tool
AI Agent: “You have the following credentials available:
github-api- GitHub personal access tokenstripe-api- Stripe API key“
User: “Get my GitHub profile”
AI Agent: calls http_request with credential=github-api
AI Agent: “Your GitHub profile shows you’re logged in as ‘username’ with 50 public repos…”
Making Authenticated Requests
User: “Create a new Stripe customer with email test@example.com”
AI Agent: calls http_request tool
{
"credential": "stripe-api",
"method": "POST",
"url": "https://api.stripe.com/v1/customers",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "email=test@example.com"
}
AI Agent: “I’ve created a new Stripe customer. The customer ID is cus_xxx…”
Troubleshooting
MCP server not starting
- Verify
VULTRINO_PASSWORDis set - Check credentials file exists (
vultrino listshould work) - Look for error messages in stderr
Tool not found
- Ensure you’re using the latest Vultrino version
- Verify MCP server started successfully
- Check client configuration
Permission denied
- Verify the API key (if RBAC enabled) has execute permission
- Check credential scope restrictions
- Review audit logs for denial reasons
Connection timeout
- MCP uses stdio transport; ensure no other process is consuming stdin
- Check that Vultrino binary path is correct
- Verify environment variables are passed correctly
Best Practices
For AI Developers
- Don’t ask for credentials — Always use aliases, never actual secrets
- Use descriptive aliases — Help the AI understand what each credential is for
- Set up RBAC — Create restricted roles for AI agent access
- Review audit logs — Monitor what requests agents are making
For System Administrators
- Use short-lived credentials — Rotate frequently
- Scope credentials narrowly — Each credential should do one thing
- Enable audit logging — Track all credential usage
- Review agent behavior — Periodically check what the AI is doing
Managing Credentials
This guide covers how to securely store, organize, and use credentials with Vultrino.
Credential Types
Vultrino supports several credential types:
| Type | Use Case | Auth Header Format |
|---|---|---|
api_key | API tokens, bearer tokens | Authorization: Bearer <key> |
basic_auth | Username/password auth | Authorization: Basic <base64> |
oauth2 | OAuth2 flows | Authorization: Bearer <access_token> |
private_key | SSH keys, signing keys | (used for signing, not HTTP) |
Adding Credentials
API Keys
Most common type for SaaS APIs:
# GitHub Personal Access Token
vultrino add --alias github-api --key ghp_xxxxxxxxxxxx
# Stripe API Key
vultrino add --alias stripe-api --key sk_live_xxxxxxxxxxxx
# OpenAI API Key
vultrino add --alias openai --key sk-xxxxxxxxxxxx
# With description
vultrino add --alias anthropic --key sk-ant-xxxx \
--description "Claude API key for production"
Basic Auth
For APIs using username/password:
vultrino add --alias jira-api --type basic_auth \
--username user@company.com \
--password api_token_here
vultrino add --alias jenkins --type basic_auth \
--username admin \
--password jenkins_token
OAuth2 Credentials
For OAuth2 flows with refresh tokens:
vultrino add --alias google-api --type oauth2 \
--client-id "xxx.apps.googleusercontent.com" \
--client-secret "GOCSPX-xxx" \
--refresh-token "1//xxx"
Organizing Credentials
Naming Conventions
Use a consistent naming scheme:
<provider>-<environment>-<scope>
Examples:
github-prod-readonlystripe-test-chargesaws-staging-s3
Using Prefixes
Group related credentials with prefixes:
# All GitHub credentials
vultrino add --alias github-api-readonly --key ghp_read...
vultrino add --alias github-api-admin --key ghp_admin...
# All Stripe credentials
vultrino add --alias stripe-live --key sk_live...
vultrino add --alias stripe-test --key sk_test...
Then restrict access with role scopes:
vultrino role create github-readonly --scopes "github-*" --permissions read,execute
Listing Credentials
Basic List
vultrino list
# Output:
# ID Alias Type Created
# 550e8400-e29b-41d4-a716-446655440000 github-api api_key 2024-01-15
# 6ba7b810-9dad-11d1-80b4-00c04fd430c8 stripe-live api_key 2024-01-16
JSON Output
vultrino list --json | jq '.[] | select(.type == "api_key")'
Via Web UI
Navigate to /credentials in the web interface for a visual list.
Using Credentials
CLI Request
vultrino request -c github-api https://api.github.com/user
HTTP Proxy
curl -H "X-Vultrino-Credential: github-api" \
http://localhost:7878/https://api.github.com/user
MCP (AI Agents)
The AI agent uses the credential alias:
AI: "Using github-api to fetch your profile..."
*Makes request without seeing actual token*
Updating Credentials
Currently, to update a credential:
-
Delete the old credential:
vultrino delete github-api -
Add the new one:
vultrino add --alias github-api --key ghp_newtoken
Future versions will support in-place updates.
Deleting Credentials
CLI
vultrino delete old-api-key
# Confirm deletion? [y/N] y
Force delete without confirmation:
vultrino delete old-api-key --force
Web UI
Click the delete button on the credentials list, then confirm.
Security Best Practices
1. Use Descriptive Names
Bad:
vultrino add --alias key1 --key xxx
Good:
vultrino add --alias github-ci-readonly --key xxx \
--description "Read-only token for CI pipeline"
2. Scope Credentials Narrowly
Instead of one admin key:
vultrino add --alias github-admin --key ghp_admin_all_perms
Create scoped credentials:
vultrino add --alias github-repos-read --key ghp_repos_read
vultrino add --alias github-actions-write --key ghp_actions_write
3. Set Expiration Reminders
Track when credentials expire:
vultrino add --alias stripe-api --key sk_live_xxx \
--description "Expires: 2024-12-31"
4. Rotate Regularly
Set up a rotation schedule:
- Generate new credential at source (GitHub, Stripe, etc.)
- Add to Vultrino with new alias
- Test the new credential
- Update applications to use new alias
- Delete old credential
5. Audit Usage
Check what’s using each credential:
# View audit logs
tail -f /var/log/vultrino/audit.log | grep github-api
Backup and Recovery
Backup
The credentials file is encrypted. Back it up securely:
cp ~/.vultrino/credentials.enc ~/backup/vultrino-$(date +%Y%m%d).enc
Recovery
To restore:
cp ~/backup/vultrino-20240115.enc ~/.vultrino/credentials.enc
You’ll need the same VULTRINO_PASSWORD used when the backup was created.
Export (Not Recommended)
Vultrino intentionally doesn’t support exporting credentials in plaintext. This is a security feature, not a limitation.
Troubleshooting
“Credential not found”
- Check the alias is spelled correctly
- Verify with
vultrino list - Credential may have been deleted
“Permission denied”
- Your API key may not have access to this credential
- Check role scopes:
vultrino role list
“Decryption error”
- Wrong
VULTRINO_PASSWORD - Corrupted credentials file
- Restore from backup if needed
“Invalid credential format”
- Check credential type matches the data
- API keys shouldn’t have usernames
- Basic auth requires both username and password
Roles & API Keys
Vultrino’s Role-Based Access Control (RBAC) system lets you create scoped API keys for different applications, each with specific permissions.
Overview
┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐
│ API Key │────▶│ Role │────▶│ Permissions │
│ vk_xxx │ │ executor │ │ + Credential Scopes│
└─────────────┘ └─────────────┘ └─────────────────────┘
- API Keys — Authenticate applications
- Roles — Define permissions and scopes
- Permissions — What actions are allowed
- Scopes — Which credentials are accessible
Permissions
| Permission | Description |
|---|---|
read | List credentials (metadata only, never secrets) |
write | Create new credentials |
update | Modify existing credentials |
delete | Remove credentials |
execute | Use credentials for authenticated requests |
Creating Roles
Basic Role
# Read-only role (can only list credentials)
vultrino role create readonly --permissions read
# Execute-only role (can use credentials but not manage them)
vultrino role create executor --permissions execute
# Full management role
vultrino role create admin --permissions read,write,update,delete,execute
Scoped Roles
Limit which credentials a role can access using glob patterns:
# Only GitHub credentials
vultrino role create github-user \
--permissions read,execute \
--scopes "github-*"
# Only test credentials (no production)
vultrino role create test-executor \
--permissions execute \
--scopes "*-test,*-staging"
# Multiple specific patterns
vultrino role create payment-processor \
--permissions execute \
--scopes "stripe-*,paypal-*,braintree-*"
With Description
vultrino role create ci-pipeline \
--permissions read,execute \
--scopes "github-ci-*" \
--description "Used by CI/CD pipeline for deployments"
Managing Roles
List Roles
vultrino role list
# Output:
# Name Permissions Scopes
# readonly read (all)
# executor execute (all)
# github-user read,execute github-*
View Role Details
vultrino role get github-user
# Output:
# Name: github-user
# Permissions: read, execute
# Scopes: github-*
# Created: 2024-01-15T10:30:00Z
Delete Role
vultrino role delete old-role
Note: Deleting a role doesn’t delete associated API keys, but those keys will no longer work.
Creating API Keys
Basic Key
vultrino key create my-app --role executor
# Output:
# Created API key: vk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
#
# IMPORTANT: Save this key now. It won't be shown again.
With Expiration
# Expires in 30 days
vultrino key create temp-access --role readonly --expires 30d
# Expires in 1 year
vultrino key create annual-key --role executor --expires 1y
# Specific date (ISO format)
vultrino key create project-key --role github-user --expires 2024-12-31
Managing API Keys
List Keys
vultrino key list
# Output:
# Prefix Name Role Expires Last Used
# vk_a1b2... my-app executor never 2024-01-15
# vk_x9y8... temp-access readonly 2024-02-14 2024-01-16
Revoke Key
vultrino key revoke vk_a1b2c3d4
# Revoked API key: vk_a1b2c3d4
Revocation is immediate. Any requests using the key will fail.
Using API Keys
HTTP Proxy
Include the API key in the Authorization header:
curl -H "Authorization: Bearer vk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "X-Vultrino-Credential: github-api" \
http://localhost:7878/https://api.github.com/user
Application Configuration
Store the API key in your application’s environment:
# .env
VULTRINO_API_KEY=vk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
import os
import requests
api_key = os.environ["VULTRINO_API_KEY"]
response = requests.get(
"http://vultrino:7878/https://api.github.com/user",
headers={
"Authorization": f"Bearer {api_key}",
"X-Vultrino-Credential": "github-api"
}
)
Web UI
Creating Roles
- Navigate to Roles in the sidebar
- Click New Role
- Fill in:
- Name
- Description (optional)
- Select permissions
- Enter credential scopes (comma-separated)
- Click Create Role
Creating API Keys
- Navigate to API Keys in the sidebar
- Click New API Key
- Fill in:
- Name (for identification)
- Select a role
- Expiration date (optional)
- Click Create Key
- Copy the key immediately — it’s only shown once
Common Patterns
Multi-Environment Setup
# Production role (limited credentials)
vultrino role create prod-app \
--permissions execute \
--scopes "*-prod"
# Staging role (more credentials)
vultrino role create staging-app \
--permissions read,execute \
--scopes "*-staging,*-test"
# Development role (everything except prod)
vultrino role create dev-app \
--permissions read,write,execute \
--scopes "*-dev,*-staging,*-test"
Per-Service Keys
# Role for payment processing
vultrino role create payment-service \
--permissions execute \
--scopes "stripe-*,paypal-*"
# Create key for the payment service
vultrino key create payment-service-prod --role payment-service
# Role for email service
vultrino role create email-service \
--permissions execute \
--scopes "sendgrid-*,mailgun-*"
# Create key for the email service
vultrino key create email-service-prod --role email-service
CI/CD Pipeline
# Read-only for listing credentials in CI
vultrino role create ci-readonly \
--permissions read \
--scopes "*"
# Execute for deployments
vultrino role create ci-deploy \
--permissions execute \
--scopes "aws-deploy-*,github-ci-*"
# Short-lived keys for CI
vultrino key create ci-read-key --role ci-readonly --expires 7d
vultrino key create ci-deploy-key --role ci-deploy --expires 7d
AI Agent Access
# Limited role for AI agents
vultrino role create ai-agent \
--permissions read,execute \
--scopes "github-api,stripe-test" # Only specific credentials
# Create key for the AI
vultrino key create claude-agent --role ai-agent
Security Best Practices
1. Principle of Least Privilege
Only grant the minimum permissions needed:
# Bad: Full admin access
vultrino role create my-app --permissions read,write,update,delete,execute
# Good: Only what's needed
vultrino role create my-app --permissions execute --scopes "api-needed-*"
2. Use Scopes
Always scope credentials when possible:
# Bad: Access to all credentials
vultrino role create service-role --permissions execute
# Good: Scoped to specific credentials
vultrino role create service-role --permissions execute --scopes "service-*"
3. Set Expiration
Use expiring keys for temporary access:
# Contractor access
vultrino key create contractor-key --role readonly --expires 90d
# CI pipeline (rotate weekly)
vultrino key create ci-key --role ci-deploy --expires 7d
4. Audit Key Usage
Check the audit log for unusual activity:
grep "vk_a1b2" /var/log/vultrino/audit.log
5. Rotate Keys Regularly
Even for long-lived applications, rotate keys periodically:
- Create new key
- Update application configuration
- Verify new key works
- Revoke old key
Troubleshooting
“Permission denied”
- Check the role has the required permission
- Verify the credential matches the role’s scopes
- Ensure the API key hasn’t expired
“API key not found”
- The key may have been revoked
- Check for typos in the key
- Verify the key was created successfully
“Role not found”
- The role may have been deleted
- Keys without valid roles won’t work
- Recreate the role or assign a new role to a new key
Using with AI Agents
Vultrino enables AI agents to make authenticated API calls without exposing credentials. This guide covers integration patterns and best practices.
Why Vultrino for AI Agents?
AI agents (Claude, GPT, etc.) need to interact with APIs, but:
- Credentials shouldn’t be in prompts or context
- Agents shouldn’t see actual secrets
- Usage should be auditable
- Access should be restricted and revocable
Vultrino solves this by:
- Storing credentials securely (encrypted at rest)
- Exposing only aliases to agents
- Injecting auth automatically
- Logging all usage
Integration Options
1. MCP Server (Recommended)
The Model Context Protocol provides native AI integration:
vultrino serve --mcp
Pros:
- Native protocol for AI tools
- Rich tool descriptions
- Session management
- Best security isolation
Setup: See MCP Server documentation for configuration.
2. HTTP Proxy
For agents that can make HTTP requests:
vultrino serve # Start proxy on :7878
The agent makes requests like:
GET http://vultrino:7878/https://api.github.com/user
X-Vultrino-Credential: github-api
Pros:
- Works with any HTTP-capable agent
- Simple integration
- Language agnostic
3. CLI Tool Calls
For agents that can execute shell commands:
vultrino request -c github-api https://api.github.com/user
Pros:
- Simplest integration
- Works with function calling
- Easy to audit
Setting Up for AI Agents
Step 1: Store Credentials
# Add credentials the AI will need
vultrino add --alias github-api --key ghp_xxx \
--description "GitHub API for repo access"
vultrino add --alias stripe-test --key sk_test_xxx \
--description "Stripe test mode API"
Step 2: Create Restricted Role
# Create a role with limited access
vultrino role create ai-agent \
--permissions read,execute \
--scopes "github-api,stripe-test" \
--description "Limited access for AI agent"
Step 3: Create API Key
vultrino key create claude-key --role ai-agent --expires 30d
# Save the generated key
Step 4: Configure AI Client
For Claude Desktop MCP:
{
"mcpServers": {
"vultrino": {
"command": "/path/to/vultrino",
"args": ["serve", "--mcp"],
"env": {
"VULTRINO_PASSWORD": "your-password"
}
}
}
}
Example Interactions
Listing Available Credentials
User: “What APIs can you access?”
AI: calls list_credentials
AI: “I have access to:
github-api— GitHub API for repo accessstripe-test— Stripe test mode API“
Making Requests
User: “Check my GitHub notifications”
AI: calls http_request with credential=github-api
{
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/notifications"
}
AI: “You have 3 unread notifications…”
Handling Errors
User: “Access my AWS account”
AI: calls http_request with credential=aws-prod
AI: “I don’t have access to AWS credentials. I can only access:
- github-api
- stripe-test
Would you like to add AWS credentials?“
Security Best Practices
1. Scope Credentials Narrowly
Don’t give AI access to admin credentials:
# Bad: Full admin access
vultrino add --alias github-admin --key ghp_admin_everything
# Good: Read-only access
vultrino add --alias github-readonly --key ghp_read_only_token
2. Use Test/Sandbox Credentials
For AI experimentation, use test mode:
vultrino add --alias stripe-test --key sk_test_xxx
# Not: stripe-live with sk_live_xxx
3. Set Key Expiration
Short-lived keys limit damage if compromised:
vultrino key create ai-key --role ai-agent --expires 7d
4. Monitor Usage
Check what the AI is doing:
# Watch audit log
tail -f /var/log/vultrino/audit.log
# Filter by credential
grep "github-api" /var/log/vultrino/audit.log
5. Restrict Scopes
Only allow access to specific credentials:
vultrino role create ai-readonly \
--permissions read,execute \
--scopes "github-readonly,stripe-test"
Common Patterns
Read-Only Research Agent
# Create read-only credential
vultrino add --alias github-public --key ghp_public_readonly
# Create restricted role
vultrino role create research-agent \
--permissions read,execute \
--scopes "github-public"
# Create key
vultrino key create research-key --role research-agent
Multi-Service Agent
# Add multiple credentials
vultrino add --alias github-api --key ghp_xxx
vultrino add --alias linear-api --key lin_xxx
vultrino add --alias notion-api --key secret_xxx
# Role with access to all
vultrino role create project-agent \
--permissions read,execute \
--scopes "github-api,linear-api,notion-api"
Development vs Production
# Dev credentials
vultrino add --alias stripe-dev --key sk_test_xxx
vultrino add --alias github-dev --key ghp_dev_xxx
# Dev-only role
vultrino role create ai-dev \
--permissions read,execute \
--scopes "*-dev,*-test"
# This role CANNOT access production credentials
Prompt Engineering Tips
Be Explicit About Available Credentials
System prompt:
You have access to these credentials through Vultrino:
- github-api: Read/write access to company repositories
- jira-api: Read access to project issues
- slack-api: Can post to #engineering channel
Always use these aliases when making API requests.
Never ask for or accept raw API keys.
Guide Credential Usage
When the user asks about GitHub:
1. Use github-api credential
2. Check available endpoints first
3. Prefer read operations over write
When uncertain, list available credentials first.
Handle Errors Gracefully
If a credential is not available or access is denied:
1. Inform the user what credentials you DO have access to
2. Suggest alternatives if possible
3. Never attempt to bypass Vultrino
Troubleshooting
Agent Can’t Find Credentials
- Verify credentials exist:
vultrino list - Check role scopes include the credential
- Ensure API key has read permission
Request Fails with 403
- Check role has execute permission
- Verify credential scope matches
- The underlying API may also be denying access
MCP Server Not Responding
- Ensure
VULTRINO_PASSWORDis set - Check Vultrino binary path is correct
- Review stderr for error messages
Audit Log Not Showing Requests
- Audit logging may be disabled
- Check config:
logging.audit_file - Verify file permissions
Monitoring AI Usage
Real-time Monitoring
# Watch all AI requests
tail -f /var/log/vultrino/audit.log | grep ai-agent-key
# Count requests per credential
awk '{print $4}' /var/log/vultrino/audit.log | sort | uniq -c
Usage Reports
Generate daily summaries:
# Requests per credential today
grep $(date +%Y-%m-%d) /var/log/vultrino/audit.log | \
awk '{print $4}' | sort | uniq -c | sort -rn
Alerting
Set up alerts for unusual activity:
- Sudden spike in requests
- Access to unexpected credentials
- Failed authentication attempts
Policy Configuration
Policies add fine-grained control over how credentials can be used, including URL restrictions, method limits, and rate limiting.
Overview
Policies are evaluated for every credential use:
Request → RBAC Check → Policy Check → Credential Injection → Forward
│
└─ Deny if policy fails
Policy Structure
Policies are defined in the configuration file:
[[policies]]
name = "github-readonly"
credential_pattern = "github-*"
default_action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.github.com/*" }
action = "allow"
[[policies.rules]]
condition = { method_match = ["GET", "HEAD"] }
action = "allow"
Configuration Fields
Policy Definition
| Field | Type | Description |
|---|---|---|
name | string | Unique policy name |
credential_pattern | string | Glob pattern for credentials this policy applies to |
default_action | string | Action when no rules match: allow, deny |
rules | array | List of policy rules |
Rule Definition
| Field | Type | Description |
|---|---|---|
condition | object | Condition to evaluate |
action | string | Action if condition matches: allow, deny |
Conditions
URL Match
Restrict to specific URLs or patterns:
# Exact match
condition = { url_match = "https://api.github.com/user" }
# Wildcard pattern
condition = { url_match = "https://api.github.com/repos/*" }
# Multiple paths
condition = { url_match = "https://api.github.com/{user,repos,gists}/*" }
Method Match
Restrict to specific HTTP methods:
# Single method
condition = { method_match = ["GET"] }
# Multiple methods
condition = { method_match = ["GET", "HEAD", "OPTIONS"] }
# All read operations
condition = { method_match = ["GET", "HEAD"] }
# Write operations
condition = { method_match = ["POST", "PUT", "PATCH", "DELETE"] }
Time Window
Restrict to specific hours:
# Business hours only (9 AM - 5 PM)
condition = { time_window = { start = "09:00", end = "17:00" } }
# Night shift (11 PM - 7 AM)
condition = { time_window = { start = "23:00", end = "07:00" } }
Rate Limit
Limit request frequency:
# 100 requests per minute
condition = { rate_limit = { max = 100, window_secs = 60 } }
# 1000 requests per hour
condition = { rate_limit = { max = 1000, window_secs = 3600 } }
# 10 requests per second (burst protection)
condition = { rate_limit = { max = 10, window_secs = 1 } }
Combined Conditions
Use and and or for complex logic:
# URL AND method match
condition = { and = [
{ url_match = "https://api.github.com/repos/*" },
{ method_match = ["GET"] }
]}
# Allow GET to anything OR POST to specific endpoint
condition = { or = [
{ method_match = ["GET"] },
{ and = [
{ method_match = ["POST"] },
{ url_match = "https://api.github.com/repos/*/issues" }
]}
]}
Complete Examples
Read-Only API Access
[[policies]]
name = "github-readonly"
credential_pattern = "github-readonly-*"
default_action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.github.com/*" }
action = "allow"
[[policies.rules]]
condition = { method_match = ["POST", "PUT", "PATCH", "DELETE"] }
action = "deny"
Rate-Limited Production Access
[[policies]]
name = "stripe-production"
credential_pattern = "stripe-live-*"
default_action = "deny"
# Allow only Stripe API
[[policies.rules]]
condition = { url_match = "https://api.stripe.com/*" }
action = "allow"
# Rate limit to prevent abuse
[[policies.rules]]
condition = { rate_limit = { max = 100, window_secs = 60 } }
action = "allow"
Business Hours Only
[[policies]]
name = "sensitive-data-access"
credential_pattern = "database-*"
default_action = "deny"
# Only during business hours
[[policies.rules]]
condition = { time_window = { start = "09:00", end = "18:00" } }
action = "allow"
Multi-Service Policy
[[policies]]
name = "payment-processing"
credential_pattern = "payment-*"
default_action = "deny"
# Allow Stripe
[[policies.rules]]
condition = { url_match = "https://api.stripe.com/*" }
action = "allow"
# Allow PayPal
[[policies.rules]]
condition = { url_match = "https://api.paypal.com/*" }
action = "allow"
# Allow Braintree
[[policies.rules]]
condition = { url_match = "https://api.braintreegateway.com/*" }
action = "allow"
# Block everything else by default
AI Agent Restrictions
[[policies]]
name = "ai-agent-safety"
credential_pattern = "ai-*"
default_action = "deny"
# Only read operations
[[policies.rules]]
condition = { method_match = ["GET", "HEAD"] }
action = "allow"
# Allow POST only to specific safe endpoints
[[policies.rules]]
condition = { and = [
{ method_match = ["POST"] },
{ or = [
{ url_match = "https://api.github.com/repos/*/issues" },
{ url_match = "https://api.github.com/repos/*/comments" }
]}
]}
action = "allow"
# Rate limit all requests
[[policies.rules]]
condition = { rate_limit = { max = 60, window_secs = 60 } }
action = "allow"
# Block dangerous operations
[[policies.rules]]
condition = { url_match = "https://api.github.com/repos/*/delete" }
action = "deny"
Policy Evaluation Order
- RBAC check — Does the API key have permission?
- Credential scope — Is the credential in scope for this role?
- Policy match — Find policies matching the credential alias
- Rule evaluation — Evaluate rules in order
- Default action — Apply if no rules matched
Rules are evaluated in order. First matching rule determines the action.
Debugging Policies
Verbose Logging
Enable debug logging to see policy evaluation:
RUST_LOG=vultrino=debug vultrino serve
Output:
DEBUG vultrino::policy: Evaluating policy "github-readonly" for credential "github-api"
DEBUG vultrino::policy: Rule 1 url_match: matched
DEBUG vultrino::policy: Rule 2 method_match: GET in [GET, HEAD] = true
DEBUG vultrino::policy: Result: allow
Test Policies
Test a policy without making real requests:
vultrino policy test --credential github-api \
--url "https://api.github.com/user" \
--method GET
# Result: allow (matched rule 1: url_match)
Audit Log
Check why requests were denied:
grep "policy_denied" /var/log/vultrino/audit.log
Common Patterns
Deny by Default
Start restrictive, add specific allows:
[[policies]]
name = "strict-access"
credential_pattern = "*"
default_action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.company.com/*" }
action = "allow"
Allow by Default with Blocklist
Allow most things, block specific patterns:
[[policies]]
name = "open-access"
credential_pattern = "dev-*"
default_action = "allow"
# Block production endpoints
[[policies.rules]]
condition = { url_match = "https://api.company.com/admin/*" }
action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.company.com/billing/*" }
action = "deny"
Environment Separation
Different policies per environment:
# Production: strict
[[policies]]
name = "production"
credential_pattern = "*-prod"
default_action = "deny"
[[policies.rules]]
condition = { url_match = "https://api.production.com/*" }
action = "allow"
# Development: permissive
[[policies]]
name = "development"
credential_pattern = "*-dev"
default_action = "allow"
Best Practices
1. Start Restrictive
Default to deny and add specific allows:
default_action = "deny"
2. Use Specific URL Patterns
# Bad: too broad
condition = { url_match = "*" }
# Good: specific
condition = { url_match = "https://api.github.com/repos/myorg/*" }
3. Combine with RBAC
Policies complement RBAC, not replace it:
- RBAC: Who can access which credentials
- Policies: How credentials can be used
4. Document Policies
Use clear names and comments:
[[policies]]
# SECURITY: Prevents AI agents from deleting repositories
name = "ai-no-destructive"
credential_pattern = "ai-*"
5. Test Before Deploying
Use the policy test command to verify behavior before applying in production.
Plugin System
Vultrino’s plugin system enables extending functionality with new credential types, actions, and MCP tools. Plugins are distributed as WebAssembly (WASM) modules that run in a sandboxed environment.
Architecture
PluginRegistry
│
├── HttpPlugin (built-in)
│
└── WasmPlugin (from installed plugins)
│
├── PluginManifest (parsed from plugin.toml)
│
└── WasmRuntime (wasmtime)
What Plugins Can Do
- Define new credential types — Store custom data like PGP keys, SSH certificates, or OAuth tokens
- Provide custom actions — Execute plugin-specific operations like signing or encryption
- Register MCP tools — Expose new tools to AI agents via the MCP protocol
Plugin Directory Structure
Plugins are installed to ~/.vultrino/plugins/:
~/.vultrino/
├── credentials.enc
└── plugins/
└── pgp-signing/
├── plugin.toml # Manifest
├── plugin.wasm # WASM module
└── .installed.json # Installation metadata
Plugin Manifest
Each plugin requires a plugin.toml manifest:
[plugin]
name = "pgp-signing"
version = "1.0.0"
description = "PGP/GPG signing and verification"
author = "Your Name"
format = "wasm"
wasm_module = "pgp_signing.wasm"
[[credential_types]]
name = "pgp_key"
display_name = "PGP/GPG Key"
[[credential_types.fields]]
name = "private_key"
label = "Private Key"
type = "textarea"
required = true
secret = true
[[actions]]
name = "sign"
description = "Sign data with PGP"
[[mcp_tools]]
name = "pgp_sign"
action = "sign"
description = "Sign data with PGP"
Available Plugins
| Plugin | Description | Credential Types |
|---|---|---|
| PGP Signing | PGP/GPG signing and verification | pgp_key |
Next Steps
- Installing Plugins — How to install and manage plugins
- Developing Plugins — Create your own plugins
- PGP Plugin — Use the PGP signing plugin
Installing Plugins
Vultrino plugins can be installed from local paths, git repositories, or archive URLs.
Installation Sources
From Local Path
Install a plugin from a local directory:
vultrino plugin install ./my-plugin
vultrino plugin install /absolute/path/to/plugin
vultrino plugin install ~/plugins/my-plugin
From Git Repository
Install directly from GitHub, GitLab, or other git hosts:
# Latest commit
vultrino plugin install https://github.com/user/vultrino-plugin
# Specific tag or branch
vultrino plugin install https://github.com/user/vultrino-plugin#v1.0.0
vultrino plugin install https://github.com/user/vultrino-plugin#main
From Archive URL
Install from a .tar.gz archive:
vultrino plugin install https://example.com/plugin-1.0.0.tar.gz
Build Process
When installing a plugin with a Cargo.toml, Vultrino automatically:
- Checks for the
wasm32-wasip1target (installs if needed) - Runs
cargo build --release --target wasm32-wasip1 - Copies the built WASM module to the plugin directory
Requirements:
- Rust toolchain installed
rustupavailable in PATH
Managing Plugins
List Installed Plugins
vultrino plugin list
Example output:
Installed plugins:
pgp-signing v1.0.0
Source: https://github.com/vultrino/plugin-pgp#v1.0.0
Installed: 2024-01-15
Credential types: pgp_key
MCP tools: pgp_sign, pgp_verify, pgp_get_public_key
View Plugin Details
vultrino plugin info pgp-signing
Remove a Plugin
vultrino plugin remove pgp-signing
Reload a Plugin
Reload a plugin’s WASM module without restarting:
vultrino plugin reload pgp-signing
Plugin Discovery
Plugins can be discovered in the Vultrino plugin registry (coming soon) or by searching GitHub for repositories tagged with vultrino-plugin.
Troubleshooting
Build Fails
If the WASM build fails:
- Ensure Rust is installed:
rustup --version - Check the target:
rustup target list --installed - Install the target manually:
rustup target add wasm32-wasip1
Plugin Not Loading
Check the plugin manifest is valid:
cd ~/.vultrino/plugins/my-plugin
cat plugin.toml
Verify the WASM module exists:
ls -la *.wasm
Hot Reload Not Working
Make sure the Vultrino server has write access to the plugins directory and that the new WASM module compiles successfully.
Developing Plugins
This guide covers creating custom Vultrino plugins that add new credential types, actions, and MCP tools.
Plugin Structure
A minimal plugin requires:
my-plugin/
├── Cargo.toml
├── plugin.toml
└── src/
└── lib.rs
Cargo.toml
Configure your crate for WASM compilation:
[package]
name = "my-plugin"
version = "1.0.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[profile.release]
opt-level = "s"
lto = true
Plugin Manifest (plugin.toml)
Define your plugin’s capabilities:
[plugin]
name = "my-plugin"
version = "1.0.0"
description = "My custom plugin"
author = "Your Name"
format = "wasm"
wasm_module = "my_plugin.wasm"
# Define custom credential types
[[credential_types]]
name = "my_credential"
display_name = "My Custom Credential"
[[credential_types.fields]]
name = "secret_value"
label = "Secret Value"
type = "password" # text, password, or textarea
required = true
secret = true
help_text = "Enter your secret value"
[[credential_types.fields]]
name = "api_endpoint"
label = "API Endpoint"
type = "text"
required = false
placeholder = "https://api.example.com"
# Define actions your plugin can perform
[[actions]]
name = "do_something"
description = "Perform an action with the credential"
[[actions.parameters]]
name = "input"
type = "string"
required = true
description = "Input data for the action"
# Expose as MCP tools
[[mcp_tools]]
name = "my_plugin_action"
action = "do_something"
description = "Perform my plugin action"
WASM ABI
Your plugin must export these functions:
vultrino_plugin_version
Return the ABI version (currently 1):
#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn vultrino_plugin_version() -> u32 {
1
}
}
vultrino_alloc
Allocate memory for the host to write data:
#![allow(unused)]
fn main() {
use std::alloc::{alloc, Layout};
#[no_mangle]
pub extern "C" fn vultrino_alloc(size: u32) -> *mut u8 {
if size == 0 {
return std::ptr::null_mut();
}
let layout = Layout::from_size_align(size as usize, 1).unwrap();
unsafe { alloc(layout) }
}
}
vultrino_free
Free memory allocated by the plugin:
#![allow(unused)]
fn main() {
use std::alloc::{dealloc, Layout};
#[no_mangle]
pub extern "C" fn vultrino_free(ptr: *mut u8, len: u32) {
if ptr.is_null() || len == 0 {
return;
}
let layout = Layout::from_size_align(len as usize, 1).unwrap();
unsafe { dealloc(ptr, layout) }
}
}
vultrino_execute
Execute an action. Takes JSON request, returns packed pointer/length to JSON response:
#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn vultrino_execute(request_ptr: *const u8, request_len: u32) -> u64 {
// Read request JSON
let request_str = read_string(request_ptr, request_len);
let request: ExecuteRequest = serde_json::from_str(&request_str).unwrap();
// Process action
let response = match request.action.as_str() {
"do_something" => handle_do_something(&request),
_ => error_response("Unknown action"),
};
// Return response
let json = serde_json::to_string(&response).unwrap();
let bytes = json.into_bytes();
let ptr = vultrino_alloc(bytes.len() as u32);
unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len()); }
// Pack pointer and length into u64
((ptr as u64) << 32) | (bytes.len() as u64)
}
}
vultrino_validate_params
Validate action parameters before execution:
#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn vultrino_validate_params(
action_ptr: *const u8,
action_len: u32,
params_ptr: *const u8,
params_len: u32,
) -> i32 {
// Return 0 for valid, negative for error
0
}
}
Request/Response Format
Execute Request
{
"action": "do_something",
"credential": {
"secret_value": "my-secret",
"api_endpoint": "https://api.example.com"
},
"parameters": {
"input": "some data"
}
}
Execute Response
{
"code": 0,
"data": "result string",
"error": null
}
Result codes:
0: Success-1: General error-2: Invalid action-3: Invalid parameters
Building
Build your plugin for WASM:
cargo build --release --target wasm32-wasip1
The output will be at target/wasm32-wasip1/release/my_plugin.wasm.
Testing Locally
- Copy or symlink your plugin to
~/.vultrino/plugins/my-plugin/ - Ensure
plugin.tomland the.wasmfile are present - Start Vultrino:
vultrino serve - The plugin should be loaded automatically
Best Practices
- Handle errors gracefully — Always return proper error responses
- Validate inputs — Check parameters before processing
- Keep secrets secure — Never log or expose credential data
- Optimize size — Use
opt-level = "s"and LTO for smaller WASM - Version carefully — Bump version when changing the manifest
PGP Signing Plugin
The PGP signing plugin enables Vultrino to store PGP private keys and perform cryptographic signing operations.
Installation
vultrino plugin install https://github.com/vultrino/plugin-pgp
Or build from source:
cd plugins/pgp-signing
cargo build --release --target wasm32-wasip1
vultrino plugin install ./plugins/pgp-signing
Credential Type: pgp_key
Store a PGP private key in Vultrino.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
private_key | textarea | Yes | ASCII-armored PGP private key |
passphrase | password | No | Passphrase to unlock the key |
key_id | text | No | Specific key ID to use |
Adding via CLI
vultrino add --alias my-pgp-key --type plugin:pgp-signing:pgp_key
# You will be prompted for the private key and passphrase
Adding via Web UI
- Navigate to Credentials > Add Credential
- Select “PGP/GPG Key (pgp-signing)” from the dropdown
- Paste your ASCII-armored private key
- Enter passphrase if the key is encrypted
- Click “Add Credential”
Available Actions
sign
Create a signature for arbitrary data. Returns a base64-encoded signature.
Parameters:
data(string, required) — Data to signarmor(boolean, optional) — Output armored format (default: true)
sign_cleartext
Create a PGP cleartext signed message. The message text is visible, with the signature appended.
Parameters:
message(string, required) — Message to sign
Example output:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Your message here
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE...
-----END PGP SIGNATURE-----
verify
Verify a cleartext signed message.
Parameters:
data(string, required) — Original data that was signedsignature(string, required) — The cleartext signed message
Returns: "true" or "false"
get_public_key
Extract the public key from the stored private key.
Parameters:
armor(boolean, optional) — Output armored format (default: true)
MCP Tools
When running with MCP enabled, these tools are available:
| Tool | Description |
|---|---|
pgp_sign | Sign data and return the signature |
pgp_sign_cleartext | Create a cleartext signed message |
pgp_verify | Verify a signature |
pgp_get_public_key | Get the public key |
Example MCP Usage
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "pgp_sign_cleartext",
"arguments": {
"credential": "my-pgp-key",
"message": "I agree to these terms."
}
}
}
Use Cases
Git Commit Signing
Use with AI agents to sign commits:
# Configure git to use Vultrino for signing
git config --global gpg.program vultrino-gpg-wrapper
Document Signing
Create verifiable signatures on documents:
vultrino request my-pgp-key --action sign_cleartext \
--param message="I approve this document"
Key Management
Securely store team PGP keys without exposing private key material:
- Store the private key in Vultrino
- Create API keys for team members
- Team members can request signatures without accessing the key
Security Considerations
- Private keys are encrypted at rest using AES-256-GCM
- Keys are only decrypted in memory during signing operations
- The WASM sandbox isolates plugin execution
- Audit logs track all signing operations
HTTP API Reference
Complete reference for Vultrino’s HTTP API endpoints.
Base URL
Default: http://127.0.0.1:7878
Authentication
API Key Authentication
Include the Vultrino API key in the Authorization header:
Authorization: Bearer vk_your_api_key_here
No Authentication (Local Mode)
In local mode without RBAC, no authentication is required.
Proxy Endpoints
Execute Proxied Request
Proxy a request through Vultrino with automatic credential injection.
URL Format:
{method} /{target_url}
Headers:
| Header | Required | Description |
|---|---|---|
X-Vultrino-Credential | Yes | Alias of credential to use |
Authorization | Depends | API key (if RBAC enabled) |
* | No | All other headers passed to target |
Example:
GET /https://api.github.com/user HTTP/1.1
Host: localhost:7878
X-Vultrino-Credential: github-api
Accept: application/json
Response: Returns the response from the target server, including:
- Status code
- Headers
- Body
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | missing_credential | X-Vultrino-Credential header not provided |
| 401 | unauthorized | Invalid or expired API key |
| 403 | forbidden | Permission denied by RBAC or policy |
| 404 | not_found | Credential alias not found |
| 502 | upstream_error | Failed to connect to target server |
Execute API
POST /v1/execute
Execute an action with a credential. More flexible than direct proxy.
Request:
POST /v1/execute HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json
{
"credential": "github-api",
"action": "http.request",
"params": {
"method": "GET",
"url": "https://api.github.com/user",
"headers": {
"Accept": "application/json"
},
"body": null
}
}
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
credential | string | Yes | Credential alias |
action | string | Yes | Action to perform |
params | object | Yes | Action-specific parameters |
Actions:
| Action | Description |
|---|---|
http.request | Make an HTTP request |
crypto.sign | Sign data (future) |
HTTP Request Params:
| Field | Type | Required | Description |
|---|---|---|---|
method | string | Yes | HTTP method |
url | string | Yes | Target URL |
headers | object | No | Additional headers |
body | string | No | Request body |
Response:
{
"status": 200,
"headers": {
"content-type": "application/json",
"x-ratelimit-remaining": "4999"
},
"body": "{\"login\":\"username\",...}"
}
Credential Management API
GET /v1/credentials
List all credentials (metadata only, no secrets).
Request:
GET /v1/credentials HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"credentials": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"alias": "github-api",
"type": "api_key",
"description": "GitHub personal access token",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
{
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"alias": "stripe-api",
"type": "api_key",
"description": null,
"created_at": "2024-01-16T14:20:00Z",
"updated_at": "2024-01-16T14:20:00Z"
}
]
}
Required Permission: read
GET /v1/credentials/
Get details about a specific credential.
Request:
GET /v1/credentials/github-api HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"alias": "github-api",
"type": "api_key",
"description": "GitHub personal access token",
"metadata": {},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
Required Permission: read
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | not_found | Credential not found |
POST /v1/credentials
Create a new credential.
Request:
POST /v1/credentials HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json
{
"alias": "new-api-key",
"type": "api_key",
"data": {
"key": "secret_key_value"
},
"description": "Description of this credential",
"metadata": {
"team": "backend"
}
}
Request Fields:
| Field | Type | Required | Description |
|---|---|---|---|
alias | string | Yes | Unique human-readable name |
type | string | Yes | Credential type |
data | object | Yes | Credential data (type-specific) |
description | string | No | Optional description |
metadata | object | No | Custom metadata |
Credential Types and Data:
api_key:
{
"type": "api_key",
"data": {
"key": "your_api_key"
}
}
basic_auth:
{
"type": "basic_auth",
"data": {
"username": "user",
"password": "pass"
}
}
oauth2:
{
"type": "oauth2",
"data": {
"client_id": "xxx",
"client_secret": "xxx",
"refresh_token": "xxx",
"access_token": "xxx",
"expires_at": "2024-02-15T10:30:00Z"
}
}
Response:
{
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"alias": "new-api-key"
}
Required Permission: write
Error Responses:
| Status | Code | Description |
|---|---|---|
| 400 | invalid_request | Invalid request body |
| 409 | conflict | Alias already exists |
DELETE /v1/credentials/
Delete a credential.
Request:
DELETE /v1/credentials/old-api HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"success": true
}
Required Permission: delete
Error Responses:
| Status | Code | Description |
|---|---|---|
| 404 | not_found | Credential not found |
Role Management API
GET /v1/roles
List all roles.
Request:
GET /v1/roles HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"roles": [
{
"id": "role-123",
"name": "executor",
"description": "Can execute requests",
"permissions": ["read", "execute"],
"credential_scopes": [],
"created_at": "2024-01-15T10:30:00Z"
}
]
}
POST /v1/roles
Create a new role.
Request:
POST /v1/roles HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json
{
"name": "github-reader",
"description": "Read-only GitHub access",
"permissions": ["read", "execute"],
"credential_scopes": ["github-*"]
}
Response:
{
"id": "role-456",
"name": "github-reader"
}
DELETE /v1/roles/
Delete a role.
Request:
DELETE /v1/roles/old-role HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"success": true
}
API Key Management
GET /v1/keys
List all API keys (shows prefix only, not full key).
Request:
GET /v1/keys HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"keys": [
{
"id": "key-123",
"name": "my-app",
"key_prefix": "vk_a1b2c3d4",
"role_id": "role-123",
"expires_at": null,
"last_used_at": "2024-01-16T14:20:00Z",
"created_at": "2024-01-15T10:30:00Z"
}
]
}
POST /v1/keys
Create a new API key.
Request:
POST /v1/keys HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Content-Type: application/json
{
"name": "new-app-key",
"role_id": "role-123",
"expires_at": "2024-12-31T23:59:59Z"
}
Response:
{
"id": "key-456",
"key": "vk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"name": "new-app-key"
}
Note: The full key is only returned once at creation. Store it securely.
DELETE /v1/keys/
Revoke an API key.
Request:
DELETE /v1/keys/key-123 HTTP/1.1
Host: localhost:7878
Authorization: Bearer vk_xxx
Response:
{
"success": true
}
Health Check
GET /health
Check if the server is running.
Request:
GET /health HTTP/1.1
Host: localhost:7878
Response:
{
"status": "ok",
"version": "0.1.0"
}
No authentication required.
Error Response Format
All errors follow this format:
{
"error": "error_code",
"message": "Human-readable error message",
"details": {}
}
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
invalid_request | 400 | Malformed request body or parameters |
missing_credential | 400 | X-Vultrino-Credential header missing |
unauthorized | 401 | Invalid or expired API key |
forbidden | 403 | Permission denied |
not_found | 404 | Resource not found |
conflict | 409 | Resource already exists |
rate_limited | 429 | Too many requests |
internal_error | 500 | Server error |
upstream_error | 502 | Target server error |
Rate Limits
Default rate limits (configurable):
| Endpoint | Limit |
|---|---|
| Proxy requests | 1000/minute |
| Credential management | 100/minute |
| Authentication | 10 failed/minute |
Rate limit headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1705330800
MCP Tools Reference
Complete reference for Vultrino’s Model Context Protocol (MCP) tools.
Overview
Vultrino exposes tools through MCP that allow AI agents to:
- List available credentials
- Make authenticated HTTP requests
- Manage credentials (with appropriate permissions)
Tool Definitions
list_credentials
List all credentials available to the current session.
Schema:
{
"name": "list_credentials",
"description": "List all available credential aliases. Returns metadata only, never actual secrets.",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
}
Input: None
Output:
{
"credentials": [
{
"alias": "github-api",
"type": "api_key",
"description": "GitHub personal access token"
},
{
"alias": "stripe-test",
"type": "api_key",
"description": "Stripe test mode API key"
}
]
}
Required Permission: read
Example Usage:
User: "What APIs can you access?"
Agent: [calls list_credentials]
Agent: "I have access to 2 credentials: github-api and stripe-test"
http_request
Make an authenticated HTTP request using a stored credential.
Schema:
{
"name": "http_request",
"description": "Make an authenticated HTTP request. The credential's actual value is never exposed - only the alias is needed. Vultrino automatically injects the appropriate authentication header.",
"inputSchema": {
"type": "object",
"properties": {
"credential": {
"type": "string",
"description": "Alias of the credential to use for authentication"
},
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
"description": "HTTP method"
},
"url": {
"type": "string",
"description": "Target URL (must be HTTPS for security)"
},
"headers": {
"type": "object",
"description": "Additional headers to include in the request",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "string",
"description": "Request body (for POST, PUT, PATCH requests)"
}
},
"required": ["credential", "method", "url"]
}
}
Input:
{
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user",
"headers": {
"Accept": "application/vnd.github.v3+json"
}
}
Output:
{
"status": 200,
"headers": {
"content-type": "application/json; charset=utf-8",
"x-ratelimit-limit": "5000",
"x-ratelimit-remaining": "4999"
},
"body": "{\"login\":\"username\",\"id\":12345,...}"
}
Required Permission: execute
Error Responses:
| Error | Description |
|---|---|
credential_not_found | The specified credential alias doesn’t exist |
permission_denied | No permission to use this credential |
policy_denied | Request blocked by policy rules |
upstream_error | Failed to connect to target server |
Example Usage:
User: "Get my GitHub profile"
Agent: [calls http_request with credential=github-api, url=https://api.github.com/user]
Agent: "Your GitHub profile shows you're logged in as 'username' with 42 public repos"
add_credential
Add a new credential to storage.
Schema:
{
"name": "add_credential",
"description": "Store a new credential. The credential will be encrypted at rest and only accessible by alias.",
"inputSchema": {
"type": "object",
"properties": {
"alias": {
"type": "string",
"description": "Unique human-readable name for this credential"
},
"type": {
"type": "string",
"enum": ["api_key", "basic_auth"],
"description": "Type of credential"
},
"key": {
"type": "string",
"description": "API key or token value (for api_key type)"
},
"username": {
"type": "string",
"description": "Username (for basic_auth type)"
},
"password": {
"type": "string",
"description": "Password (for basic_auth type)"
},
"description": {
"type": "string",
"description": "Optional description of what this credential is for"
}
},
"required": ["alias", "type"]
}
}
Input (API Key):
{
"alias": "new-service-api",
"type": "api_key",
"key": "sk_live_xxxxxxxxxxxx",
"description": "API key for new service"
}
Input (Basic Auth):
{
"alias": "jenkins-ci",
"type": "basic_auth",
"username": "admin",
"password": "token123",
"description": "Jenkins CI access"
}
Output:
{
"success": true,
"id": "550e8400-e29b-41d4-a716-446655440000",
"alias": "new-service-api"
}
Required Permission: write
Error Responses:
| Error | Description |
|---|---|
permission_denied | No write permission |
alias_exists | A credential with this alias already exists |
invalid_type | Unknown credential type |
delete_credential
Remove a credential from storage.
Schema:
{
"name": "delete_credential",
"description": "Delete a credential by its alias. This action cannot be undone.",
"inputSchema": {
"type": "object",
"properties": {
"alias": {
"type": "string",
"description": "Alias of the credential to delete"
}
},
"required": ["alias"]
}
}
Input:
{
"alias": "old-api-key"
}
Output:
{
"success": true
}
Required Permission: delete
Error Responses:
| Error | Description |
|---|---|
permission_denied | No delete permission |
not_found | Credential with this alias not found |
Permission Requirements
| Tool | Required Permission |
|---|---|
list_credentials | read |
http_request | execute |
add_credential | write |
delete_credential | delete |
Scope Restrictions
If the API key’s role has credential scopes, tools are further restricted:
list_credentials— Only shows credentials matching scope patternshttp_request— Only works with credentials matching scope patternsadd_credential— New credentials must match scope patternsdelete_credential— Can only delete credentials matching scope patterns
Error Format
All MCP tool errors follow this format:
{
"error": {
"code": "error_code",
"message": "Human-readable error message"
}
}
Usage Patterns
Basic API Call
{
"tool": "http_request",
"arguments": {
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user"
}
}
POST with JSON Body
{
"tool": "http_request",
"arguments": {
"credential": "stripe-api",
"method": "POST",
"url": "https://api.stripe.com/v1/customers",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "email=test@example.com&name=Test+User"
}
}
Check Available Credentials First
// Step 1: List what's available
{
"tool": "list_credentials",
"arguments": {}
}
// Step 2: Use a credential
{
"tool": "http_request",
"arguments": {
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/repos/owner/repo"
}
}
Best Practices for AI Agents
1. List First, Then Use
Always check available credentials before attempting to use one:
1. Call list_credentials
2. Verify the needed credential exists
3. Call http_request with the credential
2. Handle Errors Gracefully
When a credential isn’t available:
Agent: "I don't have access to AWS credentials. The credentials I can use are:
- github-api (GitHub API)
- stripe-test (Stripe test mode)
Would you like to add AWS credentials?"
3. Use Appropriate Methods
- GET — Fetch data
- POST — Create resources
- PUT — Replace resources
- PATCH — Update resources
- DELETE — Remove resources
4. Include Necessary Headers
Many APIs require specific headers:
{
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
}
}
5. Parse Response Bodies
The body field is a string. Parse it as appropriate:
- JSON APIs:
JSON.parse(response.body) - XML APIs: Parse as XML
- Plain text: Use directly
Security Notes
- Credentials are never exposed — The AI only sees aliases
- All requests are logged — Audit trail of all tool usage
- Policies are enforced — URL and method restrictions apply
- Rate limits apply — Prevent abuse
- Scopes restrict access — Roles limit which credentials are visible
LLM Reference
This section provides documentation optimized for Large Language Models (LLMs) to understand and use Vultrino effectively.
Quick Reference
What is Vultrino?
Vultrino is a credential proxy that allows applications (including AI agents) to make authenticated API requests without seeing the actual credentials.
Key Concept: You use credential aliases (like “github-api”), not actual secrets.
Available Paths
For programmatic access to documentation, these raw markdown paths are available:
| Content | Path |
|---|---|
| Full reference | /llm/full-reference.md |
| Quick start | /getting-started/quickstart.md |
| CLI commands | /components/cli.md |
| HTTP API | /api/http.md |
| MCP tools | /api/mcp-tools.md |
Condensed Reference
Making Authenticated Requests
Via HTTP Proxy:
curl -H "X-Vultrino-Credential: <alias>" \
http://localhost:7878/https://target-api.com/endpoint
Via MCP (AI Agents):
{
"tool": "http_request",
"arguments": {
"credential": "<alias>",
"method": "GET",
"url": "https://target-api.com/endpoint"
}
}
Via CLI:
vultrino request -c <alias> https://target-api.com/endpoint
MCP Tools Summary
| Tool | Purpose | Required Permission |
|---|---|---|
list_credentials | List available credentials | read |
http_request | Make authenticated request | execute |
add_credential | Store new credential | write |
delete_credential | Remove credential | delete |
Common Credential Aliases
Typical naming patterns:
github-api— GitHub API tokenstripe-live/stripe-test— Stripe API keysopenai— OpenAI API keyanthropic— Anthropic API keyaws-prod/aws-staging— AWS credentials
For AI Agents
Step 1: Check Available Credentials
{"tool": "list_credentials", "arguments": {}}
Response:
{
"credentials": [
{"alias": "github-api", "type": "api_key", "description": "..."},
{"alias": "stripe-test", "type": "api_key", "description": "..."}
]
}
Step 2: Make Request
{
"tool": "http_request",
"arguments": {
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user"
}
}
Response:
{
"status": 200,
"headers": {"content-type": "application/json"},
"body": "{\"login\":\"username\",...}"
}
Step 3: Parse Response
The body field is a JSON string. Parse it to access the data.
Error Handling
If a credential isn’t available:
- List what IS available
- Explain to user
- Offer alternatives
Example response to user:
“I don’t have access to AWS credentials. I can access: github-api, stripe-test. Would you like to add AWS credentials?”
HTTP API Quick Reference
Proxy Request
GET /https://api.example.com/endpoint
X-Vultrino-Credential: <alias>
Authorization: Bearer <vultrino-api-key> (if RBAC enabled)
List Credentials
GET /v1/credentials
Authorization: Bearer <vultrino-api-key>
Execute Action
POST /v1/execute
Authorization: Bearer <vultrino-api-key>
Content-Type: application/json
{"credential": "<alias>", "action": "http.request", "params": {...}}
Configuration Summary
Environment Variables
VULTRINO_PASSWORD— Storage encryption password (required)VULTRINO_CONFIG— Config file pathRUST_LOG— Log level
Default Ports
7878— HTTP proxy7879— Web UI
File Locations
~/.vultrino/credentials.enc— Encrypted credentials~/.vultrino/admin.json— Admin auth/etc/vultrino/config.toml— System config
Security Model
- Credentials encrypted at rest — AES-256-GCM
- Aliases only — Never expose actual secrets
- RBAC — Role-based access control
- Policies — URL/method restrictions
- Audit logging — Track all usage
Common Tasks
“List my credentials”
{"tool": "list_credentials", "arguments": {}}
“Get my GitHub user info”
{
"tool": "http_request",
"arguments": {
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user"
}
}
“Create a Stripe customer”
{
"tool": "http_request",
"arguments": {
"credential": "stripe-api",
"method": "POST",
"url": "https://api.stripe.com/v1/customers",
"headers": {"Content-Type": "application/x-www-form-urlencoded"},
"body": "email=user@example.com"
}
}
“List GitHub repos”
{
"tool": "http_request",
"arguments": {
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user/repos"
}
}
Vultrino Complete LLM Reference
This document contains everything an LLM needs to know to use Vultrino effectively.
What is Vultrino?
Vultrino is a credential proxy for the AI era. It allows AI agents and applications to make authenticated API requests without ever seeing the actual credentials.
Core Concept: You reference credentials by alias (e.g., “github-api”), and Vultrino automatically injects the real credential into your request.
MCP Tools
list_credentials
Lists all credentials you have access to.
Input: None required
Output:
{
"credentials": [
{
"alias": "github-api",
"type": "api_key",
"description": "GitHub personal access token"
}
]
}
Permission Required: read
http_request
Makes an authenticated HTTP request.
Input:
{
"credential": "string (required) - credential alias",
"method": "string (required) - GET|POST|PUT|PATCH|DELETE",
"url": "string (required) - target URL",
"headers": "object (optional) - additional headers",
"body": "string (optional) - request body"
}
Output:
{
"status": 200,
"headers": {"content-type": "application/json"},
"body": "string - response body"
}
Permission Required: execute
Example:
{
"credential": "github-api",
"method": "GET",
"url": "https://api.github.com/user"
}
add_credential
Stores a new credential.
Input:
{
"alias": "string (required) - unique name",
"type": "string (required) - api_key|basic_auth",
"key": "string (for api_key) - the secret value",
"username": "string (for basic_auth)",
"password": "string (for basic_auth)",
"description": "string (optional)"
}
Output:
{
"success": true,
"id": "uuid",
"alias": "string"
}
Permission Required: write
delete_credential
Removes a credential.
Input:
{
"alias": "string (required)"
}
Output:
{
"success": true
}
Permission Required: delete
HTTP API Endpoints
Proxy Request
{METHOD} /{target_url}
Headers:
X-Vultrino-Credential: {alias}
Authorization: Bearer {api_key} (if RBAC enabled)
List Credentials
GET /v1/credentials
Authorization: Bearer {api_key}
Get Credential
GET /v1/credentials/{alias}
Authorization: Bearer {api_key}
Create Credential
POST /v1/credentials
Authorization: Bearer {api_key}
Content-Type: application/json
{
"alias": "string",
"type": "api_key|basic_auth",
"data": { ... },
"description": "string"
}
Delete Credential
DELETE /v1/credentials/{alias}
Authorization: Bearer {api_key}
Execute Action
POST /v1/execute
Authorization: Bearer {api_key}
Content-Type: application/json
{
"credential": "alias",
"action": "http.request",
"params": {
"method": "GET",
"url": "https://...",
"headers": {},
"body": null
}
}
CLI Commands
# Initialize
vultrino init
# Add credential
vultrino add --alias NAME --key SECRET
# List credentials
vultrino list
# Make request
vultrino request -c ALIAS URL
# Start proxy
vultrino serve
# Start web UI
vultrino web
# Start MCP server
vultrino serve --mcp
# Manage roles
vultrino role create NAME --permissions read,execute
vultrino role list
vultrino role delete NAME
# Manage API keys
vultrino key create NAME --role ROLE
vultrino key list
vultrino key revoke KEY_PREFIX
Credential Types
api_key
- For API tokens, bearer tokens
- Injected as:
Authorization: Bearer {key}
basic_auth
- For username/password
- Injected as:
Authorization: Basic {base64(user:pass)}
oauth2
- For OAuth2 with refresh tokens
- Handles token refresh automatically
Permissions
| Permission | Description |
|---|---|
| read | List credentials (metadata only) |
| write | Create new credentials |
| update | Modify existing credentials |
| delete | Remove credentials |
| execute | Use credentials for requests |
Error Codes
| Code | Meaning |
|---|---|
| missing_credential | X-Vultrino-Credential header required |
| unauthorized | Invalid or expired API key |
| forbidden | Permission denied |
| not_found | Credential not found |
| policy_denied | Request blocked by policy |
| upstream_error | Target server error |
Common Patterns
List then use
1. list_credentials → see what's available
2. http_request → use the appropriate credential
Handle missing credentials
When a credential isn’t available:
- List available credentials
- Tell user what’s available
- Suggest adding the needed credential
Parse response body
The body field in http_request response is always a string.
Parse it according to the content-type:
application/json→ JSON.parse()text/plain→ use directlytext/html→ use directly
Security Notes
- Never ask for actual secrets - only use aliases
- Credentials are never returned - only metadata
- All requests are logged - audit trail exists
- Policies may restrict access - some URLs/methods may be blocked
- Scopes limit visibility - you may not see all credentials
Environment
| Variable | Purpose |
|---|---|
| VULTRINO_PASSWORD | Decryption password (required) |
| VULTRINO_CONFIG | Config file path |
| RUST_LOG | Log level |
| Port | Service |
|---|---|
| 7878 | HTTP Proxy |
| 7879 | Web UI |
Quick Examples
Get GitHub user:
{"tool": "http_request", "arguments": {"credential": "github-api", "method": "GET", "url": "https://api.github.com/user"}}
Create Stripe customer:
{"tool": "http_request", "arguments": {"credential": "stripe-api", "method": "POST", "url": "https://api.stripe.com/v1/customers", "headers": {"Content-Type": "application/x-www-form-urlencoded"}, "body": "email=test@example.com"}}
List repos:
{"tool": "http_request", "arguments": {"credential": "github-api", "method": "GET", "url": "https://api.github.com/user/repos"}}
Post to Slack:
{"tool": "http_request", "arguments": {"credential": "slack-webhook", "method": "POST", "url": "https://hooks.slack.com/services/xxx", "headers": {"Content-Type": "application/json"}, "body": "{\"text\":\"Hello!\"}"}}