Skip to content

[Bug Report] Go SDK sends envd data-plane RPCs to the Jupyter port (49999) instead of envd (49983) #816

Description

@chaojixinren

Problem

Every envd data-plane call in the Go SDK (sdk/go) is sent to the sandbox's Jupyter port (49999) instead of the envd port (49983). Against a real deployment this makes the following fail with a non-2xx error (observed as HTTP 404 Not Found or 502 Bad Gateway, depending on what the Jupyter port fronts in a given template):

Only RunCode (/execute) works, because /execute genuinely lives on the Jupyter port.

Root Cause

The SDK only defines the Jupyter port:

// sdk/go/sandbox.go
const JupyterPort = 49999

and newEnvdRequest — the shared builder for all envd RPCs (process, files, filesystem) — hardcodes it as the host port:

// sdk/go/envd.go
func (s *Sandbox) newEnvdRequest(ctx context.Context, method, path string, query url.Values, body io.Reader) (*http.Request, error) {
	target := url.URL{
		Scheme: s.client.config.ProxyScheme,
		Host:   s.GetHost(JupyterPort), // <-- should be the envd port (49983)
		Path:   path,
		...
	}
}

CubeProxy routes by the port prefix in the virtual host (<port>-<sandboxID>.<domain>), so these requests are forwarded to the Jupyter kernel gateway, which does not serve /process.Process/*, /files, or /filesystem.Filesystem/* → non-2xx (404 or 502).

Evidence (live run)

Against a real CubeSandbox (api=http://127.0.0.1:13000, proxy=127.0.0.1:11080, domain=cube.app), with the proxy DNS-bypassed via ProxyNodeIP.

1) Existing Commands.Run integration test — 404 on port 49999 (template tpl-58f2b99af8684ca48973a61b):

=== RUN   TestIntegrationSandboxExecutionCommandsFilesAndErrors
    integration_test.go:57: CubeSandbox integration target api=http://127.0.0.1:13000 template=tpl-58f2b99af8684ca48973a61b proxy=127.0.0.1:11080 domain=cube.app
    integration_test.go:150: Commands.Run returned error: Not Found (HTTP 404)
--- FAIL: TestIntegrationSandboxExecutionCommandsFilesAndErrors (2.62s)

(Note: RunCode at line 90 — /execute on 49999 — succeeded; the failure is the first /process.Process/* call.)

2) PTY E2E — same 404 on port 49999:

=== RUN   TestIntegrationPtyLifecycle
    pty_integration_test.go:60: CubeSandbox integration target api=http://127.0.0.1:13000 template=tpl-58f2b99af8684ca48973a61b proxy=127.0.0.1:11080 domain=cube.app
    pty_integration_test.go:80: pty.Create returned error: Not Found (HTTP 404)
--- FAIL: TestIntegrationPtyLifecycle (1.40s)

3) After pointing newEnvdRequest at the envd port 49983 — full PTY lifecycle passes (create → echo → resize/stty size → reattach → kill):

=== RUN   TestIntegrationPtyLifecycle
    pty_integration_test.go:60: CubeSandbox integration target api=http://127.0.0.1:13000 template=tpl-58f2b99af8684ca48973a61b proxy=127.0.0.1:11080 domain=cube.app
    pty_integration_test.go:82: PTY started, pid=27
    pty_integration_test.go:131: reattached.Wait -> code=-1 err=PTY exited with error: signal: killed exitKnown=true endErr="signal: killed"
    pty_integration_test.go:141: second Kill correctly reported not-found (false)
--- PASS: TestIntegrationPtyLifecycle (1.74s)
PASS
ok  	github.com/tencentcloud/CubeSandbox/sdk/go	1.742s

Same api/proxy/template; the only change between runs #2 and #3 was GetHost(JupyterPort)GetHost(49983) in newEnvdRequest.

4) Same-sandbox A/B (raw HTTP, only the host port differs), template tpl-d635607121a94ef5b9413339:

GET /files @49999 -> HTTP 502 (openresty "Bad Gateway")
GET /files @49983 -> HTTP 200, body = /etc/hostname contents

Identical request; flipping the virtual-host port prefix from 49999 to 49983 is the only change, and it flips failure → success. (This template returns 502 where the one in runs #1#3 returned 404 — the failure code depends on the Jupyter upstream, but the wrong-port routing is the same bug.)

Cross-SDK parity

Python and Node both keep the two ports separate:

  • Python: ENVD_PORT = 49983 (_commands.py) for process/filesystem; JUPYTER_PORT = 49999 (sandbox.py) for /execute.
  • Node: ENVD_PORT = 49983 (commands.ts) for process/pty; JUPYTER_PORT = 49999 (sandbox.ts) for /execute.

The architecture docs also state envd is health-probed on :49983.

Impact

All data-plane envd functionality of the Go SDK (commands, files, filesystem, and PTY) is unusable against real deployments. Existing unit tests do not catch this: they route through a single httptest server via ProxyNodeIP, so the virtual host's port prefix never selects a backend and the wrong port is never exercised.

Proposed Solution

  1. Add const EnvdPort = 49983.
  2. In newEnvdRequest, use s.GetHost(EnvdPort) (process/files/filesystem).
  3. Leave RunCode's /execute on JupyterPort (49999).
  4. Add an integration-tagged E2E (or extend the existing one) that actually drives Commands.Run/Files/Pty against a live sandbox so the port is covered.

This is a one-line routing fix; every newEnvdRequest-based API (including the PTY added in #815) recovers automatically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions