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 thestraylight_db_queryMCP tool, provisions a temporary database user via OpenBao (CREATE ROLEwith 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 bystraylight_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 thestraylight_scanMCP 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 thestraylight_read_fileMCP 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,*.pempatterns result in a blocked response with guidance to usestraylight_api_callinstead. -
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.jsonland 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.
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.
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.
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.
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.
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.
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.
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].
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.
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.