Addon model
roxy understands HTTP, not any particular API. Logic that needs to understand the traffic, such as checking a model's tool calls or counting tokens, goes in an addon. Each addon is a step that the request passes through on its way to the rules, and that the response passes back through on its way to the client. An addon is either:
- a WASM layer (
kind: wasm): a WebAssembly component that runs inside roxy (writing addons); or - a service layer (
kind: service): an external service that each exchange streams through over a WebSocket (service layer protocol).
Configuring addons is covered in addon configuration.
The stack
Addons run in the order they are listed under addons:, between the
client and the rules. Nothing configurable runs between the rules and the
network, so whatever the rules approved is what leaves. All the steps run
at the same time on a stream, each working on the chunk the step before
it passed on.
What an addon can do
For each exchange, an addon gets the request as the step before it passed it on. It can:
- pass the request on unchanged, or change it first;
- hold it, for example while a person approves it;
- answer the client itself, without passing the request on;
- do the same with the response on its way back.
The request and response are handled separately, so an addon can start answering before it has passed the request on, or while it still holds it. An addon passes each request on at most once and answers exactly once. To call anything else, such as a monitoring model, it uses a named endpoint, which does not go through the stack.
An addon can ask for the head only, or the head and the body, in each direction. A body it does not ask for goes past it untouched and costs nothing (what a layer sees).
Guarantees
- The rules judge every request that leaves. Whatever the last addon passes on is checked as strictly as a client's request, and judged by the rules as if the client had sent it.
- Addons are held to the client's limits. Header limits, body caps and timeouts apply to what an addon passes on, as they do to a client.
- Failure is closed. An enforcing addon that fails makes the
exchange fail with
503. There is no "on error, pass" (failure).
An addon can replace any response on its way back, including a refusal from the rules. Addons are trusted, the refused request never left, and the flow log keeps the rules' decision.
Ordering across layers
Traffic passes through the addons in order: the second addon sees what the first passed on, in the order it passed it on.
Calls to roxy's host services, such as
writing state or adding a tag, run alongside the traffic rather than in
it. If addon A writes state that addon B should read, A must wait for its
write to finish before passing on the bytes B will read them with. A WASM
addon's calls always finish before it carries on. A service layer waits
for the result itself.
For the rules to see a tag or state write when they decide on the request, it must land before the addon passes the request on. A later write is seen by the next rule that reads it, but never changes a decision already made.
Modes
mode: enforce(the default): the addon is in the path, and what it does takes effect.mode: observe: the addon gets a copy of the traffic and cannot change or delay it. Its failures are logged, never fatal. This is the way to try out a new monitor. An observer that falls too far behind loses its copy, and the exchange carries on (observers).