How Straylight-AI is built

A self-hosted Docker container housing cooperating components: an MCP server, an HTTP proxy, an OpenBao vault, a React dashboard, and (in v0.2.0) a database manager, cloud provider manager, scanner, firewall, audit logger, and lease manager. Every component is stateless except the vault — credentials survive restarts, everything else is ephemeral.

What's inside the Docker container

One docker run command starts all four components. They communicate via localhost — no external network exposure for inter-component traffic.

Five new internal components

The v0.2.0 release adds five new components to the Go MCP server, all coordinated through a shared audit event bus and lease manager. Each component is independent and can be disabled individually through configuration.

Database Manager

  • Package: internal/database/ Manages the OpenBao database secrets engine. Accepts a SQL query from the straylight_db_query MCP tool, provisions a temporary database user via OpenBao (CREATE ROLE with scoped grants), executes the query through an internal connection pool, and emits a revocation event when the lease expires.
  • Supported engines PostgreSQL, MySQL / MariaDB, Redis (OpenBao built-in plugins). Admin connection strings are stored encrypted in the vault and are never exposed to the MCP layer.
  • Lease integration Delegates all TTL and renewal logic to the Lease Manager. Default TTL is 10 minutes. On expiry, OpenBao executes the configured revocation statement (DROP ROLE).

Cloud Provider Manager

  • Package: internal/cloud/ Coordinates with AWS STS, GCP token service, and Azure identity to generate ephemeral credentials on demand. Uses native Go SDKs — no OpenBao external plugins required.
  • Credential injection Ephemeral credentials are injected as environment variables (AWS_ACCESS_KEY_ID, GOOGLE_OAUTH_ACCESS_TOKEN, AZURE_CLIENT_SECRET) into the subprocess started by straylight_exec. They are not returned to the MCP layer.
  • Scope templates Pre-defined permission scopes (read-only, deploy, specific service) reduce the blast radius of any single session. Parent credentials are stored in vault only.

Scanner

  • Package: internal/scanner/ Walks the project directory tree, applies the sanitizer's regex pattern set to each file, and produces a structured report of findings. Invoked via the straylight_scan MCP tool.
  • Detection patterns Reuses the same 14-category regex engine as the output sanitizer, covering AWS keys, GitHub PATs, Stripe keys, OpenAI keys, private key headers, connection strings, generic API key patterns, and more.
  • Report format Each finding includes file path, line number, pattern category, severity, and a redacted preview. The report includes recommended ignore rules for Claude Code, Cursor, Windsurf, and Roo.

Firewall

  • Package: internal/firewall/ Intercepts file read requests from the straylight_read_file MCP tool. Applies a two-rule decision: block the file entirely (returns a vault-redirect message) or redact secrets in place (returns the file structure with values masked).
  • Block rules .env, .env.*, *credentials*, *secret*, id_rsa, *.pem patterns result in a blocked response with guidance to use straylight_api_call instead.
  • Redact rules Config files (docker-compose.yml, settings.py, *.yaml) are served with secrets replaced by [STRAYLIGHT:service-name]. File structure is preserved so the AI coding assistant can reason about non-sensitive sections.

Audit Logger

  • Package: internal/audit/ Event bus with a JSON Lines append-only writer. All other components emit structured events to the bus. The logger writes to /data/audit/access.jsonl and maintains an in-memory ring buffer for dashboard queries.
  • Event producers Every MCP tool call (api_call, exec, db_query, scan, read_file) emits an audit event. Events include timestamp, tool name, service name, action taken, and whether sanitization fired. Credentials are never included in audit events.
  • Rotation and retention Log files rotate at 100 MB. Retention is configurable. The dashboard audit page queries the ring buffer for recent events and streams from the log file for historical queries.

Lease Manager

  • Package: internal/lease/ Lease-aware credential cache that replaces the fixed 60-second TTL cache used in v0.1.0. Tracks OpenBao lease IDs, handles renewal before expiry, and fires revocation on session end or explicit release.
  • Renewal strategy Renews leases at 75% of TTL elapsed. Stops renewing at max_ttl. On renewal failure, marks the credential as expired and forces re-provisioning on next use.
  • Used by Database Manager (database credential leases) and Cloud Provider Manager (session token tracking). Static KV secrets from v0.1.0 continue to use a simple TTL cache.

Request lifecycle: agent to API and back

Tracing a single api_call request from the AI agent through every component and back, showing exactly where credentials are handled and where they are not.

1

Agent invokes api_call MCP tool

Claude Code calls the tool with parameters: {"service": "github", "method": "GET", "path": "/user"}. No credential is in this call.

No credential in request
2

MCP server receives the tool call

The Go MCP server parses the JSON parameters and identifies the service name "github". It knows the service's base URL and auth method from the template, but does not yet have the credential value.

Service config only, no credential value
3

MCP server requests a credential-injecting transport

The MCP server calls an internal function to obtain an http.RoundTripper configured for the GitHub service. This function retrieves the credential from the vault and binds it to the transport — but returns the transport, not the credential.

Credential fetched from vault, bound to transport object
4

Vault authenticates and returns the secret

OpenBao verifies the AppRole credentials (RoleID + SecretID), checks the policy allows reading secret/github, decrypts the AES-256-GCM ciphertext, and returns the plaintext PAT value to the Go process memory.

Credential in Go process memory (not agent memory)
5

HTTP request sent with injected Authorization header

The transport's RoundTrip method adds Authorization: token ghp_xxxxx to the outgoing request headers. This happens at the net/http layer, after MCP tool parameter parsing is complete.

Credential in HTTP headers (not in MCP layer)
6

GitHub API responds

GitHub's API processes the request and returns JSON. In some edge cases (rate limit, auth error), GitHub may echo parts of the token in error messages.

Response may contain credential fragments
7

Output sanitizer scans the response

The response body is passed through the two-layer sanitizer. Regex patterns match common credential formats. Value matching checks for the exact stored PAT value. Any matches are replaced with [REDACTED].

Credentials stripped before agent receives response
8

Sanitized response returned to MCP tool, then agent

The cleaned JSON is returned as the MCP tool result. Claude Code receives it and can use the data (username, repos, etc.) in its context. No credential value ever entered the agent's context window.

Zero-knowledge guarantee achieved

Defense in depth — 5 independent layers

Each security layer is independent. Bypassing one does not compromise the others. An attacker who achieves code execution inside the Docker container still cannot exfiltrate credentials to the AI agent context window without defeating all five layers.

Built with production-proven tools

Backend (Go)

  • Go 1.23+ MCP server, HTTP proxy, vault client, output sanitizer, database manager, cloud provider manager, scanner, firewall, audit logger, lease manager
  • net/http + RoundTripper Transport-layer credential injection
  • hashicorp/vault/api OpenBao client (fully compatible), used for KV and database secrets engines
  • mark3labs/mcp-go MCP protocol implementation; seven tools in v0.2.0
  • AWS SDK for Go v2, google.golang.org/api, Azure SDK Cloud provider SDKs for ephemeral credential generation (STS AssumeRole, GCP token service, Azure identity)

Vault

  • OpenBao 2.x Apache 2.0 fork of HashiCorp Vault, drop-in compatible
  • KV Secrets Engine v2 Versioned key-value secret storage for static credentials
  • Database Secrets Engine Dynamic credential provisioning for PostgreSQL, MySQL, and Redis; temporary users with auto-revocation
  • AppRole Auth Method Machine-to-machine authentication
  • Shamir Secret Sharing Auto-unseal via derived key on restart

Frontend (React)

  • React 18 + TypeScript Dashboard UI with strict type checking
  • Vite Build tooling, dev server, HMR
  • Tailwind CSS Utility-first styling, dark mode via class strategy
  • React Router Client-side routing between dashboard views

Infrastructure

  • Docker + Docker Compose Single-container deployment, named volumes for persistence
  • Node.js / npx One-command install and Claude Code registration
  • GitHub Actions CI/CD, Docker image build and push
  • GitHub Container Registry Image distribution: ghcr.io/aj-geddes/straylight-ai

Get started in 2 minutes

One command installs and configures everything. Your AI agent will be making secure API calls before your next coffee.