gaze-proxy
gaze-proxy is the feature-gated HTTP proxy runtime for LLM SDK base-URL swaps.
It preserves each provider's native wire shape and uses adapters only to locate
PII-bearing fields before calling the supplied gaze::Pipeline.
Cargo
[]
= "0.15.1"
Quickstart
Then point SDKs at the daemon:
The default config is written to ~/.config/gaze/proxy.toml and includes:
= "127.0.0.1:8787"
= "30m"
= "core"
[]
= "https://api.openai.com/"
[]
= "https://api.anthropic.com/"
[]
= "https://generativelanguage.googleapis.com/"
Locale
Recognizers that declare locales = [...] — postal.de, postal.us, the national phone
recognizers — only run when the active locale chain intersects that list. The proxy resolves
that chain once and uses it for both the primary surface pass and the outbound residual
re-scan, so the two can never disagree about what counts as PII.
gaze proxy has no --locale flag; the chain comes from the policy you pass:
= ["de-DE"]
With no policy the chain is ["global"] and locale-gated recognizers stay inert. Library
adopters set it explicitly, passing the same chain the pipeline was assembled under:
let core = new
.with_bundled_rulepack
.with_locale
.build?;
let config = new.with_locale_chain;
A locale chain always ends in global, so configuring one can only widen detection.
Providers
- OpenAI:
POST /v1/chat/completions,/v1/completions,/v1/responses - Anthropic:
POST /v1/messages - Gemini:
POST /v1beta/models/*:{generateContent,streamGenerateContent,countTokens}
Each adapter walks text, tool-call, tool-result, and function argument surfaces in that provider's native JSON. The proxy does not transcode requests.
Daemon Commands
Pidfiles live under the platform local-data directory, never /tmp. Stale
pidfiles are revalidated with process liveness checks and cleaned before start.
Lifecycle operations coordinate through a persistent <pidfile>.lock sidecar.
Do not remove this sidecar or edit the pidfile while daemon commands are active;
cleanup removes only the owned pidfile and leaves the sidecar in place.
gaze proxy logs --follow is also available for local daemon inspection.
Security Notes
The strict Anthropic Messages profile rebuilds its outbound headers from a closed
allowlist: content-type, x-api-key, anthropic-version, and an optional,
explicitly allowlisted anthropic-beta. Unconfigured Authorization, bearer
credentials, cookies, and unknown SDK headers are accepted at ingress and
dropped. If an embedding host configures Authorization as local listener
authentication, it is a singleton consumed only by the trusted principal
resolver and is still never forwarded.
AnthropicAdapter::new is an ephemeral, single-request profile and rejects
x-gaze-session-id. Session continuity is an explicit builder/configuration
choice and then requires that header with a canonical lowercase UUIDv4 value.
The SDK base URL is the proxy root, while the only direct route is exactly
POST /v1/messages.
The listener does not become an access-control boundary merely because provider credentials pass through it. Bind to loopback unless an explicit trusted principal resolver protects a non-loopback listener. See the strict Anthropic Messages contract for the complete wire, proof, inspection, migration, and manual SDK-test contract.
Configured safety nets at request admission
Surfaced request text in the direct Anthropic and legacy adapter paths first runs
the safety-net Resolve step of gaze clean --safety-net-fallback strict: every span
a configured net flags becomes a restorable token. Plain gaze clean defaults to
the redact fallback, which also tokenizes what the nets' re-run flags and deletes
what is left one way; the proxy refuses that request instead. The result then
passes configured safety-net admission before provider I/O. Nets inspect the
complete transformed surface with a manifest built from the actual session token
ownership and restore boundaries. Token-contained
reflags, including class disagreements, remain allowed; a flagged span Resolve could
not tokenize, a malformed suspect, or a net execution error rejects the request with
422 and a typed refusal (variant, fallback reason, suspect classes; never text). No
destructive clean fallback is used. See
Safety nets and refusals.
Compatibility: a net-flagged span, including text preserved by primary policy, is now tokenized and forwarded instead of refused. Requests Resolve cannot protect are still denied before reaching the provider. Admission adds inference on surfaced text and codec validation views, bypasses observer skip optimizations, and runs all registry backends selected across the locale chain. Registry resolution and inference errors fail closed. This can increase latency and inference cost. Primary Preserve/Redact actions and public legacy clean defaults are unchanged; admission does not certify their reversibility.
No installed net, a custom net skipped for locale coverage, or a detector miss remains a coverage limitation. A model is not required globally. Existing strict protection still requires its full primary and locale contract. Direct failures abandon staged mappings before commit/send. Legacy mappings are already live and remain so on failure; there is no whole-request rollback guarantee. Response restoration and response residual validation retain their existing contracts.