Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/branch-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,14 @@ jobs:
cargo fmt --all -- --check
cargo fmt --manifest-path e2e/rust/Cargo.toml --all -- --check
cargo fmt --manifest-path examples/governance-interceptor/Cargo.toml --all -- --check
cargo fmt --manifest-path examples/supervisor-middleware-response-transform/Cargo.toml --all -- --check

- name: Lint
run: |
cargo clippy --workspace --all-targets -- -D warnings
cargo clippy --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings
cargo check --manifest-path examples/governance-interceptor/Cargo.toml --all-targets
cargo check --manifest-path examples/supervisor-middleware-response-transform/Cargo.toml --all-targets

- name: Test
env:
Expand Down
15 changes: 15 additions & 0 deletions architecture/security-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ raw relay by default. A `protocol: rest` endpoint can opt in to
after an allowed `101` upgrade; server-to-client traffic and all other upgraded
protocols remain raw passthrough.

Supervisor middleware attaches independently of the network rule that admits
the destination. Request and client WebSocket hooks run after policy admission
and before credential injection. HTTP response hooks run after the upstream
returns a final non-`1xx` head and before the sandbox receives it. Response
middleware operates on normalized representation bytes, not transfer chunks or
socket reads, and retains each V1 input until the stage acknowledges it. This
lets `fail_open` preserve content. A `fail_closed` error returns a platform-owned
502 before response commitment and aborts the stream after commitment. Generic
response processing never handles a `101` protocol upgrade.

One shared header-mutation validator applies request, response, and trailer
authority profiles atomically. Middleware can write permitted end-to-end fields
without a namespace prefix, while each direction protects its credentials,
routing, framing, connection control, and semantic security fields.

## Credentialed Endpoints

OpenShell keeps provider credentials on paths it can inspect or rewrite by
Expand Down
33 changes: 30 additions & 3 deletions crates/openshell-core/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ use tokio::sync::mpsc;
use tonic::{Request, Response, Status};

use crate::proto::{
HttpHeader, HttpRequestEvaluation, HttpRequestResult, HttpRequestTarget, MiddlewareManifest,
RequestContext, SupervisorMiddlewarePhase, ValidateConfigRequest, ValidateConfigResponse,
WebSocketSessionEvent, WebSocketSessionEventResult,
HttpHeader, HttpRequestEvaluation, HttpRequestResult, HttpRequestTarget, HttpResponseEvent,
HttpResponseEventResult, MiddlewareManifest, RequestContext, SupervisorMiddlewarePhase,
ValidateConfigRequest, ValidateConfigResponse, WebSocketSessionEvent,
WebSocketSessionEventResult,
};

/// Transport-neutral result stream for one HTTP response middleware stage.
pub type HttpResponseResultStream = Pin<
Box<dyn tokio_stream::Stream<Item = Result<HttpResponseEventResult, Status>> + Send + 'static>,
>;

/// Transport-neutral response stream for one WebSocket middleware stage.
pub type WebSocketResponseStream = Pin<
Box<
Expand Down Expand Up @@ -47,6 +53,15 @@ pub trait SupervisorMiddlewareEndpoint: Send + Sync {
&self,
requests: mpsc::Receiver<WebSocketSessionEvent>,
) -> Result<WebSocketResponseStream, Status>;

async fn open_http_response_pre_return(
&self,
_requests: mpsc::Receiver<HttpResponseEvent>,
) -> Result<HttpResponseResultStream, Status> {
Err(Status::unimplemented(
"middleware does not implement HTTP response pre-return evaluation",
))
}
}

/// Borrowed request state exposed to one in-process middleware invocation.
Expand Down Expand Up @@ -242,6 +257,18 @@ pub trait InProcessMiddleware: Send + Sync {
"middleware does not implement WebSocket sessions",
))
}

/// Open one HTTP response pre-return stream.
///
/// Request-only implementations may keep the default unsupported response.
async fn open_http_response_pre_return(
&self,
_requests: mpsc::Receiver<HttpResponseEvent>,
) -> std::result::Result<HttpResponseResultStream, Status> {
Err(Status::unimplemented(
"middleware does not implement HTTP response pre-return evaluation",
))
}
}

/// Default timeout for one supervisor middleware RPC.
Expand Down
Loading
Loading