Keiko
README.md
# Keiko
Secure secrets manager for AI agents. Keiko lets AI tools like Claude Code use API tokens, passwords, and credentials **without ever seeing the actual values**.
## The Problem
AI agents need API tokens to do useful work — calling APIs, deploying code, managing infrastructure. But passing secret values through an AI's context window is a security risk: they appear in conversation logs, tool responses, and potentially in training data.
## How Keiko Solves It
Keiko uses a **proxy pattern**. The AI agent tells Keiko *what command to run* and *which secrets it needs*, but never sees the secret values themselves:
```
AI Agent ──► Keiko MCP Server ──► Keiko Backend
│ │
resolves secrets ◄── HTTPS ──┘
│
spawns: bash -c "your command"
with secrets as env vars
│
captures output
sanitizes (redacts leaked values)
│
AI Agent ◄── clean output only
```
The AI says *"run this curl command with my API token"* — Keiko injects the token as an environment variable, runs the command, scans the output for any leaked values, redacts them, and returns clean output.
## Quick Start
### 1. Deploy the backend
The backend stores secrets encrypted (AES-256-GCM) and serves them over HTTPS. Deploy with Docker to any platform with persistent storage:
```bash
# Generate an encryption key
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Deploy (Railway, Render, Fly, Docker Compose, etc.)
# See docs/setup-guide.md for full instructions
```
### 2. Set up the MCP server
The MCP server runs on each machine that needs access to secrets. It's a standalone package with only 2 dependencies — no native compilation needed.
```bash
cd packages/mcp
npm install
npm run build
node dist/index.js --store-token YOUR_TOKEN
```
### 3. Connect to Claude Code
Add to `~/.mcp.json`:
```json
{
"mcpServers": {
"keiko": {
"command": "node",
"args": ["/absolute/path/to/keiko/packages/mcp/dist/index.js"],
"env": {
"KEIKO_URL": "https://your-keiko-backend.example.com"
}
}
}
}
```
Restart Claude Code. Ask *"Check Keiko session status"* to verify.
## How the AI Uses It
Once connected, the AI discovers available secrets and uses them in commands — without ever seeing the values:
```
AI: list_secrets
→ my_api_token [Bearer token in Authorization header]
AI: run_with_secrets
command: curl -s -H "Authorization: Bearer $API_TOKEN" https://api.example.com/data
secrets: [{env: "API_TOKEN", name: "my_api_token"}]
→ {"data": [...]} (secret values redacted from output)
```
Each secret includes `auth_pattern` and `auth_instructions` metadata, so the AI knows how to use it without external documentation.
## MCP Tools
| Tool | Purpose |
|------|---------|
| `run_with_secrets` | Run a command with secrets injected as env vars |
| `list_secrets` | Discover available secrets (names and auth patterns only) |
| `session_status` | Check authentication state |
| `lock` | Kill switch — revoke all active sessions |
| `set_ttl` | Configure session expiry (0.5–24 hours) |
| `add_secret` | Create a new secret |
| `update_secret` | Replace an existing secret's value |
| `get_guide` | Fetch the usage guide |
## Security
- **Encryption at rest** — AES-256-GCM with per-secret random IV, plus versioned keys (`ENCRYPTION_KEY`, `ENCRYPTION_KEY_V2`, …) and an in-place rotation endpoint so the key can be rotated without downtime
- **Hashed auth tokens** — 64-char hex tokens, SHA-256 hashed in the database, plaintext shown once
- **Token scoping** — each token is restricted to a set of secret-name glob patterns (e.g. `railway_*`), so a leaked token only exposes the slice of the vault it actually needs
- **Output sanitization** — command output scanned for secret values in raw, base64, base64url, URL-encoded, hex, JSON-string-escaped, and HTML-entity-escaped forms
- **Per-token rate limit** — independent of per-IP, caps abuse from a single token routed through multiple IPs
- **CSRF protection** — `X-CSRF-Token` header required on every mutating admin-UI endpoint
- **Session management** — configurable TTL, auto-refresh, global kill switch
- **Audit trail** — every action logged with token ID, session ID, IP, and timestamp
Secret values **never** appear in AI tool responses, conversation logs, or on disk on client machines. See `GET /api/guide` for the full threat model — what the proxy pattern does and does not prevent.
## Architecture
```
keiko/
├── packages/mcp/ # MCP server (runs on your machine)
│ ├── src/
│ │ ├── server.ts # 7 tool registrations
│ │ ├── client.ts # HTTPS client to backend
│ │ ├── executor.ts # Shell spawner + env var injection
│ │ ├── sanitizer.ts # Output redaction
│ │ └── keychain.ts # OS keychain (Win/Mac/Linux)
│ └── package.json # 2 deps: @modelcontextprotocol/sdk + zod
│
├── src/ # Backend API (runs on your server)
│ ├── api/ # Express routes, auth, crypto
│ └── ui/ # EJS admin templates
│
└── Dockerfile # Multi-stage production build
```
The MCP server and backend are fully independent packages. The MCP server has **zero native dependencies** — `npm install` works everywhere without compilers or `--ignore-scripts`.
## Platform Support
| Environment | Token Storage | Notes |
|------------|---------------|-------|
| Windows | Windows Credential Manager | Commands run via Git Bash (not WSL) |
| macOS | macOS Keychain | System bash |
| Linux | libsecret | Requires `secret-tool` |
| Docker / CI | `KEIKO_TOKEN` env var | No keychain needed |
## Documentation
- **[Setup Guide](docs/setup-guide.md)** — Full setup instructions for all environments, backend deployment, troubleshooting, API reference, and configuration options
## Tech Stack
**Backend:** Node.js 20, TypeScript, Express, SQLite (better-sqlite3), EJS, Google OAuth
**MCP Server:** Node.js 20, TypeScript, MCP SDK v1.x, Zod
**Deployment:** Docker (multi-stage build), any platform with persistent volumes. Tested with [Railway](https://railway.com).
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues