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
- Add
const EnvdPort = 49983.
- In
newEnvdRequest, use s.GetHost(EnvdPort) (process/files/filesystem).
- Leave
RunCode's /execute on JupyterPort (49999).
- 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.
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 asHTTP 404 Not Foundor502 Bad Gateway, depending on what the Jupyter port fronts in a given template):Commands.Run(/process.Process/Start)Files.Read/Write/WriteFiles(/files)Filesystem.List/Stat/Exists/MakeDir/Rename/Remove/WatchDir(/filesystem.Filesystem/*)Pty(/process.Process/*, added in feat(sdk): add PTY (interactive terminal) support to Go SDK #815)Only
RunCode(/execute) works, because/executegenuinely lives on the Jupyter port.Root Cause
The SDK only defines the Jupyter port:
and
newEnvdRequest— the shared builder for all envd RPCs (process, files, filesystem) — hardcodes it as the host port: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 viaProxyNodeIP.1) Existing
Commands.Runintegration test — 404 on port 49999 (templatetpl-58f2b99af8684ca48973a61b):(Note:
RunCodeat line 90 —/executeon 49999 — succeeded; the failure is the first/process.Process/*call.)2) PTY E2E — same 404 on port 49999:
3) After pointing
newEnvdRequestat the envd port49983— full PTY lifecycle passes (create → echo → resize/stty size→ reattach → kill):Same
api/proxy/template; the only change between runs #2 and #3 wasGetHost(JupyterPort)→GetHost(49983)innewEnvdRequest.4) Same-sandbox A/B (raw HTTP, only the host port differs), template
tpl-d635607121a94ef5b9413339:Identical request; flipping the virtual-host port prefix from
49999to49983is the only change, and it flips failure → success. (This template returns502where the one in runs #1–#3 returned404— 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:
ENVD_PORT = 49983(_commands.py) for process/filesystem;JUPYTER_PORT = 49999(sandbox.py) for/execute.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
httptestserver viaProxyNodeIP, so the virtual host's port prefix never selects a backend and the wrong port is never exercised.Proposed Solution
const EnvdPort = 49983.newEnvdRequest, uses.GetHost(EnvdPort)(process/files/filesystem).RunCode's/executeonJupyterPort(49999).Commands.Run/Files/Ptyagainst 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.