Problem Statement
Supervisor middleware policies attach a complete external middleware by its operator-owned registration name. The service manifest declares the operation and phase pairs it supports, and each pair can appear at most once. The gateway validates those capabilities, but operators cannot constrain which operation/phase pairs a registered service may expose.
V1 supports only HTTP_REQUEST/PRE_CREDENTIALS, so a service cannot currently expand its authority beyond that one pair. This becomes relevant when the middleware contract adds more operations or phases: a service upgrade or compromise could expose a newly supported pair without a matching operator configuration change.
PR #2005 adds dynamic, allowlist, and exact binding policies for gateway interceptors. Supervisor middleware should share the same operator authority model while selecting bindings by operation and phase rather than introducing service-owned binding IDs.
This complements #2217. Manifest digest pinning detects or coordinates manifest drift, while a binding policy expresses which capabilities the operator intends to grant even when the service manifest changes.
Proposed Design
Define shared binding authorization semantics in the common configuration layer and use them for both gateway interceptors and operator-registered supervisor middleware:
dynamic: accept all valid manifest operation/phase pairs. Keep this as the compatibility default and emit the same explicit warning used for dynamically authorized interceptors.
allowlist: enable only operator-configured operation/phase selectors that the manifest advertises. Fail startup when a configured selector is missing or invalid. Ignore and log additional manifest bindings.
exact: require the configured and manifest operation/phase selector sets to match exactly, and fail startup on either missing or additional bindings.
Extend each [[openshell.supervisor.middleware]] registration with binding_policy and an explicit binding selector list. Selectors should use the platform-owned operation and phase enums. The configuration shape should leave room for future per-binding operator overrides, such as tighter body limits or timeouts, without relying on a service-owned identifier.
Normalize the effective authorized binding set at gateway startup. Use only that set for manifest validation, configuration validation routing, operation/phase dispatch, and existing body-limit and timeout checks. Policies continue to attach the complete middleware by registration name; they do not select individual bindings.
Carry the normalized authorized selector set in SupervisorMiddlewareService when the gateway publishes sandbox configuration. The supervisor must enforce that set when it connects to the service and processes its own Describe response, so a capability rejected by the gateway cannot become runnable through supervisor-side manifest discovery. Include the effective set in configuration revision calculation so changes propagate to running sandboxes.
Add automated coverage for:
- compatibility behavior when
binding_policy is omitted
- allowlist handling of matching, missing, additional, malformed, and duplicate operation/phase selectors
- exact-mode set mismatches
- rejection of duplicate manifest bindings for one operation/phase pair
- supervisor runtime rejection of bindings outside the propagated authorized set
- per-binding operator overrides that can only narrow service-advertised limits
- configuration revision changes when the effective binding set changes
Document the shared semantics and middleware TOML shape in architecture/gateway.md, docs/reference/gateway-config.mdx, and the supervisor middleware documentation.
Alternatives Considered
Relying only on manifest digest pinning from #2217 detects drift but does not express the authority an operator intends to grant.
Using service-owned binding IDs would make an external label part of the operator authority model. Operation/phase selectors are platform-owned, unambiguous because duplicate pairs are rejected, and align with the interceptor allowlist and exact modes selecting RPC capabilities rather than manifest IDs.
Adding a middleware-specific allowlist or boolean would solve only one mode and allow the interceptor and middleware security models to drift.
Filtering only in the gateway registry would leave a trust-boundary gap because sandbox supervisors independently connect to the service, call Describe, and build their runtime registries.
Agent Investigation
Reviewed PR #2005 and its discussion, the interceptor binding-policy implementation, supervisor middleware registration-name attachment semantics, middleware registry validation, and the sandbox configuration protobuf. Supervisor middleware policies attach registrations by operator-owned name, while manifests identify unique capabilities by operation and phase. V1 currently permits only HTTP_REQUEST/PRE_CREDENTIALS, so operator binding authorization is intentionally deferred until additional capabilities or per-binding overrides are introduced.
Problem Statement
Supervisor middleware policies attach a complete external middleware by its operator-owned registration name. The service manifest declares the operation and phase pairs it supports, and each pair can appear at most once. The gateway validates those capabilities, but operators cannot constrain which operation/phase pairs a registered service may expose.
V1 supports only
HTTP_REQUEST/PRE_CREDENTIALS, so a service cannot currently expand its authority beyond that one pair. This becomes relevant when the middleware contract adds more operations or phases: a service upgrade or compromise could expose a newly supported pair without a matching operator configuration change.PR #2005 adds
dynamic,allowlist, andexactbinding policies for gateway interceptors. Supervisor middleware should share the same operator authority model while selecting bindings by operation and phase rather than introducing service-owned binding IDs.This complements #2217. Manifest digest pinning detects or coordinates manifest drift, while a binding policy expresses which capabilities the operator intends to grant even when the service manifest changes.
Proposed Design
Define shared binding authorization semantics in the common configuration layer and use them for both gateway interceptors and operator-registered supervisor middleware:
dynamic: accept all valid manifest operation/phase pairs. Keep this as the compatibility default and emit the same explicit warning used for dynamically authorized interceptors.allowlist: enable only operator-configured operation/phase selectors that the manifest advertises. Fail startup when a configured selector is missing or invalid. Ignore and log additional manifest bindings.exact: require the configured and manifest operation/phase selector sets to match exactly, and fail startup on either missing or additional bindings.Extend each
[[openshell.supervisor.middleware]]registration withbinding_policyand an explicit binding selector list. Selectors should use the platform-owned operation and phase enums. The configuration shape should leave room for future per-binding operator overrides, such as tighter body limits or timeouts, without relying on a service-owned identifier.Normalize the effective authorized binding set at gateway startup. Use only that set for manifest validation, configuration validation routing, operation/phase dispatch, and existing body-limit and timeout checks. Policies continue to attach the complete middleware by registration name; they do not select individual bindings.
Carry the normalized authorized selector set in
SupervisorMiddlewareServicewhen the gateway publishes sandbox configuration. The supervisor must enforce that set when it connects to the service and processes its ownDescriberesponse, so a capability rejected by the gateway cannot become runnable through supervisor-side manifest discovery. Include the effective set in configuration revision calculation so changes propagate to running sandboxes.Add automated coverage for:
binding_policyis omittedDocument the shared semantics and middleware TOML shape in
architecture/gateway.md,docs/reference/gateway-config.mdx, and the supervisor middleware documentation.Alternatives Considered
Relying only on manifest digest pinning from #2217 detects drift but does not express the authority an operator intends to grant.
Using service-owned binding IDs would make an external label part of the operator authority model. Operation/phase selectors are platform-owned, unambiguous because duplicate pairs are rejected, and align with the interceptor
allowlistandexactmodes selecting RPC capabilities rather than manifest IDs.Adding a middleware-specific allowlist or boolean would solve only one mode and allow the interceptor and middleware security models to drift.
Filtering only in the gateway registry would leave a trust-boundary gap because sandbox supervisors independently connect to the service, call
Describe, and build their runtime registries.Agent Investigation
Reviewed PR #2005 and its discussion, the interceptor binding-policy implementation, supervisor middleware registration-name attachment semantics, middleware registry validation, and the sandbox configuration protobuf. Supervisor middleware policies attach registrations by operator-owned name, while manifests identify unique capabilities by operation and phase. V1 currently permits only
HTTP_REQUEST/PRE_CREDENTIALS, so operator binding authorization is intentionally deferred until additional capabilities or per-binding overrides are introduced.