Vibe-coding sessions leak API keys into LLM context via runtime logs. VibeProxy prevents this by acting as a local proxy that swaps dummy tokens for real API keys.
Agent (dummy token) → VibeProxy (localhost) → Real API (real key)
- Give your AI agent a dummy token (e.g.
vp-local-openai) - VibeProxy intercepts requests, swaps the dummy token for the real API key stored in your OS keychain
- The real key never appears in logs, context, or agent memory
Stateless dummy tokens: Tokens like vp-local-openai are deterministic identifiers derived from the service name, not secrets. They simply tell VibeProxy "this request is for the OpenAI service — look up the real key." Since the proxy runs locally on each developer's machine with their own keychain, there is no need for per-person token generation or file-based state (.vibelocal).
Pluggable secret backends: Real API keys are managed via a secret.Provider interface. The default backend uses the OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager). Alternative backends (e.g. environment variables, 1Password CLI, AWS Secrets Manager) can be added by implementing a single interface.
VibeProxy is intentionally a lightweight, single-purpose local proxy. The following decisions are deliberate and final:
What VibeProxy does:
- Swap dummy tokens for real API keys on localhost
- Support any HTTP-header-based auth provider via YAML config (
auth_header+auth_scheme) - Provide pluggable secret backends (
secret.Providerinterface) - Offer an optional OpenAI-compatible gateway for model-based routing
- Produce JSON audit logs and provider-compatible error responses
What VibeProxy intentionally does NOT do:
- Per-person token generation (
.vibelocal): Dummy tokens are deterministic markers (vp-local-{service}), not secrets. Since each developer runs their own local proxy with their own keychain, per-user token disambiguation adds complexity without benefit. - Plugin/interface-based provider abstraction: The current
HeaderProvider+ YAML config handles all major LLM providers (OpenAI, Anthropic, Google, Mistral, etc.). Exotic auth schemes (e.g. AWS V4 signing for Bedrock) will be addressed when real demand arises — not speculatively. - OpenTelemetry / observability dashboards: A local proxy does not need distributed tracing.
slog+ JSON audit logs are sufficient. OTel can be bridged fromslogif enterprise demand materializes. - Framework-based middleware or routing: The standard Go middleware chaining pattern (
func(http.Handler) http.Handler) is idiomatic, readable, and requires zero dependencies.
# Clone & build
git clone https://github.com/unco3/vibeproxy.git
cd vibeproxy
go build -o vibe .
# Initialize project config
./vibe init
# Store your real API key in the OS keychain
./vibe auth login openai
./vibe auth login anthropic
# Start the proxy
./vibe start
# Check status
./vibe status
# View audit logs
./vibe logs
# Stop the proxy
./vibe stopservices:
openai:
target: https://api.openai.com
auth_header: Authorization
auth_scheme: Bearer
allowed_paths:
- /v1/chat/completions
- /v1/embeddings
- /v1/responses
rate_limit:
requests_per_minute: 60
anthropic:
target: https://api.anthropic.com
auth_header: x-api-key
auth_scheme: ""
allowed_paths:
- /v1/messages
rate_limit:
requests_per_minute: 60
listen: 127.0.0.1:8080
timeouts:
read_seconds: 30 # Client request read timeout
write_seconds: 120 # Response write timeout (long for LLM streaming)
upstream_seconds: 90 # Upstream API response header timeout
cors:
enabled: false
allowed_origins: # Wildcard "*" is not allowed
- http://localhost:3000
# Optional: secret_backend: env (default: keychain)
# Optional: OpenAI-compatible gateway
# gateway:
# enabled: true
# paths:
# - /v1/chat/completions
# - /v1/embeddings
# models:
# gpt-: openai
# claude-: anthropicGenerated by vibe init. Point your agent's environment here:
VIBEPROXY_URL=http://127.0.0.1:8080
OPENAI_API_KEY=vp-local-openai
ANTHROPIC_API_KEY=vp-local-anthropicRequest
→ Localhost validation
→ CORS (if enabled)
→ Audit (start time, agent identity)
→ Body size limit (10 MB)
→ [Gateway path] → Token validation → Rate limit → Translate → Upstream API
→ [Proxy path] → Token extraction (Authorization / x-api-key)
→ Route resolution (vp-local-{service} → service name)
→ Path whitelist check (with path normalization)
→ Rate limit check
→ Secret lookup (on demand, not stored in context)
→ httputil.ReverseProxy → Upstream API
→ Response → Audit log
| Feature | Detail |
|---|---|
| Token Swap | Extracts dummy token from Authorization or x-api-key headers, resolves the real key from the secret provider, and forwards the request |
| Localhost Only | Binds to 127.0.0.1 — refuses non-localhost addresses |
| Path Whitelist | Only configured API paths are proxied; all others return 403. Paths are normalized (path.Clean) to prevent traversal attacks |
| Rate Limiting | In-memory sliding window counter per service (resets on restart) |
| Timeout Control | Configurable read/write/upstream timeouts to handle slow or hanging APIs |
| CORS | Optional CORS support for browser-based agent UIs (disabled by default). Wildcard * origin is rejected at startup |
| Error Formatting | Provider-compatible error responses (OpenAI/Anthropic SDK format) |
| Gateway | Optional OpenAI-compatible universal API — route by model prefix to any provider. Requires vp-local-* token, enforces rate limits and audit logging |
| Body Limit | Request bodies are limited to 10 MB to prevent memory exhaustion from oversized payloads |
| Audit Log | JSON Lines at ~/.vibeproxy/audit.log — timestamps, methods, paths, status codes, durations (no secrets) |
| Daemon Mode | Background process via self-re-exec with PID file at ~/.vibeproxy/vibeproxy.pid |
| Command | Description |
|---|---|
vibe init |
Generate vibeproxy.yaml, .env and update .gitignore |
vibe auth login <provider> |
Store an API key via the secret provider |
vibe start |
Start the proxy as a background daemon |
vibe start --foreground |
Start the proxy in the foreground |
vibe stop |
Stop the running proxy |
vibe status |
Show whether the proxy is running |
vibe logs [-n 20] |
Show recent audit log entries |
vibe service install |
Register as OS service (macOS launchd; auto-start on boot) |
vibe service uninstall |
Remove from OS services |
- Real API keys are stored via pluggable secret providers (default: OS keychain)
- Keys are loaded into memory on demand, resolved inline at the point of use, and never stored in request context, disk, or logs
- Dummy tokens (
vp-local-*) are static identifiers, not secrets — safe to appear in logs,.envfiles, and LLM context - Service names in tokens are validated (lowercase alphanumeric + hyphen only) to prevent log injection
- The proxy binds exclusively to localhost — no network exposure
- Path whitelisting with
path.Cleannormalization prevents access to unintended API endpoints and path traversal - Request body size is limited to 10 MB to prevent memory exhaustion
- The gateway requires a valid
vp-local-*dummy token, enforces rate limits, and writes audit logs — same security guarantees as the proxy path - CORS wildcard origin (
*) is rejected at startup to prevent browser-based attacks against localhost - PID file creation uses exclusive file locking (
O_EXCL) to prevent race conditions
- Go 1.21+
- macOS / Linux / Windows (via go-keyring)