Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

ComponentDescription
CLICommand-line interface for all operations
Web UIBrowser-based admin dashboard
HTTP ProxyMakes authenticated requests on behalf of agents
MCP ServerModel Context Protocol server for LLM integration

Next Steps

Installation

Requirements

  • Rust 1.75 or later (for building from source)
  • OpenSSL development libraries (on Linux)
# 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:

  1. Set a storage password (encrypts your credentials at rest)
  2. Create an admin username for the web UI
  3. Set an admin password for the web UI

Tip: Set the VULTRINO_PASSWORD environment 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

CommandDescription
vultrino initInitialize configuration
vultrino addAdd a credential
vultrino listList all credentials
vultrino remove <alias>Remove a credential
vultrino request <alias> <url>Make authenticated request
vultrino webStart web UI
vultrino serve --mcpStart MCP server
vultrino role listList roles
vultrino key createCreate API key

Next Steps

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"
OptionDescriptionDefault
bindAddress and port for the HTTP proxy127.0.0.1:7878
modeDeployment mode (local or server)local

Storage Section

[storage]
backend = "file"  # Storage backend: "file", "keychain", or "vault"

[storage.file]
path = "~/.local/share/vultrino/credentials.enc"
OptionDescriptionDefault
backendStorage backend typefile
pathPath to encrypted credentials fileOS-specific

Logging Section

[logging]
level = "info"  # Log level: error, warn, info, debug, trace
# audit_file = "~/.local/share/vultrino/audit.log"  # Optional audit log
OptionDescriptionDefault
levelLogging verbosityinfo
audit_filePath to audit log (optional)disabled

MCP Section

[mcp]
enabled = true
transport = "stdio"  # "stdio" or "http"
OptionDescriptionDefault
enabledEnable MCP servertrue
transportTransport methodstdio

Environment Variables

VariableDescription
VULTRINO_PASSWORDStorage encryption password (avoids prompts)
VULTRINO_CONFIGPath to config file
RUST_LOGOverride 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

MethodBest ForComplexity
Local DevelopmentPersonal use, testingSimple
VPS / ServerTeam deployment, productionModerate
Cloudflare WorkersEdge deployment, serverlessModerate
DockerContainerized environmentsSimple

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.1 only
  • 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

  1. Always use HTTPS in production — Use a reverse proxy with TLS
  2. Restrict network access — Firewall rules, VPN, or private network
  3. Use strong passwords — Both storage and admin passwords
  4. Enable audit logging — Track credential usage
  5. Rotate API keys — Set expiration on Vultrino API keys
  6. 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

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

  1. Store password in keychain — Use OS keychain to avoid plaintext passwords
  2. Use aliases — Add alias vreq='vultrino request' to your shell
  3. 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 ComponentCloudflare Equivalent
Encrypted file storageKV with encryption
Session managementDurable Objects
HTTP handlersWorkers
Static assetsWorkers 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

FeatureStatusNotes
HTTP ProxyVia fetch()
Web UIWorkers Sites
MCP ServerRequires stdio (not available)
File Storage⚠️Use KV instead
OS KeychainNot available

Security Considerations

  1. Secrets Management — Use Wrangler secrets for encryption keys
  2. KV Encryption — Always encrypt credentials before storing in KV
  3. Access Control — Use Cloudflare Access for additional auth layer
  4. 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

VariableDescriptionRequired
VULTRINO_PASSWORDStorage encryption passwordYes
VULTRINO_CONFIGPath to config fileNo
RUST_LOGLog levelNo

Volumes

PathDescription
/dataCredential storage and state
/etc/vultrino/config.tomlConfiguration 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

VariableDescription
VULTRINO_PASSWORDStorage encryption password (required)
VULTRINO_CONFIGPath to config file
RUST_LOGLog level (trace, debug, info, warn, error)

Commands

init

Initialize a new Vultrino instance.

vultrino init [OPTIONS]

Options:
  --force    Overwrite existing configuration

This command:

  1. Creates the credentials storage file
  2. Prompts for admin username and password
  3. 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 token
  • basic_auth — Username and password
  • oauth2 — 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

CodeMeaning
0Success
1General error
2Invalid arguments
3Credential not found
4Permission denied
5Storage 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:

  1. Delete the admin configuration:

    rm ~/.vultrino/admin.json
    
  2. 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_secure matches 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:

  1. Extract the credential alias from X-Vultrino-Credential
  2. Look up the credential in storage
  3. Strip the header
  4. Add the appropriate auth header (e.g., Authorization: Bearer ghp_xxx)
  5. 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

  1. Receive Request

    • Parse target URL from path or proxy request
    • Extract X-Vultrino-Credential header
    • Extract Authorization header (if RBAC enabled)
  2. Authenticate

    • Validate API key (if provided)
    • Check role permissions
    • Verify credential scope access
  3. Policy Check

    • Evaluate policies for the credential
    • Check URL patterns, methods, rate limits
    • Deny if any policy fails
  4. Credential Injection

    • Decrypt credential from storage
    • Format appropriate auth header
    • Remove Vultrino-specific headers
  5. 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:

  1. API key validity (from session or configuration)
  2. Role permissions (read, execute, write, delete)
  3. 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 token
  • stripe-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_PASSWORD is set
  • Check credentials file exists (vultrino list should 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

  1. Don’t ask for credentials — Always use aliases, never actual secrets
  2. Use descriptive aliases — Help the AI understand what each credential is for
  3. Set up RBAC — Create restricted roles for AI agent access
  4. Review audit logs — Monitor what requests agents are making

For System Administrators

  1. Use short-lived credentials — Rotate frequently
  2. Scope credentials narrowly — Each credential should do one thing
  3. Enable audit logging — Track all credential usage
  4. 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:

TypeUse CaseAuth Header Format
api_keyAPI tokens, bearer tokensAuthorization: Bearer <key>
basic_authUsername/password authAuthorization: Basic <base64>
oauth2OAuth2 flowsAuthorization: Bearer <access_token>
private_keySSH 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-readonly
  • stripe-test-charges
  • aws-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:

  1. Delete the old credential:

    vultrino delete github-api
    
  2. 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:

  1. Generate new credential at source (GitHub, Stripe, etc.)
  2. Add to Vultrino with new alias
  3. Test the new credential
  4. Update applications to use new alias
  5. 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.

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

PermissionDescription
readList credentials (metadata only, never secrets)
writeCreate new credentials
updateModify existing credentials
deleteRemove credentials
executeUse 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

  1. Navigate to Roles in the sidebar
  2. Click New Role
  3. Fill in:
    • Name
    • Description (optional)
    • Select permissions
    • Enter credential scopes (comma-separated)
  4. Click Create Role

Creating API Keys

  1. Navigate to API Keys in the sidebar
  2. Click New API Key
  3. Fill in:
    • Name (for identification)
    • Select a role
    • Expiration date (optional)
  4. Click Create Key
  5. 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:

  1. Create new key
  2. Update application configuration
  3. Verify new key works
  4. 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:

  1. Storing credentials securely (encrypted at rest)
  2. Exposing only aliases to agents
  3. Injecting auth automatically
  4. Logging all usage

Integration Options

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 access
  • stripe-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_PASSWORD is 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

FieldTypeDescription
namestringUnique policy name
credential_patternstringGlob pattern for credentials this policy applies to
default_actionstringAction when no rules match: allow, deny
rulesarrayList of policy rules

Rule Definition

FieldTypeDescription
conditionobjectCondition to evaluate
actionstringAction 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

  1. RBAC check — Does the API key have permission?
  2. Credential scope — Is the credential in scope for this role?
  3. Policy match — Find policies matching the credential alias
  4. Rule evaluation — Evaluate rules in order
  5. 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

PluginDescriptionCredential Types
PGP SigningPGP/GPG signing and verificationpgp_key

Next Steps

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:

  1. Checks for the wasm32-wasip1 target (installs if needed)
  2. Runs cargo build --release --target wasm32-wasip1
  3. Copies the built WASM module to the plugin directory

Requirements:

  • Rust toolchain installed
  • rustup available 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:

  1. Ensure Rust is installed: rustup --version
  2. Check the target: rustup target list --installed
  3. 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

  1. Copy or symlink your plugin to ~/.vultrino/plugins/my-plugin/
  2. Ensure plugin.toml and the .wasm file are present
  3. Start Vultrino: vultrino serve
  4. The plugin should be loaded automatically

Best Practices

  1. Handle errors gracefully — Always return proper error responses
  2. Validate inputs — Check parameters before processing
  3. Keep secrets secure — Never log or expose credential data
  4. Optimize size — Use opt-level = "s" and LTO for smaller WASM
  5. 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

FieldTypeRequiredDescription
private_keytextareaYesASCII-armored PGP private key
passphrasepasswordNoPassphrase to unlock the key
key_idtextNoSpecific 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

  1. Navigate to Credentials > Add Credential
  2. Select “PGP/GPG Key (pgp-signing)” from the dropdown
  3. Paste your ASCII-armored private key
  4. Enter passphrase if the key is encrypted
  5. Click “Add Credential”

Available Actions

sign

Create a signature for arbitrary data. Returns a base64-encoded signature.

Parameters:

  • data (string, required) — Data to sign
  • armor (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 signed
  • signature (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:

ToolDescription
pgp_signSign data and return the signature
pgp_sign_cleartextCreate a cleartext signed message
pgp_verifyVerify a signature
pgp_get_public_keyGet 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:

  1. Store the private key in Vultrino
  2. Create API keys for team members
  3. 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:

HeaderRequiredDescription
X-Vultrino-CredentialYesAlias of credential to use
AuthorizationDependsAPI key (if RBAC enabled)
*NoAll 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:

StatusCodeDescription
400missing_credentialX-Vultrino-Credential header not provided
401unauthorizedInvalid or expired API key
403forbiddenPermission denied by RBAC or policy
404not_foundCredential alias not found
502upstream_errorFailed 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:

FieldTypeRequiredDescription
credentialstringYesCredential alias
actionstringYesAction to perform
paramsobjectYesAction-specific parameters

Actions:

ActionDescription
http.requestMake an HTTP request
crypto.signSign data (future)

HTTP Request Params:

FieldTypeRequiredDescription
methodstringYesHTTP method
urlstringYesTarget URL
headersobjectNoAdditional headers
bodystringNoRequest 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:

StatusCodeDescription
404not_foundCredential 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:

FieldTypeRequiredDescription
aliasstringYesUnique human-readable name
typestringYesCredential type
dataobjectYesCredential data (type-specific)
descriptionstringNoOptional description
metadataobjectNoCustom 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:

StatusCodeDescription
400invalid_requestInvalid request body
409conflictAlias 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:

StatusCodeDescription
404not_foundCredential 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

CodeHTTP StatusDescription
invalid_request400Malformed request body or parameters
missing_credential400X-Vultrino-Credential header missing
unauthorized401Invalid or expired API key
forbidden403Permission denied
not_found404Resource not found
conflict409Resource already exists
rate_limited429Too many requests
internal_error500Server error
upstream_error502Target server error

Rate Limits

Default rate limits (configurable):

EndpointLimit
Proxy requests1000/minute
Credential management100/minute
Authentication10 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:

ErrorDescription
credential_not_foundThe specified credential alias doesn’t exist
permission_deniedNo permission to use this credential
policy_deniedRequest blocked by policy rules
upstream_errorFailed 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:

ErrorDescription
permission_deniedNo write permission
alias_existsA credential with this alias already exists
invalid_typeUnknown 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:

ErrorDescription
permission_deniedNo delete permission
not_foundCredential with this alias not found

Permission Requirements

ToolRequired Permission
list_credentialsread
http_requestexecute
add_credentialwrite
delete_credentialdelete

Scope Restrictions

If the API key’s role has credential scopes, tools are further restricted:

  • list_credentials — Only shows credentials matching scope patterns
  • http_request — Only works with credentials matching scope patterns
  • add_credential — New credentials must match scope patterns
  • delete_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

  1. Credentials are never exposed — The AI only sees aliases
  2. All requests are logged — Audit trail of all tool usage
  3. Policies are enforced — URL and method restrictions apply
  4. Rate limits apply — Prevent abuse
  5. 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:

ContentPath
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

ToolPurposeRequired Permission
list_credentialsList available credentialsread
http_requestMake authenticated requestexecute
add_credentialStore new credentialwrite
delete_credentialRemove credentialdelete

Common Credential Aliases

Typical naming patterns:

  • github-api — GitHub API token
  • stripe-live / stripe-test — Stripe API keys
  • openai — OpenAI API key
  • anthropic — Anthropic API key
  • aws-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:

  1. List what IS available
  2. Explain to user
  3. 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 path
  • RUST_LOG — Log level

Default Ports

  • 7878 — HTTP proxy
  • 7879 — Web UI

File Locations

  • ~/.vultrino/credentials.enc — Encrypted credentials
  • ~/.vultrino/admin.json — Admin auth
  • /etc/vultrino/config.toml — System config

Security Model

  1. Credentials encrypted at rest — AES-256-GCM
  2. Aliases only — Never expose actual secrets
  3. RBAC — Role-based access control
  4. Policies — URL/method restrictions
  5. 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

PermissionDescription
readList credentials (metadata only)
writeCreate new credentials
updateModify existing credentials
deleteRemove credentials
executeUse credentials for requests

Error Codes

CodeMeaning
missing_credentialX-Vultrino-Credential header required
unauthorizedInvalid or expired API key
forbiddenPermission denied
not_foundCredential not found
policy_deniedRequest blocked by policy
upstream_errorTarget 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:

  1. List available credentials
  2. Tell user what’s available
  3. 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 directly
  • text/html → use directly

Security Notes

  1. Never ask for actual secrets - only use aliases
  2. Credentials are never returned - only metadata
  3. All requests are logged - audit trail exists
  4. Policies may restrict access - some URLs/methods may be blocked
  5. Scopes limit visibility - you may not see all credentials

Environment

VariablePurpose
VULTRINO_PASSWORDDecryption password (required)
VULTRINO_CONFIGConfig file path
RUST_LOGLog level
PortService
7878HTTP Proxy
7879Web 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!\"}"}}