Skip to content

Docker driver requires launch-scoped session JWT that local gateway does not mint (0.1.2) #3900

Description

@makkavelle2015-bit

Summary

Sandbox creation through the Docker compute driver fails on a local,
mTLS-authenticated OpenShell gateway. The driver aborts with:

the operation's execution, message: "docker sandboxes require launch-scoped
gateway authentication" event crates/openshell-driver-docker/src/lib.rs:1867

Per #2965, the launch-scoped credential is a gateway-minted sandbox/session
JWT used to authenticate the protected channel between the workload and its
companion supervisor — not a user-supplied identity. It is therefore an
internal gateway-to-driver contract, and the local gateway is not minting or
exposing it in this deployment.

v0.1.2 was published 2026-09-28, which postdates #2965 (closed 2026-09-14), so
the supervisor architecture should be present in this release. This is
reported against 0.1.2.

Environment

OpenShell 0.1.2 (v0.1.2, published 2026-09-28)
Gateway local, mTLS transport
Compute driver docker, selected and connected
Docker 29.7.2, build a7adca6a
Host Linux 6.18.33.1-microsoft-standard-WSL2
Landlock ABI 0 (WSL2 kernel) — downstream of this issue, not implicated

Gateway configuration

Accepted only with the version-2 schema. Complete non-secret configuration;
TLS material is generated outside the repo and is not included.

[openshell]
version = 2

[openshell.drivers.docker]
allow_driver_config = true

allow_driver_config governs caller-supplied template.driver_config and was
accepted. Enabling it did not change the failure.

Reproduction

openshell gateway add <local> --local
openshell gateway start <local>

# driver selection from the gateway log (verbatim):
#   openshell_server: Using compute driver driver=docker
#   driver.initialize{otel.name="driver.initialize" driver.name=docker}
#   openshell_server::compute: Compute driver connected
#     configured_driver=docker advertised_driver=docker

openshell status
#   Status: Connected
#   Authentication: Authenticated (mTLS transport)
#   Version: 0.1.2

openshell sandbox template create apex-base --image ubuntu:24.04
#   Created sandbox template apex-base

openshell sandbox create --name run-abc-worker --template apex-base \
  --policy policy.yaml --no-keep -- /bin/sh -c 'id; echo ok'

Actual result

The error above, unchanged across every variation attempted. No supervisor
companion container is observable on the Docker host.

Expected result

Any one of these would resolve the report:

  1. The local gateway mints the launch-scoped session JWT and the Docker driver
    proceeds to launch the workload plus companion supervisor.
  2. The gateway/CLI exposes a documented control for issuing that identity.
  3. The driver returns an explicit error naming the missing gateway-side
    condition, rather than referring to an identity the user cannot supply.

Ruled out

Hypothesis Observation Status
Docker driver not selected Using compute driver driver=docker; Compute driver connected configured_driver=docker Ruled out
Gateway config cannot express required driver settings v2 TOML accepted; gateway starts Ruled out
Caller driver config rejected pre-launch allow_driver_config = true accepted; error unchanged Ruled out
TLS / client authentication failure Authenticated (mTLS transport) Ruled out
Invalid template or spec construction Template created; --template path reproduces failure Ruled out
Docker host/runtime failure openshell doctor check all checks passed; docker run --rm alpine succeeds Ruled out
Feature absent from the release v0.1.2 (2026-09-28) postdates #2965 (2026-09-14) Ruled out
User-supplied launch identity #2965 defines the JWT as gateway-minted Not applicable

Questions for maintainers

  1. At which point in the local launch path should the gateway mint the
    launch-scoped session JWT for the Docker driver, and what would cause it to
    be absent here?
  2. codex/fix local driver session token e2e #3612 refers to "finite launch-scoped session JWTs" and notes legacy
    non-expiring tokens. Is there a configuration or gateway state that still
    selects the legacy contract, which the Docker driver then rejects?
  3. Since the local gateway cannot ask the user for this credential, is the
    error text actionable? Naming an identity no local user can supply is likely
    to cost the same debugging time for others.

Related validation (not the subject of this report)

The consumer's own live-runtime integration found two defects upstream of this
blocker, both fixed: process.user/process.group are not supported policy
fields in 0.1.2, and run IDs producing sandbox names over the 19-character
limit are rejected before staging. The client is not the blocker here.

Activity

  1. makkavelle2015-bit commented on Sep 29, 2026

    @makkavelle2015-bit
    Author

    Resolved. The launch-scoped failure was caused by incomplete gateway configuration, not a defect in the sandbox driver. Closing with the full causal chain, since each link masked the next.

    1. Missing [openshell.gateway.gateway_jwt]

    openshell-server only constructs a sandbox_session_jwt_authority when config.gateway_jwt is Some. Otherwise launch_authentication is None, and the Docker driver rejects the launch via .filter(|spec| !spec.launch_authentication.is_empty()). mTLS transport auth was working throughout and was never the missing piece — the gateway simply had no signing material to mint a launch-scoped JWT from.

    Adding the block (Ed25519 signing_key_path / public_key_path / kid_path / gateway_id) produced gateway-minted sandbox JWT enabled and removed the original refusal.

    2. bind_address was loopback

    The gateway listened on 127.0.0.1, which is unreachable from the supervisor container. This surfaced only after link 1, as Startup configuration fetch failed / failed to connect to OpenShell server. Setting bind_address = "0.0.0.0:17670" fixed it.

    3. grpc_endpoint

    Must be set, and because TLS materials are present it must use https://. Note the coupling in docker_supervisor_host_address: it returns Some only for an IPv4 literal or the literal localhost; any other domain returns None and no host alias is created. So an IP endpoint is what generates the host.openshell.internal / host.docker.internal aliases — using the alias name directly would silently disable the mechanism that provides it.

    4. Server certificate SANs

    The final blocker. The server certificate was CN=localhost with DNS:localhost, IP:127.0.0.1 only. The TCP probe succeeded while the TLS handshake failed, because the supervisor validates the endpoint identity and 172.21.216.231 was absent from the SANs. Re-issuing with DNS:host.openshell.internal, DNS:host.docker.internal, DNS:localhost, IP:127.0.0.1, IP:<host> resolved it.

    A sandbox now boots and executes (uid=1000, companion supervisor running).

    Observations worth considering

    • The error text for link 1 names an identity a local user cannot supply. Every link above presented as the same "failed to connect"-class symptom, and links 2-4 were only reachable sequentially. A startup preflight that validates listener reachability and certificate SAN coverage against the configured supervisor endpoint would fail fast with an actionable message instead.
    • Binding loopback while a peer container is expected to connect is a configuration state worth rejecting or warning on at startup.

    Environment: v0.1.2, Docker driver, WSL2 bare-metal gateway (not a Compose service, so Compose service DNS does not apply). Happy to provide the full working gateway.toml shape if useful.

  2. makkavelle2015-bit commented on Sep 29, 2026

    @makkavelle2015-bit
    Author

    Root cause: missing [openshell.gateway.gateway_jwt] config (plus loopback bind and certificate SAN coverage). Full chain in the comment above. Sandbox now boots and executes.

  3. removed
    state:triage-neededOpened without agent diagnostics and needs triage
    on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions