Summary
Define a uniform semantic contract for the gateway callback endpoint used by sandbox supervisors, and separate it from gateway listener ownership, driver-specific route materialization, and credential delivery.
This issue intentionally does not select a protobuf shape or delivery mechanism. Its purpose is to reach maintainer agreement on the boundary that any implementation must satisfy across built-in and external compute drivers.
Problem
Every supervisor-backed compute driver must arrange for its sandbox supervisor to connect back to the gateway through OPENSHELL_ENDPOINT. The compute-driver protocol does not currently define where this callback URI comes from, what it means, or which component owns making it reachable.
The gap is filled differently for each built-in driver:
| Driver |
Callback endpoint |
Route materialization |
Listener ownership |
| Docker |
Constructor defaults and rewrites the host and port, including explicitly configured values |
Managed bridge plus host.openshell.internal; bridge IP or host-gateway |
Concrete in-process driver reports extra addresses through gateway_bind_addresses() |
| Podman |
Explicit value or host.containers.internal:<gateway-port> |
Podman host aliases; behavior differs across pasta, bridge, Podman machine, and a containerized gateway |
Primary listener on main; #2111 proposes a second concrete-driver hook |
| Kubernetes |
Helm-derived Service DNS name or explicit override |
Kubernetes Service and DNS |
Deployment and Service configuration |
| VM/libkrun |
Gateway-supplied value; loopback rewrites to host.openshell.internal |
gvproxy maps the guest alias to host loopback |
Primary listener |
| VM/QEMU |
Host rewrites to the per-sandbox TAP host IP |
TAP route plus a gateway-port firewall rule |
No matching listener hook; a loopback-only bind appears insufficient |
| Extension |
Not present in compute_driver.proto |
Out of band |
Undefined |
This makes callback reachability a built-in construction concern rather than part of the common driver contract. It also encourages Docker-like listener exceptions to be copied into other in-tree drivers, as #2111 demonstrates.
Docker and Podman are the immediate compatibility baseline. A design is not sufficient if it only enables external or future drivers while leaving either local container driver on a separate callback-endpoint or listener path. Their route materialization may remain runtime-specific, but the endpoint semantics and gateway ownership boundary must be shared.
Terminology
- Compute-driver control endpoint: the Unix domain socket or transport implementing
ComputeDriver; the gateway initiates these calls.
- Gateway callback endpoint: the HTTP(S) URI used by the sandbox supervisor; the sandbox initiates these calls.
- Advertised callback endpoint: the URI supplied to a driver.
- Effective callback endpoint: the workload-visible URI ultimately injected as
OPENSHELL_ENDPOINT after any permitted transformation.
- Gateway listener: a local address bound by the gateway process.
- Route materialization: driver-specific DNS, host alias, NAT, forwarding, Service, bridge, or proxy work that connects the effective endpoint to a listener.
- Credential delivery: sandbox JWT and TLS material; separate from endpoint selection.
- Listener authorization: the gateway/operator decision permitting a process to bind a network address.
callback endpoint is preferable to grpc_endpoint: the same gateway endpoint carries policy/configuration, inference bundles, logs, relay traffic, token operations, and supervisor control streams. It is not the compute-driver control endpoint.
Technical findings
Shared protocol gap
proto/compute_driver.proto:18-56 defines the service and an identity-only GetCapabilities response.
proto/compute_driver.proto:79-97 carries the per-sandbox JWT but no callback endpoint.
ValidateSandboxCreateRequest and CreateSandboxRequest only carry DriverSandbox (proto/compute_driver.proto:210-239).
- External-driver configuration accepts only
socket_path (crates/openshell-server/src/compute/driver_config.rs:98-113).
- RFC 0001 expects driver APIs to use version and capability negotiation (
rfc/0001-core-architecture/README.md:179-183), but the current handshake cannot negotiate this feature.
Gateway listener coupling
- Driver construction occurs before listeners bind (
crates/openshell-server/src/lib.rs:249-265).
ComputeRuntime stores extra bind addresses outside the shared driver API (crates/openshell-server/src/compute/mod.rs:260-324).
- Docker's concrete method is called before trait erasure (
crates/openshell-server/src/compute/mod.rs:339-369).
- The primary and driver-derived listeners all serve the same
MultiplexService with the same TLS configuration (crates/openshell-server/src/lib.rs:384-394,525-692).
- Sandbox JWT principals are method-allowlisted within that shared service (
crates/openshell-server/src/multiplex.rs:596-615); this is distinct from client-certificate authentication.
#2111's proposed Podman path also performs a bind-and-drop preflight before the gateway later binds the address for real. That is a time-of-check/time-of-use window and leaves authoritative bind failure ownership unclear.
Driver-specific behavior
- Docker route derivation and listener reporting:
crates/openshell-driver-docker/src/lib.rs:353-447; explicit URI authority rewrite: :2422-2432.
- Podman endpoint derivation:
crates/openshell-driver-podman/src/driver.rs:265-287; protected environment injection: container.rs:347-400.
- Kubernetes Service URI derivation:
deploy/helm/openshell/templates/_helpers.tpl:138-144; driver config rendering: gateway-config.yaml:112-120.
- VM/libkrun and VM/QEMU rewrites:
crates/openshell-driver-vm/src/driver.rs:871-877,4031-4070.
- The VM firewall extracts only
Url::port(), so a URI without an explicit port can become port 0 in the TAP rule.
- The supervisor derives plaintext versus mTLS behavior from the URI scheme (
crates/openshell-core/src/grpc_client.rs:125-175); the URI host is also the effective TLS/SNI identity.
architecture/compute-runtimes.md:33 is stale: it says Docker uses host networking, while the implementation uses a managed bridge and, on native Linux, an extra listener.
Minimum contract to define
The chosen design should settle these semantics independently of its protobuf or configuration shape:
- The callback endpoint is an absolute
http:// or https:// URI.
- Port semantics are unambiguous; decide whether an explicit nonzero port is mandatory.
- Legal URI components are defined, including base path, userinfo, query, and fragment behavior.
- The endpoint contains no credentials.
- Its effective authority is stable for the sandbox lifetime unless an update mechanism is defined.
- HTTPS authority, SNI, CA trust, and server certificate SANs agree.
- Sandbox JWT identity remains distinct from transport mTLS.
- User/template environment cannot override the driver-injected endpoint.
- Listener authorization is not implied by route materialization or access to the driver control socket.
- Explicit configuration provenance is retained; a driver does not silently reinterpret an operator URI without defined semantics.
- Failures distinguish invalid URI, unsupported topology, unauthorized listener request, bind failure, and post-start reachability failure.
- The same semantic contract works for built-in and external drivers.
- Drivers that do not launch a supervisor can represent that capability explicitly.
- The endpoint's global, per-driver, or per-sandbox scope is defined before multi-driver routing lands.
- Startup ordering is defined, including whether dynamic or port-zero listeners can be advertised.
- Existing sandboxes can reconnect across gateway or driver restart.
- Docker and Podman both implement the contract without retaining separate in-process listener semantics.
Responsibility boundaries
The design should keep these responsibilities separate:
- The gateway or deployment selects and serves an advertised endpoint, issues sandbox identity, and authorizes listeners.
- The compute driver validates and materializes a runtime-specific route, injects the effective endpoint, and provisions runtime-appropriate credential references or material.
- The supervisor parses the URI, applies scheme/TLS semantics, authenticates with sandbox-scoped credentials, and reconnects without changing the endpoint's meaning.
Neutral alternatives
No alternative is preferred by this issue.
- Deployment/operator-owned endpoint and listeners: drivers receive equivalent static configuration and only inject/materialize it.
- Endpoint in each create request: add a typed descriptor to
DriverSandboxSpec or CreateSandboxRequest.
- Driver initialization RPC: provide deployment callback information once per driver connection.
- Gateway-advertised endpoint candidates: provide an ordered set from which the driver chooses a materializable route.
- Authorized driver-reported listener requirements: formalize the Docker pattern through a policy-constrained common contract.
- Driver-owned forwarder or NAT: keep a fixed gateway listener and let the runtime map a sandbox-visible address to it.
- Dedicated callback-only listener/API: expose only supervisor-callable methods on a separate surface.
- Standard logical hostname: require runtimes to materialize a conventional name such as
host.openshell.internal.
These alternatives have different HA, security, startup, compatibility, and deployment consequences and may not be mutually exclusive. Listener negotiation, if retained, should be evaluated as a separately authorized capability rather than an implicit consequence of endpoint advertisement.
Affected components
| Component |
Likely impact |
| Driver protocol |
Endpoint semantics, optionality, and version/capability negotiation |
| Gateway startup |
Listener ownership, initialization ordering, and endpoint provenance |
| Docker/Podman/Kubernetes/VM |
Existing grpc_endpoint behavior and route materialization |
| External drivers |
First documented way to obtain callback configuration through the common boundary |
| Supervisor client |
URI, TLS identity, and reconnect invariants |
| Packaging and Helm |
Advertised address, Service/listener exposure, and migration of existing settings |
| Documentation and e2e |
Common contract plus runtime-specific conformance coverage |
Scope assessment
- Issue type:
refactor
- Complexity: High
- Confidence: Medium
- Estimated implementation surface: 15–25 files, depending on the selected protocol and migration model
The missing boundary is clear. Complexity remains high because a uniform implementation crosses gateway startup/listening, protocol compatibility, all four current drivers, supervisor TLS semantics, packaging, Helm, and end-to-end coverage.
Risks and open decisions
Security
All current listeners expose the same multiplexed gateway service. Application authorization limits sandbox JWT methods, but an extra listener still expands the TLS/parser/router and user API attack surface. A remote driver's control-socket access must not automatically authorize raw host binds.
The URI itself has no direct SELinux/AppArmor impact. Particular implementations can introduce labeling, socket ownership, capabilities, firewall/NAT, user-namespace, and TLS-secret concerns; #1909 remains relevant to containerized Podman.
High availability
A replica-local endpoint or listener is unsuitable for reconnect after replica failure. A stable Service or load balancer helps, but session affinity/shared state and the readiness work in #1951 remain related design constraints.
Configuration and compatibility
bind_address cannot be mechanically converted to a sandbox-visible URI: wildcard and loopback addresses, NAT, Service DNS, TLS identity, and container namespaces differ. Migration must cover all four driver grpc_endpoint fields, Helm server.grpcEndpoint, Docker's concrete listener hook, and older external drivers.
Human decisions required
- Is the authoritative endpoint global, per driver, or per sandbox?
- Is it one endpoint or ordered candidates?
- May a driver rewrite an explicit authority, or only make it routable?
- Does listener negotiation belong in this contract, a separately authorized contract, or nowhere?
- Does the callback use the full gateway surface or a callback-only surface?
- Is delivery out of band, initialization-time, or per-create?
- What capability/version negotiation is required for existing external drivers?
- Must the URI include an explicit nonzero port, and which other URI components are valid?
- How are dynamic listener ports and driver-before-listener startup ordering handled?
- What stable endpoint is required for HA and supervisor reconnect?
- What happens to running sandboxes if the endpoint changes?
- Is VM/QEMU's TAP-host listener requirement supported behavior or a separate bug?
- What migration/deprecation path applies to
grpc_endpoint and Docker's hook?
Test impact
A selected implementation should cover:
- URI validation, TLS identity, default/explicit ports, and endpoint provenance.
- Protocol compatibility and capability negotiation for older external drivers.
- A fake external driver receiving the same callback contract as a built-in.
- Listener authorization, de-duplication, wildcard coverage, and atomic bind failure.
- Per-driver environment injection and route transformation.
- HTTP, HTTPS, mTLS, and sandbox JWT authorization as distinct concerns.
- Restart/HA behavior for existing supervisors.
- E2E on native Linux Docker bridge, Docker Desktop, containerized Docker, rootless Podman pasta/bridge, Podman machine, containerized Podman on SELinux-enforcing Fedora, Kubernetes Service/ingress/HA, VM libkrun/gvproxy, VM QEMU/TAP/GPU, and a fake external driver.
- Future Apple/container and Windows drivers when they exist; no Apple/container compute driver exists on current
main.
Documentation impact
Likely updates include architecture/compute-runtimes.md, architecture/gateway.md, driver networking documentation, docs/reference/gateway-config.mdx, sandbox compute-driver documentation, Helm values/templates/tests, packaging configuration, and external-driver protocol documentation.
Related work
Expected outcome
Maintainers agree on the terminology, responsibility boundaries, minimum semantics, and required decisions above. A follow-on implementation issue can then select a protocol shape and migration sequence without adding another in-tree special case.
Summary
Define a uniform semantic contract for the gateway callback endpoint used by sandbox supervisors, and separate it from gateway listener ownership, driver-specific route materialization, and credential delivery.
This issue intentionally does not select a protobuf shape or delivery mechanism. Its purpose is to reach maintainer agreement on the boundary that any implementation must satisfy across built-in and external compute drivers.
Problem
Every supervisor-backed compute driver must arrange for its sandbox supervisor to connect back to the gateway through
OPENSHELL_ENDPOINT. The compute-driver protocol does not currently define where this callback URI comes from, what it means, or which component owns making it reachable.The gap is filled differently for each built-in driver:
host.openshell.internal; bridge IP orhost-gatewaygateway_bind_addresses()host.containers.internal:<gateway-port>main; #2111 proposes a second concrete-driver hookhost.openshell.internalcompute_driver.protoThis makes callback reachability a built-in construction concern rather than part of the common driver contract. It also encourages Docker-like listener exceptions to be copied into other in-tree drivers, as #2111 demonstrates.
Docker and Podman are the immediate compatibility baseline. A design is not sufficient if it only enables external or future drivers while leaving either local container driver on a separate callback-endpoint or listener path. Their route materialization may remain runtime-specific, but the endpoint semantics and gateway ownership boundary must be shared.
Terminology
ComputeDriver; the gateway initiates these calls.OPENSHELL_ENDPOINTafter any permitted transformation.callback endpointis preferable togrpc_endpoint: the same gateway endpoint carries policy/configuration, inference bundles, logs, relay traffic, token operations, and supervisor control streams. It is not the compute-driver control endpoint.Technical findings
Shared protocol gap
proto/compute_driver.proto:18-56defines the service and an identity-onlyGetCapabilitiesresponse.proto/compute_driver.proto:79-97carries the per-sandbox JWT but no callback endpoint.ValidateSandboxCreateRequestandCreateSandboxRequestonly carryDriverSandbox(proto/compute_driver.proto:210-239).socket_path(crates/openshell-server/src/compute/driver_config.rs:98-113).rfc/0001-core-architecture/README.md:179-183), but the current handshake cannot negotiate this feature.Gateway listener coupling
crates/openshell-server/src/lib.rs:249-265).ComputeRuntimestores extra bind addresses outside the shared driver API (crates/openshell-server/src/compute/mod.rs:260-324).crates/openshell-server/src/compute/mod.rs:339-369).MultiplexServicewith the same TLS configuration (crates/openshell-server/src/lib.rs:384-394,525-692).crates/openshell-server/src/multiplex.rs:596-615); this is distinct from client-certificate authentication.#2111's proposed Podman path also performs a bind-and-drop preflight before the gateway later binds the address for real. That is a time-of-check/time-of-use window and leaves authoritative bind failure ownership unclear.
Driver-specific behavior
crates/openshell-driver-docker/src/lib.rs:353-447; explicit URI authority rewrite::2422-2432.crates/openshell-driver-podman/src/driver.rs:265-287; protected environment injection:container.rs:347-400.deploy/helm/openshell/templates/_helpers.tpl:138-144; driver config rendering:gateway-config.yaml:112-120.crates/openshell-driver-vm/src/driver.rs:871-877,4031-4070.Url::port(), so a URI without an explicit port can become port0in the TAP rule.crates/openshell-core/src/grpc_client.rs:125-175); the URI host is also the effective TLS/SNI identity.architecture/compute-runtimes.md:33is stale: it says Docker uses host networking, while the implementation uses a managed bridge and, on native Linux, an extra listener.Minimum contract to define
The chosen design should settle these semantics independently of its protobuf or configuration shape:
http://orhttps://URI.Responsibility boundaries
The design should keep these responsibilities separate:
Neutral alternatives
No alternative is preferred by this issue.
DriverSandboxSpecorCreateSandboxRequest.host.openshell.internal.These alternatives have different HA, security, startup, compatibility, and deployment consequences and may not be mutually exclusive. Listener negotiation, if retained, should be evaluated as a separately authorized capability rather than an implicit consequence of endpoint advertisement.
Affected components
grpc_endpointbehavior and route materializationScope assessment
refactorThe missing boundary is clear. Complexity remains high because a uniform implementation crosses gateway startup/listening, protocol compatibility, all four current drivers, supervisor TLS semantics, packaging, Helm, and end-to-end coverage.
Risks and open decisions
Security
All current listeners expose the same multiplexed gateway service. Application authorization limits sandbox JWT methods, but an extra listener still expands the TLS/parser/router and user API attack surface. A remote driver's control-socket access must not automatically authorize raw host binds.
The URI itself has no direct SELinux/AppArmor impact. Particular implementations can introduce labeling, socket ownership, capabilities, firewall/NAT, user-namespace, and TLS-secret concerns; #1909 remains relevant to containerized Podman.
High availability
A replica-local endpoint or listener is unsuitable for reconnect after replica failure. A stable Service or load balancer helps, but session affinity/shared state and the readiness work in #1951 remain related design constraints.
Configuration and compatibility
bind_addresscannot be mechanically converted to a sandbox-visible URI: wildcard and loopback addresses, NAT, Service DNS, TLS identity, and container namespaces differ. Migration must cover all four drivergrpc_endpointfields, Helmserver.grpcEndpoint, Docker's concrete listener hook, and older external drivers.Human decisions required
grpc_endpointand Docker's hook?Test impact
A selected implementation should cover:
main.Documentation impact
Likely updates include
architecture/compute-runtimes.md,architecture/gateway.md, driver networking documentation,docs/reference/gateway-config.mdx, sandbox compute-driver documentation, Helm values/templates/tests, packaging configuration, and external-driver protocol documentation.Related work
Expected outcome
Maintainers agree on the terminology, responsibility boundaries, minimum semantics, and required decisions above. A follow-on implementation issue can then select a protocol shape and migration sequence without adding another in-tree special case.