Skip to content

sandbox download/upload (SSH ProxyCommand path) broken on macOS + Podman Machine when following the documented IPv6-loopback workaround for callback-listener conflicts #2607

Closed

Description

@grsadesso

Summary

On macOS with a local Podman Machine gateway, following the sandbox-compute-drivers.mdx guidance to fix the new callback-listener conflict in v0.0.97 breaks every sandbox operation that shells out to OpenSSH (sandbox download, sandbox upload, sandbox create without --no-keep/a command, sandbox connect). The ProxyCommand these spawn passes the gateway's advertised bind address literally as --gateway 'https://[::1]:17670', and openshell ssh-proxy's own transport layer rejects that value with invalid dns name. There appears to be no way to make the CLI advertise a hostname instead for this specific path.

Environment

  • OpenShell 0.0.97 (Homebrew formula, upgraded from 0.0.80)
  • macOS (arm64), Podman Machine (applehv, rootless, pasta network helper)
  • Gateway driver: podman (auto-detected)

Steps to reproduce

  1. Install/upgrade to OpenShell 0.0.97 via Homebrew on macOS with an existing Podman Machine.
  2. Start the gateway service (brew services restart openshell). It fails to start sandboxes with:
    Error:   × configuration error: compute driver 'podman' requested gateway callback
      │ listener 127.0.0.1:17670, but it is the same address as the primary
      │ listener; callback-only authorization cannot be preserved
    
  3. Per docs/reference/sandbox-compute-drivers.mdx ("For the IPv4-loopback callback used by Podman Machine, bind the primary listener to a distinct address such as [::1]:17670"), set in the gateway's user config (~/.config/openshell/gateway.toml, which the Homebrew service script prefers over the prefix config when present):
    [openshell.gateway]
    bind_address = "[::1]:17670"
    compute_drivers = ["podman"]
  4. Restart the gateway. It now starts cleanly and sandbox create, sandbox exec (gRPC-based) work fine.
  5. Register the CLI against a hostname (required — registering against the literal https://[::1]:17670 or https://127.0.0.1:17670 fails CLI-side operations with the same invalid dns name error described below):
    openshell gateway add https://localhost:17670 --local --name openshell
  6. Run openshell sandbox download <name> <path> <dest> (or sandbox upload, or sandbox create without --no-keep/a trailing command, which drops into an interactive SSH shell). All fail:
    debug1: Executing proxy command: exec /opt/homebrew/bin/openshell ssh-proxy --gateway 'https://[::1]:17670' --sandbox-id <id> --token <token> --gateway-name openshell
    Error:   × transport error
      ╰─▶ invalid dns name
    kex_exchange_identification: Connection closed by remote host
    Error:   × failed to resolve sandbox source path '<path>'
      ╰─▶ ssh probe exited with status exit status: 255
    

Isolating the bug

Running the exact ssh-proxy invocation from the debug log by hand:

openshell ssh-proxy --gateway 'https://[::1]:17670' --sandbox-id <id> --token bogus --gateway-name openshell
# => Error: × transport error ╰─▶ invalid dns name

Substituting a hostname for the same call gets past the transport layer entirely (fails only on the intentionally-invalid token, as expected):

openshell ssh-proxy --gateway 'https://localhost:17670' --sandbox-id <id> --token bogus --gateway-name openshell
# => Error: × code: 'The request does not have valid authentication credentials', message: "SSH session token not found"

This confirms the failure is specifically ssh-proxy's transport-layer host validation rejecting a bracketed IPv6 literal ([::1]) — not anything about the token, sandbox, or gateway auth itself. Also note: OPENSHELL_GATEWAY_ENDPOINT=https://localhost:17670 as an environment override on the outer sandbox download command has no effect — the ProxyCommand's --gateway value is still generated as the literal [::1]:17670, so it seems to come from the gateway's own advertised/session-token-embedded address rather than anything client-configurable.

Impact

Any macOS + Podman Machine gateway that applies the documented [::1] workaround for the new callback-listener validation (added in the "negotiate gateway callback listeners" work, #2492) loses sandbox download, sandbox upload, and interactive sandbox create/connect entirely. Only the gRPC-based sandbox exec still works. This is a fairly severe regression for local dev workflows that round-trip files in and out of sandboxes (e.g., building something in-sandbox and pulling the result back to the host).

Expected behavior

Either:

  • ssh-proxy's transport client should accept IP-literal hosts (including bracketed IPv6) for its own gRPC/TLS connection to the gateway, or
  • The gateway should advertise/embed a connectable hostname (e.g. localhost) rather than the raw configured bind_address when constructing SSH session info for sandbox download/upload/create/connect, so it's consistent with the primary CLI transport (which works fine against a hostname).

Suggested workaround for others hitting this

Downgrade to the last version before the callback-listener-negotiation change (0.0.96 or earlier) if you rely on sandbox download/upload/interactive sandbox create with a local Podman Machine gateway on macOS.

Activity

  1. matthewgrossman commented on Aug 5, 2026

    @matthewgrossman
    Member

    📋 triage-agent

    Triage Assessment

    Classification: validated-bug

    Summary

    This is a confirmed macOS Podman Machine regression, not a configuration error. The report provides enough concrete diagnostics to assess despite not using a separate Agent Diagnostic heading, and an independent principal-engineer review reached the same conclusion with high confidence.

    Investigation

    • The failure was reproduced from origin/main with mise run e2e:podman: the primary gateway and Podman callback requirement both resolved to 127.0.0.1:<port>, so startup correctly failed closed rather than combining the unrestricted and callback-only authorization scopes.
    • The documented IPv6-loopback workaround avoids that socket collision, but the reporter correctly isolated a second defect: SSH-backed operations retained the gateway-reported ::1, constructed https://[::1]:<port>, and failed tonic TLS setup with invalid dns name before authentication. Using localhost reached authentication successfully.
    • The code paths match the report: the Podman driver requests the macOS IPv4-loopback callback, gateway listener setup rejects an exact primary-listener collision, and resolve_ssh_gateway on the affected releases retains the raw ::1 authority when both endpoints are local.
    • PR feat(compute): negotiate gateway callback listeners #2492 / commit d220d894 introduced the callback-listener behavior in v0.0.97. The defect remains present in released versions through v0.0.99.
    • No exact duplicate was found. refactor(compute): define a uniform gateway callback endpoint contract #2215 tracks the broader callback-endpoint contract; it is not a duplicate of this concrete regression.
    • PR fix(e2e): separate Podman Machine loopback listeners #2622 is the targeted fix. It preserves the same-port authorization boundary, keeps the CLI TLS authority as localhost, adds focused regression coverage, and passed the complete macOS Podman Machine E2E suite including SSH-backed paths.

    Impact Signals

    • Affected users/scope: macOS users running a local Podman Machine gateway on v0.0.97-v0.0.99. Default startup fails; applying the documented workaround then breaks upload, download, connect, and interactive-create paths.
    • Regression: Yes, introduced by feat(compute): negotiate gateway callback listeners #2492 and first released in v0.0.97.
    • Workaround: Downgrade to v0.0.96 or avoid SSH-backed operations. The published IPv6 bind workaround alone is incomplete on affected releases.
    • Evidence quality: High. The report includes exact configuration, commands, logs, and an authentication-boundary isolation test; code inspection matches it; baseline reproduction failed as reported; and the full macOS Podman E2E suite passed with fix(e2e): separate Podman Machine loopback listeners #2622.

    Human Decision Required

    A targeted fix is already open in #2622 under direct instruction, but triage does not infer roadmap acceptance. Decide whether OpenShell should address this issue. If yes, replace state:validated with state:accepted, associate it with a roadmap item, and decide whether the work remains human-owned. If no, close it as not planned and record the rationale. Roadmap association is independent sequencing metadata.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions