Documentation
¶
Overview ¶
Package aiproxy is an authenticated reverse proxy for OpenAI-compatible APIs and MCP servers (streamable HTTP). Upstream URLs and keys stay on the server; clients authenticate with the normal security layer and call the proxy instead.
p := aiproxy.New(aiproxy.Config{Prefix: "/ai"})
p.RegisterOpenAI(aiproxy.OpenAIUpstream{Upstream: aiproxy.Upstream{Name: "gpt", BaseURL: "https://api.openai.com/v1", APIKey: key}})
p.RegisterMCP(aiproxy.MCPUpstream{Upstream: aiproxy.Upstream{Name: "tools", BaseURL: "http://localhost:3000/mcp", APIKey: key}})
mux.Handle("/ai/", p.Handler(security.NewAuthMiddleware(securityList)))
Client routes: {prefix}/{name}/v1/... (OpenAI) and {prefix}/{name} (MCP).
Index ¶
- Constants
- type AuditRecord
- type AuditSink
- type Config
- type Definition
- type HookContext
- type HookFunc
- type HookRegistry
- type HookType
- type Kind
- type LogAuditSink
- type MCPUpstream
- type NopAuditSink
- type OpenAIUpstream
- type Outcome
- type ProcStore
- type Proxy
- func (p *Proxy) AutoReload(ctx context.Context, store UpstreamStore, interval time.Duration) error
- func (p *Proxy) Handler(auth func(http.Handler) http.Handler) http.Handler
- func (p *Proxy) Hooks() *HookRegistry
- func (p *Proxy) Names() []string
- func (p *Proxy) Register(d Definition) error
- func (p *Proxy) RegisterMCP(u MCPUpstream) error
- func (p *Proxy) RegisterOpenAI(u OpenAIUpstream) error
- func (p *Proxy) Reload(ctx context.Context, store UpstreamStore) error
- func (p *Proxy) Unregister(name string) bool
- type RateLimit
- type Upstream
- type UpstreamStore
- type Usage
Constants ¶
const DefaultProc = "resolvespec_ai_proxies"
DefaultProc is the stored procedure (function) ProcStore calls when no name is given.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuditRecord ¶
type AuditRecord struct {
Time time.Time
UserID int
User string
RemoteID string
Upstream string
Kind Kind
Method string
Path string // client path, no query string
Model string
Tools []string
Status int
Outcome Outcome
Reason string // why a request was refused
Duration time.Duration
Usage Usage
Error string
}
AuditRecord is written once per handled request. It holds no bodies and no keys.
type AuditSink ¶
type AuditSink interface {
Record(AuditRecord)
}
AuditSink receives audit records. It runs on the request path, so keep it fast (queue slow work such as database writes).
type Config ¶
type Config struct {
// Prefix the handler is mounted under, e.g. "/ai". Empty when mounted with http.StripPrefix.
Prefix string
// MaxBodyBytes caps JSON request bodies that are inspected. Default 10 MB.
MaxBodyBytes int64
// StripHeaders are removed from client requests in addition to the defaults
// (Authorization, Cookie, Proxy-Authorization, X-Api-Key).
StripHeaders []string
// Audit receives one record per request. Default: LogAuditSink. Use NopAuditSink to disable.
Audit AuditSink
}
Config configures a Proxy.
type Definition ¶
type Definition struct {
Kind Kind
Upstream Upstream
// Allowed holds AllowedModels (OpenAI) or AllowedTools (MCP).
Allowed []string
}
Definition describes one upstream independent of how it was registered.
type HookContext ¶
type HookContext struct {
Context context.Context
Request *http.Request // BeforeProxy may modify headers/query; the key is injected later
UserContext *security.UserContext
Upstream string
Kind Kind
Model string // OpenAI: requested model
Tools []string // MCP: tools named by tools/call
// AfterProxy only
StatusCode int
Duration time.Duration
Usage Usage // OpenAI only; needs stream_options.include_usage for streams
Error error
// BeforeProxy only
Abort bool
AbortMessage string
AbortCode int // default 403
}
HookContext is passed to hooks.
type HookFunc ¶
type HookFunc func(*HookContext) error
HookFunc is a hook. A returned error aborts a BeforeProxy request.
type HookRegistry ¶
type HookRegistry struct {
// contains filtered or unexported fields
}
HookRegistry holds hooks per type.
func NewHookRegistry ¶
func NewHookRegistry() *HookRegistry
NewHookRegistry creates an empty registry.
func (*HookRegistry) Execute ¶
func (r *HookRegistry) Execute(t HookType, ctx *HookContext) error
Execute runs the hooks in order and stops at the first error or abort.
func (*HookRegistry) Register ¶
func (r *HookRegistry) Register(t HookType, h HookFunc)
Register adds a hook.
type LogAuditSink ¶
type LogAuditSink struct{}
LogAuditSink writes records through pkg/logger. It is the default sink.
type MCPUpstream ¶
type MCPUpstream struct {
Upstream
// AllowedTools restricts tools/call to these tool names. Empty means any.
AllowedTools []string
}
MCPUpstream is an MCP server speaking streamable HTTP.
type NopAuditSink ¶
type NopAuditSink struct{}
NopAuditSink discards records (use it to switch the default logging off).
type OpenAIUpstream ¶
type OpenAIUpstream struct {
Upstream
// AllowedModels restricts the "model" a request may use. Empty means any.
// When set, write requests must be JSON bodies carrying an allowed model.
AllowedModels []string
}
OpenAIUpstream is an OpenAI-compatible API (OpenAI, Azure, Ollama, vLLM, ...).
type ProcStore ¶
type ProcStore struct {
// contains filtered or unexported fields
}
ProcStore loads upstream definitions from a stored procedure (PostgreSQL), using the same contract as the security procedures:
SELECT p_success, p_error, p_data FROM resolvespec_ai_proxies()
p_data is a JSON array; each element has: name, kind ("openai"|"mcp"), base_url, and optionally api_key, auth_header, auth_format, headers (object), allowed_roles (array), allowed (array: models or tools), rate_per_second, rate_burst, timeout_seconds, enabled (default true). See resolvespec_ai_proxies.sql.
func NewProcStore ¶
NewProcStore calls proc through db. An empty proc means DefaultProc.
func NewProcStoreFromDatabase ¶
NewProcStoreFromDatabase is NewProcStore for an application's common.Database. The connection is fetched on every call, so adapter reconnects are followed.
type Proxy ¶
type Proxy struct {
// contains filtered or unexported fields
}
Proxy routes authenticated requests to registered upstreams.
func (*Proxy) AutoReload ¶
AutoReload calls Reload now and then every interval until ctx ends. The first error is returned; later ones are logged.
func (*Proxy) Handler ¶
Handler returns the proxy handler. auth is the authentication middleware, normally security.NewAuthMiddleware(securityList). It is required: without it every request is refused.
func (*Proxy) Register ¶
func (p *Proxy) Register(d Definition) error
Register registers an upstream from a Definition. The name must be unused.
func (*Proxy) RegisterMCP ¶
func (p *Proxy) RegisterMCP(u MCPUpstream) error
RegisterMCP registers an MCP server (streamable HTTP).
func (*Proxy) RegisterOpenAI ¶
func (p *Proxy) RegisterOpenAI(u OpenAIUpstream) error
RegisterOpenAI registers an OpenAI-compatible upstream.
func (*Proxy) Reload ¶
func (p *Proxy) Reload(ctx context.Context, store UpstreamStore) error
Reload syncs the store-managed upstreams with the store.
- New definitions are added, changed ones replaced, unchanged ones left alone (their rate-limit state and connections survive), missing ones removed.
- Upstreams registered in code are never replaced or removed; a stored definition with the same name is skipped and reported.
- Invalid definitions are skipped and reported in the returned error; valid ones are still applied. If the store itself fails, nothing changes.
func (*Proxy) Unregister ¶
Unregister removes an upstream (code-registered or loaded). It reports whether it existed. In-flight requests finish normally.
type RateLimit ¶
type RateLimit struct {
PerSecond float64 // sustained requests per second
Burst int // bucket size; defaults to ceil(PerSecond), min 1
}
RateLimit limits requests per user per upstream (token bucket).
type Upstream ¶
type Upstream struct {
// Name is the route segment clients use: {prefix}/{Name}/...
Name string
// BaseURL of the upstream. OpenAI: include the version path (https://api.openai.com/v1).
// MCP: the full streamable HTTP endpoint (http://localhost:3000/mcp).
BaseURL string
// APIKey is sent to the upstream. Never exposed to clients or logs.
APIKey string
// AuthHeader carries the key. Default "Authorization". Applies to OpenAI and MCP upstreams.
AuthHeader string
// AuthFormat renders the header value; %s is the key (e.g. "Bearer %s", "Token %s", "%s").
// Default: "Bearer %s" for the Authorization header, "%s" (raw key) for any other header.
AuthFormat string
// Headers are extra static headers sent to the upstream (applied before the key header).
// Per-request values can be set in a BeforeProxy hook.
Headers map[string]string
// AllowedRoles: caller needs at least one. Empty means any authenticated user.
AllowedRoles []string
// RateLimit per user for this upstream. Nil means unlimited.
RateLimit *RateLimit
// Timeout waiting for the upstream response headers. Default 120s. Streams are not cut off.
Timeout time.Duration
}
Upstream holds the settings shared by every upstream kind.
func (Upstream) MarshalJSON ¶
MarshalJSON redacts the key.
type UpstreamStore ¶
type UpstreamStore interface {
List(ctx context.Context) ([]Definition, error)
}
UpstreamStore supplies upstream definitions for Reload.