internal/proxy
HTTP/HTTPS forward proxy with traffic inspection and request filtering.
Overview
The proxy package implements a forward proxy that intercepts HTTP and HTTPS traffic from AI agents. It works with the rules and mode packages to inspect requests and enforce blocking policies.
Types
LogEntry
Represents a logged request for audit trails.
type LogEntry struct {
Timestamp time.Time `json:"timestamp"`
AgentToken string `json:"agent_token,omitempty"`
Domain string `json:"domain"`
Method string `json:"method"`
Action string `json:"action"` // "allow", "block", or "audit"
Reason string `json:"reason,omitempty"`
}Handler
Main HTTP proxy handler. Implements http.Handler.
type Handler struct {
inspector *Inspector
client *http.Client
}Inspector
Traffic inspection component that coordinates with the rule engine and mode manager.
type Inspector struct {
engine *rules.Engine
modeManager *mode.Manager
}ExecCheckRequest / ExecCheckResponse
Request/response types for the /exec/check endpoint.
type ExecCheckRequest struct {
Command string `json:"command"`
AgentToken string `json:"agent_token,omitempty"`
}
type ExecCheckResponse struct {
Allowed bool `json:"allowed"`
Reason string `json:"reason,omitempty"`
}Functions
NewHandler
Creates a new proxy handler with the given inspector.
func NewHandler(inspector *Inspector) *HandlerThe handler configures an HTTP client with:
- 30-second timeout
- No automatic redirect following (client handles redirects)
NewInspector
Creates a new traffic inspector.
func NewInspector(engine *rules.Engine, modeManager *mode.Manager) *InspectorNewExecCheckHandler
Creates a handler for command execution checks.
func NewExecCheckHandler(inspector *Inspector) *ExecCheckHandlerInspector Methods
ExtractHost
Extracts and normalizes the domain from a request.
func (i *Inspector) ExtractHost(r *http.Request) string- Uses
Hostheader or URL host - Strips port numbers
- Returns lowercase
ExtractAgentToken
Extracts the agent token from the X-Agent-Token header.
func (i *Inspector) ExtractAgentToken(r *http.Request) stringCheckRequest
Mode-aware request checking. Returns whether to block, if a rule matched, and the reason.
func (i *Inspector) CheckRequest(r *http.Request) (shouldBlock bool, ruleMatched bool, reason string)Mode behavior:
- Enforce: Blocks if rule matched
- Audit: Never blocks, logs what would have been blocked
- Lockdown: Blocks everything
CheckDomain
Direct domain check against the rule engine (not mode-aware).
func (i *Inspector) CheckDomain(domain string) (bool, string)CheckCommand
Command pattern check against the rule engine.
func (i *Inspector) CheckCommand(command string) (bool, string)Mode / IsLockdown
Query the current mode for an agent.
func (i *Inspector) Mode(agentID string) mode.Mode
func (i *Inspector) IsLockdown(agentID string) boolHandler Behavior
HTTP Requests (non-CONNECT)
- Extract domain and agent token
- Check request against rules (mode-aware)
- Log the request (allow/block/audit)
- If blocked: return 403 Forbidden
- Forward request to upstream, stripping proxy headers
- Copy response back to client
Headers stripped before forwarding:
Proxy-ConnectionX-Agent-Token(prevents leaking to upstream)
HTTPS CONNECT Tunnels
- Extract domain and agent token
- Check request against rules (mode-aware)
- Log the request
- If blocked: return 403 Forbidden
- Connect to target host
- Hijack client connection
- Send "200 Connection Established"
- Tunnel data bidirectionally
Usage Example
// Create dependencies
engine := rules.NewEngine()
engine.LoadRules("rules.yaml")
modeManager := mode.NewManager()
// Create inspector and handler
inspector := proxy.NewInspector(engine, modeManager)
handler := proxy.NewHandler(inspector)
// Start proxy server
http.ListenAndServe(":8080", handler)Testing Example
From handler_test.go:
func testInspector(t *testing.T, rulesYAML string) *Inspector {
engine := rules.NewEngine()
if rulesYAML != "" {
if err := engine.LoadRulesFromBytes([]byte(rulesYAML)); err != nil {
t.Fatalf("failed to load rules: %v", err)
}
}
modeManager := mode.NewManager()
return NewInspector(engine, modeManager)
}
// Usage
inspector := testInspector(t, `
rules:
- id: block-evil
domain: "evil.com"
action: block
description: "Block evil domain"
enabled: true
`)
handler := NewHandler(inspector)Package Dependencies
proxy
├── rules.Engine - Rule matching
├── mode.Manager - Mode-aware blocking decisions
└── net/http - HTTP handlingLog Format
Requests are logged as JSON to stdout:
{"timestamp":"2024-01-15T10:30:00Z","agent_token":"agent-123","domain":"example.com","method":"GET","action":"allow","reason":""}Actions:
allow– Request permittedblock– Request blockedaudit– Would have been blocked, but in audit mode