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: jacob@example.com"
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 at
https://localhost:8443— add your passwords here -
Login Router at
http://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.
Top comments (9)
The placeholder pattern is smart but honestly what scares me more is the stuff that happens before you even get to login flows. I've been building side projects with AI assistance and caught it dumping .env contents into debug logs, copying API keys into comments "for reference", stuff like that. The credential exposure surface is way bigger than just the login page.
The prompt injection angle is the real nightmare scenario though. If a malicious page can extract creds from context, the whole "just paste your password" workflow that most people use with AI agents is basically an open door. Nice that you're handling TOTP too - that's usually where the local-first approach falls apart because people just give up and paste the code manually.
Totally agree on the .env and debug log leakage — the login page is just the most visible part of a much bigger surface.
The placeholder pattern isn't limited to passwords either. API keys, OAuth tokens — anything sensitive could go through the same
{{placeholder}}→ local substitution pipeline. Haven't built that layer yet, but the architecture supports it.The bigger elephant in the room though: AI agents with shell access. Once an agent can run arbitrary commands on your machine, it can read keychains, dump process memory, sniff traffic — no real way to stop it. Cerberus solves the LLM context window problem, but the shell-level trust boundary is a much harder unsolved problem for the whole industry.
On TOTP — deliberate choice. Too many local-first tools handle passwords fine then expect you to manually paste the 2FA code.
Appreciate the real-world perspective.
The shell access thing is honestly a different beast. Placeholder pattern handles the context window leak cleanly, but once an agent can run arbitrary commands - reading ~/.zsh_history, dumping process memory - there's no semantic layer that saves you. I don't think anyone in the industry has a clean answer yet. 'Properly sandboxed agent' is still a fuzzy concept.
Wow — this was an eye-opener! 🚨 It’s easy to forget that AI tools can end up storing sensitive data like passwords if we’re not careful. Really appreciate the practical steps you shared to fix it — things like clearing history, tightening permissions, and isolating credentials are exactly the kind of actionable advice developers need today. Thanks for breaking it down clearly!
Thank you so much — really appreciate the kind words 🙏
I also think this can go far beyond passwords: protecting other sensitive data is a very interesting next step, especially for real-world commercial use cases.
Great approach with the placeholder pattern. The credential isolation problem extends beyond login automation though.
The same trust model issue shows up in social media automation. Most scheduling tools (Buffer, Hootsuite, Later) require uploading platform API keys to their cloud. One breach at the provider level and every connected account is compromised.
The Cerberus architecture — keeping secrets local, resolving on-device — is the right pattern for any automation touching sensitive tokens.
For social media specifically, defpromo takes a similar local-first approach: zero-cloud browser extension that automates engagement with API keys stored entirely on your machine. Same principle as your Vaultwarden + CDP pipeline, applied to marketing automation.
The broader lesson: any time a tool says "give us your credentials," ask where they live. Someone else's server = attack surface.
Good point — the credential isolation pattern definitely applies beyond login. API keys for social media, payment gateways, CI/CD secrets... anything that touches third-party automation has the same trust problem.
One thing I'd add: Cerberus goes a step further than just "keeping secrets local." The LLM itself never sees the real credentials — it only writes
{{password}}, and the substitution happens at a layer below the AI. So even if the model gets prompt-injected or the conversation leaks, there's nothing sensitive to extract. That's the key difference from approaches where the agent has access to credentials but stores them locally.Thanks for the thoughtful comment!
Impressive work on the Cerberus KeyRouter! The approach to prevent LLMs from accessing sensitive credentials while allowing seamless automation is essential, especially given the current security landscape. Are you considering implementing additional layers, like multi-factor authentication, to enhance security further? 🔐
Thanks! MFA is actually already part of the design — we support
{{totp}}as a placeholder for two-factor authentication codes, so the router can grab a TOTP code from Vaultwarden and fill it in automatically, same as passwords. The AI never sees the actual code.On top of that, there's a human-in-the-loop mode for high-risk accounts: the router sends you a confirmation request before executing the login, with a 50-second timeout that auto-rejects if you don't approve. Combined with rate limiting (3 attempts/min, 20/hour per vault item) and full audit logging, it's meant to cover the "what if something goes wrong" scenarios too.
Would love to hear if you try it out!