@@ -182,57 +182,65 @@ adapter renders it for its protocol.
182182``` mermaid
183183flowchart TD
184184 Start["Authorized egress + destination connector"]
185- Start --> FirstReq{"Forward HTTP adapter supplied first request?"}
185+ Start --> FirstReq{"Forward HTTP adapter<br/>already has first request?"}
186186
187- FirstReq -- Yes --> ForwardMode{"decision.endpoint.enforcement"}
188- ForwardMode -- "None" --> HttpCred["HTTP relay<br/>credential injection only"]
189- ForwardMode -- "Http" --> HttpL7["HTTP relay<br/>REST/GraphQL/JSON-RPC/MCP/WebSocket policy"]
190- ForwardMode -- "ProtocolProcessor" --> BadForward["Deny: HTTP request for native protocol endpoint"]
187+ FirstReq -- Yes --> ForwardEnforcement{"Endpoint enforcement"}
188+ ForwardEnforcement -- "None or HTTP" --> HttpReq["Parsed HTTP request"]
189+ ForwardEnforcement -- "Protocol processor" --> BadForward["Deny: HTTP request for native protocol endpoint"]
191190
192- FirstReq -- No --> TlsPolicy{"TLS handling skipped?"}
193- TlsPolicy -- Yes --> Readable["Readable client stream"]
194- TlsPolicy -- No --> Peek["Peek client bytes"]
191+ FirstReq -- No --> Prepare["Prepare readable client stream"]
192+ Prepare --> TlsPolicy{"TLS handling enabled?"}
193+ TlsPolicy -- No --> Readable["Client stream"]
194+ TlsPolicy -- Yes --> Peek["Peek client bytes"]
195195 Peek --> Tls{"TLS ClientHello?"}
196196 Tls -- Yes --> Terminate["Shared TLS terminator"]
197197 Tls -- No --> Readable
198198 Terminate --> Readable
199199
200- Readable --> Enforce{"decision.endpoint.enforcement"}
201- Enforce -- "None" --> Sniff{"Looks like HTTP?"}
202- Sniff -- Yes --> HttpCred
203- Sniff -- No --> TcpRelay["TcpRelay<br/>byte copy"]
200+ Readable --> Enforce{"Endpoint enforcement"}
201+ Enforce -- "None" --> Sniff{"HTTP request detected?"}
202+ Sniff -- Yes --> ParseHttp["Parse HTTP request"]
203+ Sniff -- No --> TcpRelay["TcpRelay<br/>connect upstream and copy bytes"]
204+ ParseHttp --> HttpReq
204205
205- Enforce -- "Http " --> MustHttp{"Looks like HTTP ?"}
206- MustHttp -- Yes --> HttpL7
206+ Enforce -- "HTTP " --> MustHttp{"HTTP request detected ?"}
207+ MustHttp -- Yes --> ParseHttp
207208 MustHttp -- No --> DenyHttp["Deny: expected HTTP"]
208209
209- Enforce -- "ProtocolProcessor" --> Processor["TcpRelay<br/>protocol processor owns loop"]
210-
211- HttpCred --> ReqLoop["HTTP request loop"]
212- HttpL7 --> ReqLoop
213- ReqLoop --> ReqPolicy{"Request allowed?"}
214- ReqPolicy -- No --> ReqDeny["Local HTTP deny<br/>no upstream write"]
215- ReqPolicy -- Yes --> Middleware{"Supervisor middleware chain configured?"}
216- Middleware -- Yes --> MwEval["Evaluate HTTP_REQUEST / PRE_CREDENTIALS<br/>allow, deny, or mutate"]
217- Middleware -- No --> StaticCreds["Resolve static placeholders"]
218- MwEval --> MwAllowed{"Middleware allowed?"}
219- MwAllowed -- No --> MwDeny["Local middleware deny<br/>no credential injection"]
220- MwAllowed -- Yes --> StaticCreds
221- StaticCreds --> TokenGrant["Apply endpoint token grant if configured"]
222- TokenGrant --> Rewrite["Rewrite configured credential slots"]
223- Rewrite --> HttpDial["Connect or reuse upstream"]
224- HttpDial --> HttpResponse["Write request and relay response"]
225- HttpResponse --> Upgrade{"101 WebSocket upgrade?"}
226- Upgrade -- No --> ReqLoop
227- Upgrade -- Yes --> WsInspect{"WebSocket inspection or rewrite configured?"}
228- WsInspect -- No --> RawUpgrade["Raw upgraded stream"]
229- WsInspect -- Yes --> WsRelay["WebSocket relay<br/>text-frame rewrite / message policy"]
230-
231- Processor --> ProcessorDial["Processor calls connector when protocol allows"]
232- TcpRelay --> TcpDial["Connect upstream"]
233- TcpDial --> ByteCopy["Copy bytes"]
210+ Enforce -- "Protocol processor" --> Processor["TcpRelay hands stream to protocol processor"]
211+ Processor --> ProcessorOwns["Processor owns message loop<br/>and calls connector when allowed"]
212+
213+ subgraph HttpLoop["HTTP relay request loop"]
214+ HttpReq --> HttpMode{"HTTP endpoint policy?"}
215+ HttpMode -- "L4-only HTTP" --> ReqAllowed["Request admitted by connection decision"]
216+ HttpMode -- "REST / GraphQL / JSON-RPC / MCP / WebSocket" --> ReqPolicy{"Request policy allowed?"}
217+ ReqPolicy -- No --> ReqDeny["Local HTTP deny<br/>no upstream write"]
218+ ReqPolicy -- Yes --> ReqAllowed
219+ ReqAllowed --> Middleware{"Supervisor middleware<br/>configured?"}
220+ Middleware -- Yes --> MwEval["Run HTTP_REQUEST / PRE_CREDENTIALS middleware"]
221+ Middleware -- No --> Creds["Resolve static placeholders<br/>and token grants"]
222+ MwEval --> MwAllowed{"Middleware allowed?"}
223+ MwAllowed -- No --> MwDeny["Local middleware deny<br/>no credential injection"]
224+ MwAllowed -- Yes --> Creds
225+ Creds --> Rewrite["Inject credentials into configured slots"]
226+ Rewrite --> HttpDial["Connect or reuse upstream"]
227+ HttpDial --> HttpResponse["Write request and relay response"]
228+ HttpResponse --> Upgrade{"101 WebSocket upgrade?"}
229+ Upgrade -- No --> NextReq{"Another HTTP request<br/>on this connection?"}
230+ NextReq -- Yes --> HttpReq
231+ NextReq -- No --> Done["HTTP relay done"]
232+ Upgrade -- Yes --> WsInspect{"WebSocket inspection<br/>or rewrite configured?"}
233+ WsInspect -- No --> RawUpgrade["Raw upgraded stream"]
234+ WsInspect -- Yes --> WsRelay["WebSocket relay<br/>text-frame rewrite / message policy"]
235+ end
234236```
235237
238+ Read this as two phases. The top half chooses the relay shape from the adapter
239+ surface and endpoint enforcement. The ` HTTP relay request loop ` only receives a
240+ parsed HTTP request. Supervisor middleware is not another policy funnel; it is
241+ an optional request-path hook after HTTP policy allows the request and before
242+ OpenShell-managed credential injection.
243+
236244Relay rules:
237245
238246- HTTP credential injection happens in both HTTP modes: L4-only HTTP and
0 commit comments