Version: 1.0 Date: 2025-12-11 Status: Production
- Executive Summary
- Architecture Overview
- Core Components
- Plugin System
- Data Flow
- Plugin Implementations
- Quality Attributes
- Design Patterns
- Deployment & Operations
- Future Extensions
The CodeMie Proxy is a plugin-based HTTP streaming proxy that sits between AI coding agents and their target API endpoints. It enables:
- SSO Authentication: Automatic cookie injection for enterprise SSO
- MCP Authorization: OAuth proxy for remote MCP servers with SSRF protection
- Header Management: CodeMie-specific header injection for traceability
- Observability: Detailed logging and metrics collection
- Metrics Sync: Background sync of session metrics to CodeMie API
- Desktop Telemetry: Local Claude Desktop 3P transcript discovery and conversation sync when daemon mode is enabled
- VS Code BYOK: Profile-configured OpenAI-compatible custom endpoints with transparent forwarding
- Extensibility: Plugin architecture for future features
- ✅ KISS (Keep It Simple): Core does ONE thing - forwards HTTP with streaming
- ✅ SOLID: Single Responsibility, Open/Closed via plugins, Dependency Injection
- ✅ Zero Buffering: True HTTP streaming with no body buffering
- ✅ Plugin-Based: Core is stable, features added via plugins
- ✅ Fail-Safe: Plugin failures don't break proxy flow
┌─────────────────────────────────────────────────────────────────┐
│ AI Coding Agent │
│ (claude, gemini, etc.) │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP Request
▼
┌─────────────────────────────────────────────────────────────────┐
│ CodeMie Proxy │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Plugin System (Priority-Based) │ │
│ │ │ │
│ │ [3] MCP Auth Plugin → MCP OAuth proxy & URL rewrite│ │
│ │ [10] SSO Auth Plugin → Inject cookies │ │
│ │ [20] Header Injection → Add X-CodeMie headers │ │
│ │ [50] Logging Plugin → Log requests/responses │ │
│ │ [100] Metrics Sync Plugin → Background metrics sync │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ HTTP Streaming Core │ │
│ │ • Build context │ │
│ │ • Run onRequest hooks │ │
│ │ • Forward to upstream (no buffering) │ │
│ │ • Run onResponseHeaders hooks │ │
│ │ • Stream response chunks (with optional transform) │ │
│ │ • Run onResponseComplete hooks │ │
│ └────────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP Request (modified)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Target API Endpoint │
│ (OpenAI, Anthropic, CodeMie SSO, LiteLLM, etc.) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1: Entry Point (CodeMieProxy class) │
│ • HTTP server management │
│ • Port binding and error handling │
│ • Top-level error handler │
└─────────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ Layer 2: Plugin Management (PluginRegistry) │
│ • Plugin registration and initialization │
│ • Priority-based sorting │
│ • Lifecycle management (enable/disable) │
└─────────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ Layer 3: Request Handling │
│ • Context building │
│ • Hook orchestration (onRequest, onResponseHeaders, etc.) │
│ • Error handling with plugin hooks │
└─────────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ Layer 4: HTTP Forwarding (ProxyHTTPClient) │
│ • Upstream connection management │
│ • True HTTP streaming (no buffering) │
│ • SSL/TLS handling │
└─────────────────────────────────────────────────────────────────┘
Location: src/utils/codemie-proxy.ts
Responsibilities:
- HTTP server lifecycle (start/stop)
- Request routing to plugins
- Error handling and recovery
- Port management (dynamic allocation)
Key Operations:
- Initialize plugins and start server
- Graceful shutdown with plugin cleanup
- Main request handler
- Streaming response handler
Configuration Parameters:
- Target API URL (upstream endpoint)
- Local port (0 = dynamic allocation)
- Client type (agent identifier)
- Timeout duration
- Profile name, provider, model
- Session ID
Location: src/proxy/plugins/registry.ts
Responsibilities:
- Plugin registration and storage
- Dependency resolution
- Priority-based sorting (0-1000)
- Lifecycle hook invocation
Key Operations:
- Register plugins at startup
- Initialize plugins with context
- Enable/disable plugins at runtime
- Retrieve plugin configurations
Plugin Priority Levels:
- 0-3: MCP protocol handling (MCP Auth: 3)
- 4-10: Authentication and security (SSO Auth: 10)
- 11-50: Header manipulation (Header Injection: 20)
- 51-100: Observability (Logging: 50, Metrics Sync: 100)
- 101-500: Business logic (rate limiting, caching)
- 501-1000: Post-processing (analytics, reporting)
Location: src/proxy/http-client.ts
Responsibilities:
- HTTP/HTTPS forwarding with streaming
- Connection pooling
- Timeout management
- SSL/TLS certificate handling
Features:
- Zero buffering (streams directly)
- Async iteration over response chunks
- Custom SSL/TLS options (self-signed certs)
- Configurable timeouts
Location: src/proxy/types.ts
Purpose: Shared state across all plugin hooks for a single request
Context Attributes:
- Identity: Request ID, Session ID, Agent name
- Traceability: Profile, Provider, Model
- Request Details: Method, URL, Headers, Body
- Timing: Request start time
- Upstream: Target URL
- Extensibility: Metadata dictionary for plugin-specific data
Design Pattern: Chain of Responsibility + Observer
Plugin Interface:
- Metadata: ID, name, version, priority, dependencies
- Factory Method: Creates interceptor instance with context
- Lifecycle Hooks: Install, uninstall, enable, disable
Interceptor Interface:
- Proxy Lifecycle: onProxyStart, onProxyStop
- Request Lifecycle: onRequest, onResponseHeaders, onResponseChunk, onResponseComplete, onError
┌──────────────────────────────────────────────────────────────┐
│ Application Startup │
│ ├─ Import: src/proxy/plugins/index.ts │
│ ├─ Auto-register: registerCorePlugins() │
│ └─ Plugins registered in PluginRegistry │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Proxy Start (per session) │
│ ├─ CodeMieProxy.start() called │
│ ├─ Build PluginContext (config, credentials) │
│ ├─ PluginRegistry.initialize(context) │
│ │ ├─ Filter enabled plugins │
│ │ ├─ Sort by priority │
│ │ ├─ Call createInterceptor() for each │
│ │ └─ Return sorted interceptor list │
│ └─ Call onProxyStart() on all interceptors │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Request Handling (per request) │
│ ├─ Build ProxyContext │
│ ├─ onRequest() hooks (all interceptors) │
│ ├─ Forward to upstream │
│ ├─ onResponseHeaders() hooks │
│ ├─ Stream response with onResponseChunk() hooks │
│ └─ onResponseComplete() hooks │
└──────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Proxy Stop (per session) │
│ ├─ CodeMieProxy.stop() called │
│ ├─ Call onProxyStop() on all interceptors │
│ └─ Cleanup resources │
└──────────────────────────────────────────────────────────────┘
Auto-Registration:
- Plugins register themselves on module import
- Core plugins registered in central index file
- Registry maintains plugin instances and configurations
Manual Registration:
- Runtime registration for conditional plugins
- Used for feature flags or environment-specific plugins
Fail-Safe Design: Plugin errors don't break proxy flow
Error Handling Strategy:
- Try-catch wrapper around all plugin hooks
- Errors logged for debugging
- Execution continues with remaining interceptors
- Graceful degradation ensures proxy availability
Benefits:
- One misbehaving plugin doesn't crash the proxy
- Full error context captured in logs
- System remains operational under failure conditions
Client → Proxy → Upstream → Proxy → Client
Detailed Flow:
1. Agent sends HTTP request to localhost:PORT
2. Proxy receives request
├─ Build ProxyContext (requestId, sessionId, headers, body)
├─ Run onRequest() hooks (priority order)
│ ├─ SSOAuthPlugin: Inject cookies
│ ├─ HeaderInjectionPlugin: Add X-CodeMie headers
│ └─ LoggingPlugin: Log request
├─ Build target URL (targetApiUrl + request path)
└─ Forward to upstream via ProxyHTTPClient
3. Upstream responds
├─ Receive response headers
├─ Run onResponseHeaders() hooks
│ └─ LoggingPlugin: Log response headers
└─ Start streaming response body
4. Stream response chunks
├─ For each chunk from upstream:
│ ├─ Run onResponseChunk() hooks (optional transform)
│ └─ Write chunk to client immediately
└─ End stream
5. Response complete
├─ Run onResponseComplete() hooks
│ ├─ LoggingPlugin: Log final stats
│ └─ MetricsSyncPlugin: No-op (runs on timer)
└─ Close connection
Client → Proxy → Error → Proxy → Client
Error Handling:
1. Error occurs (network, timeout, upstream error)
2. Proxy catches error
├─ Check if client disconnected (abort error)
│ └─ If yes: Log and exit silently
├─ Build minimal ProxyContext
├─ Run onError() hooks on all interceptors
│ └─ LoggingPlugin: Log error details
├─ Normalize error (NetworkError, TimeoutError, etc.)
└─ Send JSON error response to client
3. Client receives structured error with:
- Error type and message
- HTTP status code
- Request ID
- Timestamp
Upstream Response → Proxy → Client (no intermediate buffering)
Streaming Strategy:
- Upstream is Node.js stream (IncomingMessage)
- Async iteration over chunks
- Optional transformation via plugin hooks
- Immediate write to client (no accumulation)
- Constant memory footprint
Benefits:
- ~90% less memory usage (no buffering)
- Constant memory regardless of response size
- True streaming for SSE and long responses
- Real-time data delivery
Priority: 10 (must run first)
File: src/proxy/plugins/sso-auth.plugin.ts
Purpose: Inject SSO cookies into requests for enterprise authentication
Behavior:
- Reads cookies from PluginContext credentials
- Builds Cookie header from key-value pairs
- Only runs when SSO credentials present
- Executes in onRequest() hook
Architecture:
- Single responsibility: Cookie injection
- No state maintained between requests
- Fails if credentials missing
Priority: 20
File: src/proxy/plugins/header-injection.plugin.ts
Purpose: Add CodeMie-specific headers for traceability
Headers Injected:
Always Injected:
X-CodeMie-CLI: CLI wrapper and version (e.g.,codemie-cli/0.0.16)X-CodeMie-Client: Agent identifier (e.g.,codemie-claude,codemie-gemini,codemie-code)X-CodeMie-Request-ID: Request UUID for traceabilityX-CodeMie-Session-ID: Session UUID for correlation
Conditionally Injected:
X-CodeMie-Integration: Integration ID (only when provider requires integration viarequiresIntegrationflag)X-CodeMie-CLI-Model: Model name from config (if configured)X-CodeMie-CLI-Timeout: Timeout value from config (if configured)
Architecture:
- Reads values from PluginContext and ProxyContext
- Adds headers to outgoing request
- Executes in onRequest() hook
- Fails gracefully if values are missing (optional headers)
Priority: 50
File: src/proxy/plugins/logging.plugin.ts
Purpose: Log detailed proxy activity
Log Destinations: ~/.codemie/logs/debug-YYYY-MM-DD.log
Log Level: DEBUG (file only, console when CODEMIE_DEBUG=1)
Lifecycle Hooks:
- onRequest: Log request details (method, URL, headers, body size)
- onResponseHeaders: Log response headers (content-type, encoding)
- onResponseChunk: Log streaming progress (every 10th chunk)
- onResponseComplete: Log final stats (status, duration, bytes sent)
- onError: Log error details (type, message, stack trace)
Architecture:
- Stateless within single request
- Maintains chunk counter for sampling
- No impact on proxy performance (async logging)
Priority: 100
File: src/proxy/plugins/metrics-sync.plugin.ts
Purpose: Background sync of session metrics to CodeMie API
Design Decisions:
- ✅ Aggregation over Granularity: Multiple deltas aggregated into single metric per sync
- ✅ Single Metric Sync: API receives one aggregated metric object, not array
- ✅ Session-Level Sync: Plugin is session-scoped (syncs only current session)
- ✅ In-Place Marking: Sync status tracked directly in JSONL file
- ✅ SSO-Only Operation: Only runs when provider is
ai-run-sso - ✅ Cookie Authentication: Uses SSO cookies from proxy context
Lifecycle:
Proxy Start
└─ onProxyStart()
├─ Initialize MetricsApiClient
├─ Start background timer (every 5 minutes)
└─ Log: "Starting metrics sync"
Background Timer (every 5 minutes)
└─ syncMetrics()
├─ Read {sessionId}_metrics.jsonl
├─ Filter deltas with syncStatus='pending'
├─ Load session metadata
├─ Aggregate deltas → single metric
├─ POST ${apiUrl}/metrics
├─ On success: Mark deltas as 'synced' in JSONL
└─ Log: "Synced N deltas"
Proxy Stop
└─ onProxyStop()
├─ Stop background timer
├─ Final sync (ensures all pending deltas sent)
└─ Log: "Final sync completed"
When the proxy daemon is started in Desktop mode, the daemon also starts a local telemetry runtime for Claude Desktop 3P:
- Discovers session metadata under
~/Library/Application Support/Claude-3p/local-agent-mode-sessions/ - Reads sibling
audit.jsonltranscripts for each detectedlocal_<session>directory - Correlates each local Desktop session to a CodeMie session stored in
~/.codemie/sessions/ - Normalizes Desktop events into the existing Claude metrics/conversation processors
- Syncs pending JSONL metrics and conversations through
SessionSyncer - Sends session lifecycle metrics with client identity
claude-desktop
This path is intentionally separate from the hook-based codemie-claude flow. Claude Desktop does not expose CodeMie-managed lifecycle hooks, so ingestion is file-discovery driven rather than event-callback driven. The shared runtime is generic; client-specific logic lives behind a Desktop adapter so future IDE or desktop clients can plug into the same sync pipeline.
Components:
- MetricsSyncPlugin: Plugin registration and initialization
- MetricsSyncInterceptor: Interceptor with timer and sync logic
- MetricsApiClient: HTTP client for API communication
- Aggregation Logic: Combines deltas into session metric
- JSONL Utilities: Atomic file operations
Endpoint: POST ${apiUrl}/metrics
Example: POST https://codemie.ai/metrics
Content-Type: application/json
Auth: Cookie: session={token} (SSO cookies)
Metric Structure:
- metric_name: Always
codemie_coding_agent_usage - attributes: Session-aggregated metrics
- time: ISO timestamp
Metric Attributes:
- Identity: agent, agent_version, llm_model, project, session_id
- Interaction: total_user_prompts, total_ai_requests, total_ai_responses
- Tokens: total_input_tokens, total_output_tokens, total_cache_read_input_tokens
- Tools: total_tool_calls, successful_tool_calls, failed_tool_calls
- Files: files_created, files_modified, files_deleted, lines_added, lines_removed
- Session: session_duration_ms, exit_reason, had_errors, status, is_final, count
Response Structure:
- success: Boolean flag
- received: Number of metrics received
- processed: Number successfully processed
- failed: Number of failures
- timestamp: Server timestamp
Local Metrics Storage: ~/.codemie/sessions/
{sessionId}.json: Session metadata{sessionId}_metrics.jsonl: Delta records (one per line)
Delta Lifecycle:
- MetricsOrchestrator writes delta with syncStatus='pending'
- MetricsSyncPlugin reads all pending deltas periodically (every 5 minutes)
- All pending deltas aggregated into single session metric
- Single metric sent to API as JSON object
- On success: All aggregated deltas marked with syncStatus='synced'
Sync Algorithm:
- Read all deltas from JSONL file
- Filter for syncStatus='pending' only
- Load session metadata
- Aggregate pending deltas into single session metric (sum tokens, count tools, calculate duration)
- POST single metric to API with SSO cookies
- On success: Mark all aggregated deltas as 'synced' with atomic JSONL rewrite
- On failure: Retry with exponential backoff, keep deltas as 'pending'
Priority: Environment Variables > Profile Config > Default (true)
Environment Variables:
CODEMIE_METRICS_SYNC_ENABLED: Enable/disable sync (default: true for SSO)CODEMIE_METRICS_SYNC_INTERVAL: Sync interval in milliseconds (default: 300000)CODEMIE_METRICS_MAX_RETRIES: Max retry attempts (default: 3)
Profile Configuration:
- Location:
~/.codemie/codemie-cli.config.json - Path:
profiles[name].metrics.sync - Properties: enabled, interval, maxRetries
Opt-Out Options:
- Single session: Set env var to false
- Profile-wide: Disable in profile config
Retryable Errors (exponential backoff: 1s → 2s → 5s):
- Network timeouts
- 5xx server errors
- 429 Rate limiting
- Connection refused
Non-Retryable Errors (fail immediately):
- 401 Unauthorized (SSO session expired)
- 403 Forbidden (insufficient permissions)
- 400 Bad Request (invalid payload)
Failure Strategy:
- On retry exhaustion: Keep deltas as 'pending'
- Next sync cycle retries automatically
- Errors logged at ERROR level
- Plugin failure doesn't break proxy
Concurrency Protection:
- isSyncing flag prevents concurrent syncs
- Timer skips if sync already in progress
- Serial processing guaranteed
Memory: ~5MB for plugin (within proxy process) Disk I/O: O(1) - single session file read/write Network: ~1KB per sync (single aggregated metric) CPU: Minimal (simple arithmetic aggregation)
Scalability:
- Session-scoped: Only syncs current session
- No cross-session interference
- Timer-based: Predictable resource usage
Log Location: ~/.codemie/logs/debug-YYYY-MM-DD.log
Log Events:
- Starting metrics sync (with interval)
- Syncing N pending deltas
- Successfully synced N deltas
- Stopping metrics sync
- Final sync completed
- Sync failures with error details
Troubleshooting:
- Check syncStatus in JSONL file (pending/synced)
- Verify SSO cookies valid
- Check network connectivity to API
- Enable debug logging
Priority: 3 (runs before all other plugins)
File: src/providers/plugins/sso/proxy/plugins/mcp-auth.plugin.ts
Purpose: Proxy MCP OAuth authorization flows through the CodeMie proxy so that all auth traffic is routed centrally and client_name can be overridden via the MCP_CLIENT_NAME environment variable.
The plugin intercepts two URL patterns:
| Route | Pattern | Purpose |
|---|---|---|
| Initial | /mcp_auth?original=<url> |
First MCP connection — starts an OAuth flow |
| Relay | /mcp_relay/<root_b64>/<relay_b64>/<path> |
Subsequent requests routed through proxy |
root_b64: Base64url-encoded root MCP server origin (for per-flow isolation)relay_b64: Base64url-encoded actual target origin (may differ when auth server is on a separate host)
/mcp_auth route:
- Extract
originalquery parameter (the real MCP server URL) - Validate URL (SSRF check)
- Forward request to the target MCP server
- Buffer the JSON response and rewrite all discovered URLs to proxy relay URLs
- Return the rewritten response to the MCP client
/mcp_relay route:
- Decode
root_b64andrelay_b64to recover target origin - Validate root-relay association (per-flow origin scoping)
- Reconstruct the full target URL from relay origin + path + query
- Forward request to the real target
- Buffer JSON auth metadata responses and rewrite URLs; stream all other responses
The plugin buffers JSON responses (auth metadata, client registration, etc.) and rewrites all absolute HTTP(S) URLs found in JSON values to proxy relay URLs. This ensures the MCP client routes all subsequent requests through the proxy.
Exceptions: Token audience identifiers (e.g., resource field) are not rewritten — they are logical identifiers, not URLs to access.
Browser endpoints (e.g., authorization_endpoint) are left as-is so the user's browser navigates directly to the auth server.
SSRF Protection:
- Private/loopback IP addresses are rejected (both literal hostname check and DNS resolution)
- Only
http:andhttps:schemes are allowed
Per-Flow Origin Scoping:
- Discovered origins (from auth metadata) are tagged with their root MCP server origin
- Relay requests validate that the relay origin is associated with the claimed root origin
- Prevents cross-flow origin confusion
Buffering Policy:
- Only auth metadata responses are buffered (for URL rewriting)
- Post-auth MCP traffic streams through without buffering
The MCP Auth Plugin works in conjunction with the stdio-to-HTTP bridge:
| Component | File | Purpose |
|---|---|---|
| Stdio-HTTP Bridge | src/mcp/stdio-http-bridge.ts |
Bridges stdio JSON-RPC to streamable HTTP transport |
| OAuth Provider | src/mcp/auth/mcp-oauth-provider.ts |
Implements OAuthClientProvider for browser-based OAuth flow |
| Callback Server | src/mcp/auth/callback-server.ts |
Ephemeral localhost server for receiving OAuth callbacks |
| Proxy Logger | src/mcp/proxy-logger.ts |
File-based logger for proxy operations |
| Constants | src/mcp/constants.ts |
MCP_CLIENT_NAME default and accessor |
Environment Variables:
MCP_CLIENT_NAME: Client name for OAuth Dynamic Client Registration (default:CodeMie CLI)MCP_PROXY_DEBUG: Enable verbose proxy loggingCODEMIE_PROXY_PORT: Fixed proxy port (for stable MCP auth URLs across restarts)
Log Location: ~/.codemie/logs/mcp-proxy.log
codemie proxy connect vscode resolves one effective CodeMie profile before configuring the client. The selected profile's model is written directly to VS Code's models[].id, which is the identifier VS Code sends in inference requests. The profile's codeMieProject is passed independently to the daemon for X-CodeMie-Project header injection.
The persistent daemon never receives a configured model and does not rewrite request bodies. It validates the local gateway key, injects SSO authentication and CodeMie context headers, then forwards request and response bodies through the existing streaming path byte-for-byte.
The command reuses a healthy daemon when its profile, project, provider, target URL, and vscode-byok client type match. Model changes only rewrite VS Code configuration; they do not restart the daemon.
The connector merges one managed model into VS Code's chatLanguageModels.json and preserves unrelated models plus an existing ${input:chat.lm.secret.*} reference as apiKey. If no valid reference exists, it omits apiKey rather than generating a placeholder and directs the user to open Chat: Manage Language Models, right-click CodeMie Profile Model, and choose Update API Key. VS Code then stores the local codemie-proxy key in secret storage; CodeMie SSO credentials never enter VS Code configuration.
GPT-5.5 and all three GPT-5.6 entries use /v1/responses with
zeroDataRetentionEnabled: true, thinking: true, and Responses-format reasoning efforts. VS
Code owns the complete conversation history for these entries and sends stateless requests with
store: false, no previous_response_id, and replayed assistant, function-call, and
function-call-output items. This keeps LiteLLM free to load-balance each request across Azure
deployments.
The proxy performs only bounded compatibility normalization for vscode-byok: it constrains the
Responses user identifier and removes deployment-bound encrypted reasoning state. It preserves
the selected reasoning.effort, visible messages, assistant phases, tools, call IDs, and tool
outputs. The proxy does not persist conversation content, add session affinity, buffer Responses
events, or transform the SSE stream. Removing encrypted state trades hidden-reasoning continuity
for deployment-independent follow-ups, matching the established Codex compatibility behavior.
sequenceDiagram
participant VS as VS Code Agent
participant PX as CodeMie Proxy
participant GW as CodeMie Gateway
participant LM as Profile Model
VS->>PX: POST /v1/responses<br/>store=false<br/>input=full local history<br/>Bearer local gateway key
PX->>PX: Validate and strip local key
PX->>PX: Inject CodeMie SSO cookies
PX->>PX: Inject profile/project context headers
PX->>PX: Normalize user and strip encrypted reasoning state
PX->>GW: Forward stateless request
GW->>LM: Invoke configured model
LM-->>GW: Streaming events / tool calls
GW-->>PX: SSE stream
PX-->>VS: Byte-preserved SSE stream
Streaming: Zero buffering, constant memory
- Before: ~100MB memory for 10MB response (buffered)
- After: ~10MB memory for 10MB response (streamed)
Throughput: No artificial limits
- Limited only by network and upstream API
- No CPU-intensive operations in hot path
Latency: Minimal overhead
- Plugin hooks: ~1-5ms per request
- Streaming: No added latency (pass-through)
Concurrency: Multi-request support
- Node.js event loop handles concurrent requests
- No blocking operations in request path
Fail-Safe: Plugin failures don't break proxy
- Try-catch around all plugin hooks
- Log errors and continue
Graceful Shutdown: Clean resource cleanup
- Call onProxyStop() on all plugins
- Final sync operations complete
- HTTP server closes gracefully
Error Recovery: Structured error responses
- Normalized error types
- Actionable error messages
- Full error context in logs
SOLID Principles:
- Single Responsibility: Core = forward HTTP, Plugins = features
- Open/Closed: Add features via plugins without modifying core
- Liskov Substitution: All plugins implement same interface
- Interface Segregation: Optional hooks (only implement what you need)
- Dependency Inversion: Core depends on plugin abstractions
Code Organization:
src/proxy/
├── errors.ts # Error types
├── http-client.ts # HTTP forwarding
├── types.ts # Core types
└── plugins/
├── index.ts # Plugin registration
├── registry.ts # Plugin management
├── types.ts # Plugin interfaces
├── sso-auth.plugin.ts
├── header-injection.plugin.ts
├── logging.plugin.ts
└── metrics-sync.plugin.ts
Authentication: SSO cookie handling
- Cookies never logged (sanitized)
- Secure credential storage (CredentialStore)
- Encrypted at rest
TLS/SSL: Support for self-signed certs
- rejectUnauthorized option configurable
- Allows enterprise CA certificates
Input Validation: Header sanitization
- Remove Host and Connection headers
- Validate proxy configuration
Audit Trail: Full request logging
- Request ID for tracing
- Session ID for correlation
- Detailed logs for forensics
Plugin System: Add features without core changes
Extension Points:
- onProxyStart: Initialization tasks, background services
- onRequest: Request modification, authentication, validation
- onResponseHeaders: Header inspection, caching decisions
- onResponseChunk: Streaming transformation, filtering
- onResponseComplete: Analytics, logging, cleanup
- onError: Error handling, alerting, recovery
Future Plugin Examples:
- Rate Limiting: Per-session request throttling
- Caching: LRU cache with TTL expiration
- Request Replay: Store/retry failed requests
- Content Transformation: Request/response body modification
Pattern: Plugins form a chain of handlers Implementation: PluginRegistry + ProxyInterceptor hooks Benefit: Add/remove handlers without modifying core
Pattern: Plugins observe proxy events Implementation: Lifecycle hooks (onRequest, onResponseHeaders, etc.) Benefit: Decoupled event handling
Pattern: Different plugin implementations for same interface Implementation: All plugins implement ProxyPlugin Benefit: Swap implementations at runtime
Pattern: createInterceptor() method Implementation: Each plugin creates its interceptor Benefit: Encapsulate interceptor creation logic
Pattern: Single PluginRegistry instance Implementation: getPluginRegistry() function Benefit: Centralized plugin management
Pattern: Core defines request handling flow, plugins fill in steps Implementation: handleRequest() method with hook call-outs Benefit: Consistent flow, customizable steps
1. Agent CLI starts
└─ codemie-claude "implement feature" --provider ai-run-sso
2. Agent detects SSO provider
└─ Checks if proxy is needed
3. Agent spawns proxy
├─ Create ProxyConfig (targetApiUrl, sessionId, etc.)
├─ new CodeMieProxy(config)
└─ await proxy.start()
├─ Load SSO credentials
├─ Initialize plugins
├─ Call onProxyStart() hooks
└─ Bind to dynamic port
4. Proxy returns URL
└─ http://localhost:54321
5. Agent uses proxy URL
└─ Set environment variable: ANTHROPIC_BASE_URL=http://localhost:54321
6. Agent runs normally
└─ All API requests go through proxy
1. User exits agent (Ctrl+C or normal exit)
2. Agent cleanup
├─ Signal proxy to stop
└─ await proxy.stop()
├─ Call onProxyStop() hooks
│ └─ MetricsSyncPlugin: Final sync
├─ Close HTTP server
└─ Cleanup HTTP client
3. Agent exits
Programmatic Configuration:
- Target API URL
- Port (0 = dynamic)
- Client type
- Session ID
- Profile, provider, model
Environment Variables (plugin-specific):
- CODEMIE_METRICS_SYNC_ENABLED
- CODEMIE_METRICS_SYNC_INTERVAL
- CODEMIE_DEBUG
Profile Configuration (plugin-specific):
- Location: ~/.codemie/codemie-cli.config.json
- Provider-specific settings
- Metrics sync configuration
Priority: Environment variables > Profile config > Defaults
Log Files: ~/.codemie/logs/debug-YYYY-MM-DD.log
Log Levels:
- ERROR: Plugin failures, network errors
- WARN: Retry attempts, deprecated features
- INFO: Plugin initialization, sync operations
- DEBUG: All proxy activity (file only)
Metrics (via Metrics Sync Plugin):
- Request count per session
- Token usage
- Tool calls
- File operations
- Session duration
Health Indicators:
- Proxy responds to requests
- Plugins loaded successfully
- No critical errors in logs
Problem: Proxy not starting Diagnosis: Check logs for port binding errors Solution: Use dynamic port (port: 0)
Problem: SSO auth failing Diagnosis: Check if credentials exist Solution: Re-authenticate with auth command
Problem: Metrics not syncing Diagnosis: Check plugin enabled in config Solution: Enable in profile or env var
Problem: Plugin error breaking proxy Diagnosis: Check logs for plugin name Solution: Plugins are fail-safe - verify implementation
Request Caching (Priority: 90):
- LRU cache for identical requests
- TTL-based expiration
- Cache invalidation API
Rate Limiting (Priority: 15):
- Per-session rate limits
- Token bucket algorithm
- Configurable limits per profile
Request Replay (Priority: 110):
- Store failed requests
- Automatic retry on recovery
- Persistence across restarts
Request/Response Transformation (Priority: 25):
- Modify request body (add system prompts)
- Filter response content (PII redaction)
- Format transformation (OpenAI → Anthropic)
Current Limitation: Single process, single session
Future Enhancement: Multi-session support
- Session routing via header
- Per-session plugin context
- Shared cache across sessions
Load Balancing: Multiple upstream targets
- Round-robin routing
- Health checks
- Failover logic
Distributed Tracing: OpenTelemetry integration
- Trace ID propagation
- Span creation for plugin hooks
- Export to observability platforms
Vision: Community-contributed plugins
Requirements:
- Plugin validation (security, performance)
- Versioning and compatibility checks
- Documentation standards
- Distribution via npm
Example Third-Party Plugins:
- Advanced analytics with custom metrics
- Record/replay for debugging
- Security scanning for vulnerabilities
- Request/response transformation
End of Document