User Story
As a user exposing an HTTP or WebSocket application from a sandbox, I want to
opt that individual service into receiving the caller's bearer credential so
that the application can enforce its own authentication without weakening the
default credential boundary for other services.
Problem Statement
OpenShell service routing currently removes every incoming Authorization
header before proxying a request to a sandbox's loopback service. That behavior
is a safe default for gateway and edge credentials, but it also prevents an
application from receiving a bearer capability intended for the application
itself. Applications that authenticate an HTTP request or the initial WebSocket
handshake therefore cannot use the standard Authorization: Bearer ... flow
through an OpenShell service URL.
Impact / Why This Matters
This blocks authenticated application protocols such as an app server that
checks a capability during its WebSocket handshake. The practical workarounds
are to disable application authentication, move a credential into a query
parameter or cookie, use a nonstandard header, or bypass the exposed-service
route. Those alternatives either weaken the application's security posture,
increase the chance of credential disclosure, require application-specific
changes, or give up OpenShell's managed service URL and relay path.
The feature must remain opt-in. Automatically forwarding Authorization would
silently change the security boundary for every existing exposed service and
could deliver a gateway, edge, or caller credential to an untrusted sandbox
process.
Proposed Design
Add a persisted, per-service authorization mode with two effective behaviors:
strip and bearer_passthrough. Omitted, unspecified, legacy, and unknown
persisted values resolve to strip; unknown values in new API requests are
rejected. A service explicitly configured for bearer_passthrough may receive
zero or one syntactically valid Bearer authorization value, forwarded unchanged
to the loopback application. Duplicate, Basic, empty, and malformed values fail
with 400 Bad Request before the gateway contacts the application.
Surface the mode through standalone service exposure, create-time service
exposure, endpoint get/list responses, CLI commands and structured output, and
the curated Rust, Python, Go, and TypeScript SDKs. Continue stripping proxy
authorization, edge identity headers, forwarded client certificates, and edge
authentication cookies in every mode. The application—not OpenShell—validates
the bearer credential.
Acceptance Criteria
- Existing and newly exposed services strip
Authorization unless the matched
persisted endpoint explicitly selects bearer passthrough.
- Omitted or
UNSPECIFIED API values materialize or resolve to STRIP, and a
legacy stored endpoint without the new field retains stripping behavior.
- Unknown authorization-mode values are rejected with
INVALID_ARGUMENT
before endpoint or sandbox persistence.
- An opted-in HTTP service receives exactly one syntactically valid Bearer
authorization value unchanged.
- The same rule applies to the initial WebSocket upgrade request; subsequent
WebSocket frames require no authorization-header handling.
- An opted-in service may receive a request without
Authorization so that the
application can return its own authentication response.
- Duplicate authorization fields and empty, Basic, or malformed credentials
receive 400 Bad Request without contacting the sandbox service.
- Gateway and edge identity headers, proxy authorization, and edge access
cookies remain stripped in both modes.
- Re-exposing an endpoint can update its target port and authorization mode
using the existing compare-and-swap behavior.
- Expose, get, list, and create-time responses report the effective mode;
structured CLI output includes authorization_mode.
- The setting round-trips through the public protobuf API and curated Rust,
Python, Go, and TypeScript SDKs.
- Raw authorization values never appear in errors, tracing fields, OCSF
events, or test output.
- Documentation explains the secure default, opt-in behavior, credential trust
boundary, HTTP/WebSocket semantics, and interaction with the gateway's
existing listener and TLS configuration.
Alternatives Considered
- Continue unconditional stripping. This best protects gateway identity
material but leaves standards-based authenticated sandbox applications
unusable through service URLs.
- Forward
Authorization for every service. This is simple but is an
unsafe compatibility break because existing sandbox applications would
begin receiving credentials without consent.
- Use a custom application header. This avoids overloading
Authorization, but requires application- and client-specific adaptations
and does not support servers that require standard Bearer authentication
during a WebSocket handshake.
- Translate into a new internal header. Translation changes the
application protocol and creates another security-sensitive convention;
it also does not work with unmodified application servers.
- Add a dedicated service listener, domain, certificate, or gateway-managed
application credential. These may provide stronger long-term separation
but materially expand deployment and PKI scope. They are not required for a
per-endpoint opt-in and should be evaluated separately.
Agent Investigation
No response
Checklist
User Story
As a user exposing an HTTP or WebSocket application from a sandbox, I want to
opt that individual service into receiving the caller's bearer credential so
that the application can enforce its own authentication without weakening the
default credential boundary for other services.
Problem Statement
OpenShell service routing currently removes every incoming
Authorizationheader before proxying a request to a sandbox's loopback service. That behavior
is a safe default for gateway and edge credentials, but it also prevents an
application from receiving a bearer capability intended for the application
itself. Applications that authenticate an HTTP request or the initial WebSocket
handshake therefore cannot use the standard
Authorization: Bearer ...flowthrough an OpenShell service URL.
Impact / Why This Matters
This blocks authenticated application protocols such as an app server that
checks a capability during its WebSocket handshake. The practical workarounds
are to disable application authentication, move a credential into a query
parameter or cookie, use a nonstandard header, or bypass the exposed-service
route. Those alternatives either weaken the application's security posture,
increase the chance of credential disclosure, require application-specific
changes, or give up OpenShell's managed service URL and relay path.
The feature must remain opt-in. Automatically forwarding
Authorizationwouldsilently change the security boundary for every existing exposed service and
could deliver a gateway, edge, or caller credential to an untrusted sandbox
process.
Proposed Design
Add a persisted, per-service authorization mode with two effective behaviors:
stripandbearer_passthrough. Omitted, unspecified, legacy, and unknownpersisted values resolve to
strip; unknown values in new API requests arerejected. A service explicitly configured for
bearer_passthroughmay receivezero or one syntactically valid Bearer authorization value, forwarded unchanged
to the loopback application. Duplicate, Basic, empty, and malformed values fail
with
400 Bad Requestbefore the gateway contacts the application.Surface the mode through standalone service exposure, create-time service
exposure, endpoint get/list responses, CLI commands and structured output, and
the curated Rust, Python, Go, and TypeScript SDKs. Continue stripping proxy
authorization, edge identity headers, forwarded client certificates, and edge
authentication cookies in every mode. The application—not OpenShell—validates
the bearer credential.
Acceptance Criteria
Authorizationunless the matchedpersisted endpoint explicitly selects bearer passthrough.
UNSPECIFIEDAPI values materialize or resolve toSTRIP, and alegacy stored endpoint without the new field retains stripping behavior.
INVALID_ARGUMENTbefore endpoint or sandbox persistence.
authorization value unchanged.
WebSocket frames require no authorization-header handling.
Authorizationso that theapplication can return its own authentication response.
receive
400 Bad Requestwithout contacting the sandbox service.cookies remain stripped in both modes.
using the existing compare-and-swap behavior.
structured CLI output includes
authorization_mode.Python, Go, and TypeScript SDKs.
events, or test output.
boundary, HTTP/WebSocket semantics, and interaction with the gateway's
existing listener and TLS configuration.
Alternatives Considered
material but leaves standards-based authenticated sandbox applications
unusable through service URLs.
Authorizationfor every service. This is simple but is anunsafe compatibility break because existing sandbox applications would
begin receiving credentials without consent.
Authorization, but requires application- and client-specific adaptationsand does not support servers that require standard Bearer authentication
during a WebSocket handshake.
application protocol and creates another security-sensitive convention;
it also does not work with unmodified application servers.
application credential. These may provide stronger long-term separation
but materially expand deployment and PKI scope. They are not required for a
per-endpoint opt-in and should be evaluated separately.
Agent Investigation
No response
Checklist