DEV Community

Sam
Sam

Posted on

How I Built Aegis — A Credential Isolation Proxy for AI Agents

AI agents are getting really good at calling APIs. But the way we usually give them access — pasting raw API keys into config files, env vars, or MCP settings — is broken from a security perspective. Stop putting API keys where AI agents can read them.

I built Aegis to help keep raw API keys out of AI agents.

Aegis is a local-first credential isolation proxy that sits between your AI agent and the APIs it calls. The agent makes HTTP requests to localhost:3100/{service}/path, and Aegis injects the real credentials on the outbound request. The agent never sees, stores, or transmits the actual keys.

This post covers why I built it, the key architecture decisions, and what I learned along the way.

The Problem That Bothered Me

I was using Claude Desktop with a few MCP servers, and I noticed something that made me uncomfortable: every MCP server config had my API keys sitting in plaintext JSON. The Slack token, the GitHub PAT, the Stripe key — all right there in claude_desktop_config.json.

And these are being read by AI models. Models that accept arbitrary input. Models that are susceptible to prompt injection.

The security model is basically "trust the AI to not leak your keys." That's not a security model. That's hope.

Architecture in 30 Seconds

┌──────────┐       ┌────────────────────────────┐       ┌──────────────┐
│ AI Agent │──────▶│         Aegis Gate         │──────▶│  Slack API   │
│          │       │  localhost:3100/slack/...  │       │              │
│ (no keys)│◀──────│  inject creds + audit log  │◀──────│  slack.com   │
└──────────┘       └────────────────────────────┘       └──────────────┘
Enter fullscreen mode Exit fullscreen mode

The agent knows about services by name (slack, github, stripe), but never sees the actual credentials. Aegis handles:

  1. Credential storage — AES-256-GCM encrypted vault, PBKDF2 key derivation
  2. Request proxying — HTTP proxy that injects credentials by service name
  3. Domain guard — every outbound request checked against allowlists
  4. Audit logging — every request (allowed + blocked) recorded in SQLite
  5. Agent identity — per-agent tokens with credential scoping
  6. Policy engine — declarative YAML rules (methods, paths, rate limits, time windows)
  7. MCP server — native Model Context Protocol for Claude/Cursor/VS Code

Decisions I'm Happy About

"Standalone proxy, not a framework"

Early on I had to decide: should Aegis be a Python/JS SDK that wraps API calls, or a standalone proxy that any process can use?

I chose proxy. Any process that can make HTTP calls — Python, Node, Rust, shell scripts, MCP servers, agents built with any framework — can use Aegis without code changes. Language-agnostic, framework-agnostic, and easy to explain.

"Local-first, no cloud"

Aegis runs on your machine. Your credentials never leave your process. This is a different trust model from cloud secrets managers like Infisical or Doppler. Those are great tools, but they require you to trust a third party with your most sensitive data from day one. Aegis builds trust locally first.

"Agent auth on by default"

This was a late change. Originally, agent authentication was opt-in (--require-agent-auth). I flipped it to on-by-default with --no-agent-auth to disable. If you're building a security tool, the secure option should be the default.

"Hash-only token storage"

Agent tokens are SHA-256 hashed before storage. Even with full database access and the master key, you can't extract a token. This matches how GitHub, Stripe, and every major API platform stores tokens. If a token is lost, you regenerate — you don't recover.

I originally implemented encrypted (recoverable) token storage with AES-256-GCM. It was more convenient. But a master key compromise would have exposed every agent token. Hash-only is the industry standard for a reason.

"SQLite, not PostgreSQL"

For a local dev tool, SQLite is perfect. No separate process, no connection management, embedded in the binary. I'm using better-sqlite3-multiple-ciphers which adds ChaCha20-Poly1305 full-database encryption. The vault databases are encrypted at rest.

The MCP Bet

The Model Context Protocol is now a Linux Foundation project with over 100 compatible clients — Claude Desktop, Cursor, VS Code, Windsurf, Cline, and more. Making Aegis a native MCP server means it shows up as a tool that any MCP-compatible agent can use.

The MCP server exposes three tools:

  • aegis_proxy_request — make an authenticated API call
  • aegis_list_services — list available services (names only, never secrets)
  • aegis_health — check status

An agent using MCP doesn't even make HTTP calls — it invokes aegis_proxy_request with a service name and path, and gets the API response back. Same credential isolation, different transport.

Being on the MCP Registry means any developer using a modern AI tool can find and install Aegis without leaving their editor.

Policy Engine: Security as Code

Policies let you declare exactly what an agent can do:

agent: research-bot
rules:
  - service: slack
    methods: [GET]
    paths:
      - /api/conversations.*
      - /api/users.*
    rate_limit: 100/hour
    time_window:
      start: "09:00"
      end: "18:00"
      timezone: "UTC"
Enter fullscreen mode Exit fullscreen mode

This agent can only read Slack conversations and user info, only during business hours, and only 100 times per hour. Anything else gets a 403.

Policies are YAML files in a policies/ directory — version-controlled, reviewable, auditable. There's a dry-run mode to test policies before enforcing them.

What I Learned Building This

The security threat model writes the feature list. Once I mapped out what can go wrong (prompt injection, credential exfiltration, unauthorised access, no audit trail), the features fell out naturally: domain guard, body inspection, agent scoping, audit ledger.

Encryption choices matter more than you think. AES-256-GCM for field-level credential encryption. ChaCha20-Poly1305 for full-database encryption. PBKDF2 with 210,000 iterations (OWASP minimum for SHA-512). Random salt per deployment. These aren't resume decorations — any weaker choice and you can't credibly call it a security tool.

OS keychain integration is surprisingly painful. macOS Keychain uses the security CLI. Windows uses cmdkey for write and PowerShell P/Invoke for reads (because cmdkey can't retrieve credential values). Linux uses secret-tool (which may or may not be installed). Each platform has its own quirks. I ended up with a KeyStorage interface, four implementations, and 64 tests just for key storage.

Solo dev + verifiable security = confidence. Published STRIDE threat model, documented security architecture, and comprehensive test coverage across every module (crypto, vault, proxy, policy, MCP). When you're the only person working on a security tool, the threat model and tests are the only reviewers you have. Every change goes through yarn build && yarn test before commit.

The Numbers

  • 28 STRIDE threats analysed in the threat model — 14 fully mitigated, 10 low residual, 4 accepted with documented rationale
  • 0 critical/high unmitigated findings
  • Comprehensive test suite covering crypto, vault, proxy, agent, policy, MCP, logging, metrics, webhooks, dashboard, users, Shamir's Secret Sharing, config, CLI, concurrent isolation, and key storage

Try It

npm install -g @getaegis/cli
aegis init
aegis vault add --name demo --service httpbin --secret "test-key" --domains httpbin.org
aegis agent add --name my-agent
# → Token: aegis_abc123_def456 (save this — it won't be shown again)
aegis agent grant --name my-agent --service httpbin
aegis gate
# In another terminal:
curl http://localhost:3100/httpbin/get \
  -H "X-Target-Host: httpbin.org" \
  -H "X-Aegis-Agent: aegis_abc123_def456"
Enter fullscreen mode Exit fullscreen mode

The source is at github.com/getaegis/aegis — Apache 2.0 licensed. If this approach resonates, stars, issues, and feedback are genuinely helpful.


If you want to understand the problem Aegis solves before diving into the architecture, start with Why Your AI Agent Shouldn't See Your API Keys.

Have questions about the architecture or security model? Drop them in the comments!

Top comments (0)