When AI agents automate your browser, they need your login credentials in their context window. That means your passwords are sent to cloud LLM providers, stored in conversation logs, and potentially exposed via prompt injection attacks.
I built Cerberus KeyRouter to fix this.
The Problem
AI agents like OpenClaw, Claude Desktop, and Cursor can automate browsers — filling forms, clicking buttons, navigating pages. But when they hit a login page, the typical flow looks like this:
User: "Log into GitHub for me"
User: "Email: [email protected]"
User: "Password: MyS3cretP@ss"
Agent: "Logging in..."
That password just traveled through:
The LLM provider's servers (Anthropic, OpenAI, etc.) — logged, stored, potentially used for training
API proxy services — if you're using a third-party API relay (common in many regions), your password passes through yet another unknown middleman
Conversation history — sitting in plain text in your chat logs
And it gets worse. Prompt injection is a real attack vector: a malicious webpage can embed hidden instructions that trick the AI into leaking credentials it learned earlier in the conversation.
The Solution: Placeholder Pattern
The core idea is simple:
The AI writes the logic using placeholders. Real credentials are substituted locally and injected directly into the browser. The LLM never sees your actual password.
Instead of passing real credentials, the AI agent sends instructions like this:
{
"vaultItem": "GitHub",
"steps": [
{ "action": "fill", "selector": "#login_field", "value": "{{email}}" },
{ "action": "fill", "selector": "#password", "value": "{{password}}" },
{ "action": "click", "selector": "[type=submit]" }
]
}
Cerberus KeyRouter — an MCP (Model Context Protocol) server running on your machine — intercepts this call, fetches the real credentials from your local Vaultwarden instance, replaces the {{placeholders}}, and executes the actions via Chrome DevTools Protocol (CDP).
The AI sees {{password}}. Your browser gets the real password. The LLM never knows the difference.
Architecture
AI Agent (OpenClaw, Claude Desktop, etc.)
│
│ MCP call: secure_login("GitHub", steps with {{placeholders}})
│
▼
Login Router (localhost:8899)
├─ Authenticate via bearer token
├─ Fetch credentials from Vaultwarden
├─ Replace {{placeholders}} with real values
├─ Execute via Chrome CDP (localhost only)
├─ Clear credentials from memory
└─ Return { status: "ok" } — no passwords in response
Vaultwarden (Docker, localhost:8443)
└─ E2E encrypted password storage
Everything runs locally. Passwords never leave your machine.
Security Model: Six Layers of Defense
1. Implicit allowlist — Vaultwarden only stores credentials for sites you've explicitly added. The AI can't log into arbitrary sites.
2. URL verification — Before injecting credentials, the router reads the browser's actual URL via CDP and matches it against the vault entry's URI. Phishing sites get rejected.
3. Bearer token auth — Each Vaultwarden account gets a unique token. No token, no access.
4. Rate limiting — 3 attempts per minute, 20 per hour per vault item. Consecutive failures trigger a cooldown.
5. Audit logging — Every login attempt is recorded (success, failure, rate-limited). Passwords are never logged. View at /audit.
6. Human-in-the-loop — High-risk accounts can require manual approval before each login. 50-second timeout, auto-reject if not approved.
Quick Start
git clone https://github.com/DemoJacob/cerberus-keyrouter.git
cd cerberus-keyrouter
cp .env.example .env
# Set VW_ADMIN_TOKEN in .env
docker compose up --build -d
This gives you:
Vaultwarden athttps://localhost:8443— add your passwords here
Login Router athttp://localhost:8899— MCP server + admin panel
Open the admin panel at http://localhost:8899/admin, add your Vaultwarden account, and copy the generated bearer token.
Connect your AI agent via MCP:
{
"mcpServers": {
"cerberus": {
"baseUrl": "http://localhost:8899/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
Done. Your agent can now log into any site in your vault — without knowing a single password.
fill vs type
Most sites work with fill (sets value + dispatches events). React/SPA sites with controlled components may need type (key-by-key input with 50ms delay). Try fill first, switch to type if the form doesn't submit properly.
Multi-Step Login
Banks and other sites often split login across multiple pages. Handle this with separate calls:
// Step 1: username + next
{ "vaultItem": "MyBank", "steps": [
{ "action": "type", "selector": "#username", "value": "{{username}}" },
{ "action": "click", "selector": "#next" },
{ "action": "wait", "selector": "input[type=password]" }
]}
// Step 2: password + submit
{ "vaultItem": "MyBank", "steps": [
{ "action": "type", "selector": "#password", "value": "{{password}}" },
{ "action": "click", "selector": "#login" }
]}
Real-World Usage
I've been running this with OpenClaw for daily tasks. Here's an actual example — my AI agent querying order history on zooplus.de, logged in via Cerberus:
The agent reports: "I didn't touch any plaintext passwords. I only passed the placeholders {{email}} and {{password}}. Cerberus fetched the credentials from your local Vaultwarden vault and filled them directly into the browser."
No password in the LLM context. No password in the logs. No password leaving my machine.

Technical Stack
TypeScript / Node.js — login router
Playwright-core — CDP browser automation
Vaultwarden — self-hosted Bitwarden (password storage)
MCP Protocol — Streamable HTTP transport
SQLite — config + audit log storage
AES-256-GCM — stored master password encryption
Docker Compose — one-command deployment
What's Next
- Cookie caching (login once, reuse sessions)
- Support for OpenClaw snapshot ref IDs (alongside CSS selectors)
- Multi-agent framework support
- SaaS version with zero-knowledge E2E encrypted backend
Try It
The project is open source under AGPL-3.0:
GitHub: github.com/DemoJacob/cerberus-keyrouter
Works on macOS and Linux. Docker + Chrome + any MCP-compatible AI agent.
Feedback, issues, and stars are all welcome. If you're building AI agents that need to handle authentication, I'd love to hear about your use case.