Supported Operations
A middleware service handles one or more operations, such as HTTP requests or WebSocket messages. This page describes when OpenShell calls your service for each operation, what your service receives, and what it can return. The RPCs that every middleware service implements are described in Build Your Middleware Service.
Operations and Phases
Your service declares each operation it supports as a binding, an operation and its phase, in its Describe manifest. OpenShell calls your service only for the operations it declares.
When several middleware match the same traffic, OpenShell calls them in ascending order, and each middleware sees the changes that earlier middleware made.
Common Inputs and Results
Every operation includes a request context. The context always includes sandbox_id. Use it for authorization, persistence, and correlation. The context also includes sandbox and workspace names when available. Names can be reused, so use them only for display and logging.
Operations that include headers deliver them in wire order, and repeated headers stay separate. OpenShell removes credential, routing, and hop-by-hop headers before sending them to your service. It also removes framing headers from request input. Response preflight retains upstream Content-Length, Content-Encoding, and Content-Range as read-only metadata, unless Connection names them as hop-by-hop headers. Your service cannot change or remove these fields; OpenShell handles downstream framing.
Every result can include an optional reason code, findings, and metadata:
- A reason code identifies why your service denied traffic. OpenShell includes it in logs and in HTTP error responses. It must be 1-64 bytes long, start with a lowercase ASCII letter, and contain only lowercase ASCII letters, digits, and underscores. OpenShell treats an invalid code as a middleware failure.
- Findings report what your service detected, even when it allows the traffic. OpenShell logs how many findings your service reported, not their text.
- Metadata carries non-secret diagnostic details. OpenShell does not log it.
OpenShell never forwards free-form text from your service to the sandbox or to logs.
HTTP
HTTP middleware can check a request before it leaves the sandbox, and the response before the sandbox receives it. A service can support either operation or both.
Requests
OpenShell calls your service after network policy allows a request and before it injects provider credentials. Your service can allow, deny, or change the request.
What Your Service Receives
The request body, target, headers, and request context.
What Your Service Can Return
- Allow the request unchanged.
- Deny the request with an optional reason code.
- Replace the body, up to the same payload limit as the input.
- Add, change, or remove headers.
When your service replaces the body, OpenShell checks the new body against body-aware policy, such as GraphQL, JSON-RPC, or MCP rules, before the next middleware or the external service sees it. A replacement cannot introduce an operation that policy denies.
Denials and Failures
When your service denies a request, the sandbox receives a structured error and OpenShell does not contact the external service:
When middleware fails, the sandbox receives error: middleware_failed instead.
Header Change Rules
A result can include an ordered list of header changes. A write change sets how it treats an existing header with the same name:
appendadds another value.overwritereplaces all existing values.skipkeeps the existing values.
A remove change removes all values for the name. OpenShell protects credential, routing, framing, and connection headers, and for responses also status, authentication challenge, content coding, range, and security policy headers. Header values cannot contain control characters, and request header values cannot contain OpenShell credential placeholders.
OpenShell applies each middleware’s changes as a unit. If one change is invalid, it discards all of them and treats the result as a middleware failure.
Responses
OpenShell calls your service after the external service responds and before the sandbox receives the response. At preflight, your service can skip inspection, block delivery, or choose how to inspect the response. During inspection, it can change or block the response.
Preflight and Body Modes
The preflight includes the status and headers. A preflight result can skip inspection, block delivery with block_delivery, or inspect the response with one body mode:
Body inspection is unavailable for responses without a body, partial responses, compressed responses, and responses with Cache-Control: no-transform. For those, a middleware that requests body inspection fails. Interim 1xx responses pass through unchanged, and a 101 protocol upgrade skips response middleware.
What Your Service Can Return
- Pass the response or body piece unchanged.
- Replace the body or a body piece.
- Add, change, or remove headers, with the same rules as requests.
- Block delivery with an optional reason code.
Blocking and Failures
If your service blocks the response before OpenShell starts sending it, the sandbox receives 403 Forbidden with the same middleware_denied error as a denied request. If middleware fails before then, the sandbox receives 502 Bad Gateway with error: response_delivery_failed. If delivery has already started, OpenShell aborts the response in both cases.
The external service has already handled the request, so blocking its response does not undo it, and retrying the request may repeat side effects.
Trailers and Framing
A service that inspects the body also receives trailers after the body. A preflight can declare new trailer names, and the service can then add them.
After middleware changes the body, OpenShell recalculates Content-Length and framing, so your service never handles transfer encoding.
WebSocket
WebSocket middleware checks the text messages the sandbox sends over a WebSocket connection.
Messages
OpenShell calls your service when the sandbox opens a WebSocket connection, then for each text message the sandbox sends. Your service can allow, deny, or replace each message.
Session Events
OpenShell opens one bidirectional stream to your service for each connection and sends these events:
- A preflight before contacting the external service. Your service returns
INSPECT,SKIP, orDENY.SKIPcloses the middleware stream and passes all messages without inspection by that middleware.DENYrejects the upgrade. - A session start after the external service accepts the connection. It includes the negotiated subprotocol.
- Each complete text message from the sandbox, in order.
- A session end when the connection closes, sent on a best-effort basis.
Your service returns a result for each preflight and message. Session start and end are notifications that need no result. When OpenShell closes its side of the stream, finish your side of the stream too.
Message Handling
OpenShell reassembles fragmented messages and decompresses permessage-deflate messages before sending them, so your service always receives a complete UTF-8 text message. A replacement can be any text, including an empty string. OpenShell re-frames and, when needed, re-compresses the replacement before forwarding it.
OpenShell does not send binary messages, control frames, or messages from the external service to middleware. Binary messages pass through unchanged, and OpenShell records an unsupported_message_type coverage event. Message sequence numbers count binary messages too, so your service can see gaps between text messages.
Only middleware that declares WEBSOCKET_MESSAGE receives messages. Middleware that declares only HTTP_REQUEST still inspects the upgrade request, and OpenShell records a binding_not_selected coverage event for it.
Denials and Failures
A denied message closes the connection with code 1008. When middleware fails, OpenShell rejects the upgrade or closes the connection. With fail_open, OpenShell instead stops using that middleware for the rest of the connection.
Close Codes
Limits
When a payload exceeds one middleware’s limit, that middleware fails. Other middleware keep their own limits.
Middleware evaluation capacity in each sandbox is shared by all operations. When it is full, OpenShell returns 503 Service Unavailable for an HTTP request before reading its body. When WebSocket message assembly capacity is full, OpenShell closes the connection with code 1013.