Skip to content

Behavior Contracts

These are promises ProxAI commits to. Breaking any of them is a breaking change that requires a major version bump and a migration note (see C30). They are more stable than field names or config defaults.

C1

Strict config loading

config.toml with missing required fields always fails to start. ProxAI never boots into a partially-valid runtime state using implicit defaults.

Source owner

src/config.rs

Failure mode

Proxy starts with missing provider, route, protocol, or timeout data.

Suggested tests
  • Config loading/default tests
  • Startup validation tests
C2

Local example generation

First run always generates config.example.toml in the app directory as a local reference.

Source owner

src/paths.rs and config bootstrap code

Failure mode

New users cannot discover the local configuration shape.

Suggested tests
  • Generated app-directory defaults tests
C3

Tracked example is canonical

The tracked config.example.toml in the repository is the canonical example. ProxAI never overwrites it at runtime.

Source owner

config.example.toml and config bootstrap code

Failure mode

Runtime generation mutates repository documentation source.

Suggested tests
  • Config example generation tests
  • Docs config coverage check
C4

Tool-call timeout is required

[tool_calls].timeout_secs must be greater than zero, otherwise ProxAI fails to start.

Source owner

src/config.rs

Failure mode

Tool-call semantic stalls can hang indefinitely.

Suggested tests
  • Config validation tests
  • Streaming tool-call timeout tests
C5

MCP stays local

The MCP listener always binds to a local address (127.0.0.1 by default) and is never exposed to external networks by ProxAI itself.

Source owner

Listener setup and config defaults

Failure mode

Control surface is exposed outside the local machine.

Suggested tests
  • Listener default tests
C6

MCP is not a model path

MCP is a control surface only. Model requests flow through the Proxy listener, never through MCP.

Source owner

HTTP routing/listener setup

Failure mode

Model traffic reaches control endpoints.

Suggested tests
  • Route/listener separation tests
C7

Explicit routes win

Default providers are used only when no explicit route matches. A matching route always wins over defaults.

Source owner

src/routing/

Failure mode

Default provider steals traffic from a matching route.

Suggested tests
  • Route matching tests
  • Proxy e2e route tests
C8

Unsupported pairs fail explicitly

Unimplemented conversion pairs always fail explicitly. ProxAI never silently falls through to a default provider when a route matches but the protocol pair is unsupported.

Source owner

src/translation/ and routing dispatch

Failure mode

Unsupported conversion silently routes to a different provider.

Suggested tests
  • Compatibility matrix coverage
  • Unsupported pair e2e tests
C9

Provider protocol is semantic

Provider protocol is the semantic identifier. Provider names are user labels and never change wire behavior.

Source owner

src/config.rs, src/provider/, src/translation/

Failure mode

Provider name accidentally changes serialization or response parsing.

Suggested tests
  • Provider protocol dispatch tests
C10

Request protocol guards are strict

If a route sets request_protocol and the model matches but the inbound protocol differs, ProxAI returns a configuration error instead of routing anyway.

Source owner

src/routing/

Failure mode

Endpoint-specific routes silently fall through.

Suggested tests
  • Protocol-aware route matching tests
C11

OpenAI provider auth is controlled by ProxAI

Provider api_key is always sent upstream. For OpenAI providers (openai_responses, openai_chat_completions) it is sent as Authorization: Bearer <key> and any client-supplied Authorization header is ignored.

Source owner

src/provider/*/transport

Failure mode

Client credentials leak upstream or provider credentials are not used.

Suggested tests
  • Provider transport header tests
C12

Anthropic provider auth uses x-api-key

For Anthropic Messages providers, the key is sent as x-api-key.

Source owner

src/provider/*/transport

Failure mode

Anthropic-compatible upstream rejects auth or receives wrong auth header.

Suggested tests
  • Anthropic transport header tests
C13

Outbound protocol decides content type

Response Content-Type is decided by the outbound protocol, not passed through from the upstream.

Source owner

src/http_support/ and response translation

Failure mode

Client receives upstream content type incompatible with inbound protocol.

Suggested tests
  • Response reconstruction tests
C14

Headers are filtered

Upstream headers are filtered before forwarding. The forwardable set is explicitly controlled by ProxAI, never a verbatim copy of all upstream headers.

Source owner

src/http_support/

Failure mode

Private or protocol-incompatible upstream headers leak to the client.

Suggested tests
  • Header filtering tests
C15

Useful upstream diagnostic headers are preserved

For non-2xx upstream responses, ProxAI preserves useful diagnostic headers when the upstream actually provides them: Retry-After, upstream request id, rate-limit headers. It never fabricates these headers.

Source owner

src/error/ and upstream response handling

Failure mode

Clients lose actionable retry/rate-limit information or receive fabricated data.

Suggested tests
  • Upstream error projection tests
C16

Client errors carry status in payload

Client-facing errors always include a numeric status field inside the payload. This is essential for SSE error events where HTTP status can no longer be changed.

Source owner

src/error/render.rs

Failure mode

SSE error events lose status context.

Suggested tests
  • HTTP and SSE error rendering tests
C17

Internal error taxonomy is private

ProxAI never exposes its internal typed error taxonomy verbatim. Client-facing type values are a fixed stable enum, extended only additively.

Source owner

src/error/

Failure mode

Internal Rust error names become public API.

Suggested tests
  • Error type projection tests
  • Docs checker error type coverage
C18

Upstream code and param are not fabricated

ProxAI never fabricates upstream code or param values. They are only present when the upstream actually provided them.

Source owner

src/error/

Failure mode

Clients make decisions from invented upstream metadata.

Suggested tests
  • Upstream error body shape tests
C19

Streams require terminal events

SSE streams are always observed for terminal events. A stream is considered complete only when the expected terminal event arrives (response.completed, [DONE], or message_stop).

Source owner

SSE scanner and streaming translation modules

Failure mode

Client treats semantically incomplete streams as successful.

Suggested tests
  • Terminal event tests
  • Incomplete stream tests
C20

Tool-call stalls close predictably

Stalled tool-call argument streams always close within [tool_calls].timeout_secs. ProxAI never lets a client hang indefinitely after tool-call arguments have started.

Source owner

Streaming state machines and tool-call timeout handling

Failure mode

Client hangs forever after partial tool arguments.

Suggested tests
  • Tool-call stall tests
C21

SSE carrier is preserved

SSE bytes and text/event-stream content type are preserved through translation.

Source owner

src/http_support/, SSE translation modules

Failure mode

Streaming clients receive non-SSE carrier data.

Suggested tests
  • SSE carrier tests
C22

Read timeout is idle-based

read_idle_timeout_secs is an idle-read timeout, not a total request duration cap. It only fires when no new upstream bytes arrive within the configured window.

Source owner

Provider transport/upstream read handling

Failure mode

Long but active streams are incorrectly cut off.

Suggested tests
  • Idle timeout tests
C23

Private request data is not logged

ProxAI never logs request bodies, Authorization headers, API keys, or private prompts.

Source owner

src/observe/ and logging call sites

Failure mode

Private prompts or credentials appear in default logs.

Suggested tests
  • Logging redaction review
  • Request hint tests
C24

Captures stay local

Capture artifacts are local files under the app directory only. They are never transmitted by ProxAI itself.

Source owner

src/observe/ capture writers

Failure mode

Capture artifacts leave the local machine automatically.

Suggested tests
  • Capture writer tests
C25

Capture paths are fixed

Capture paths are fixed and not configurable, to avoid accidental writes to arbitrary locations.

Source owner

src/observe/ and paths code

Failure mode

User-provided paths write private captures into unsafe locations.

Suggested tests
  • Capture path tests
C26

Translation is carrier-pure

Translation stays pure at the HTTP carrier boundary. It accepts protocol values and payload/stream carriers, not HTTP Response/Body, route details, model rewrite details, or provider-private structs.

Source owner

src/translation/

Failure mode

Translation depends on HTTP/provider internals and becomes impossible to reason about pair-wise.

Suggested tests
  • Boundary-focused conversion tests
C27

No raw provider round-trips

ProxAI never round-trips raw provider structures across a translation pair. Each direction is independently translated into the target protocol's types.

Source owner

src/translation/

Failure mode

Target protocol receives provider-private structures.

Suggested tests
  • Pair-specific response conversion tests
C28

Provider quirks stay in provider code

Provider-local normalization belongs in provider/. General cross-protocol shape changes belong in translation/. Provider code must not hide protocol-to-protocol conversion.

Source owner

src/provider/ and src/translation/

Failure mode

Protocol conversion logic is hidden in provider-specific adapters.

Suggested tests
  • Provider/request and translation boundary review
C29

Client-facing error types are additive

Client-facing type values are extended additively. Existing values are never renamed or removed without a major version bump.

Source owner

src/error/render.rs and reference docs

Failure mode

Existing clients break on renamed or removed error types.

Suggested tests
  • Error type coverage checker
C30

Contract breaks are major-version changes

Breaking any contract in this page (C1–C30) is a breaking change that requires a major version bump and a migration note.

Source owner

Release process and docs

Failure mode

User-visible breaking behavior ships as a minor/patch change.

Suggested tests
  • Release checklist review