fix(cli): give runtimeForApp a timed HTTP client - #1881
Conversation
Product run, connect, and status paths load backends through runtimeForApp, which left Runtime.HTTP unset. Providers then fell back to http.DefaultClient and could hang on a stalled control plane. Set a 60s whole-request client on that runtime and on RuntimeForProviderOperation via a shared helper. Signed-off-by: Sebastien Tardif <SebTardif@ncf.ca>
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed September 5, 2026, 8:47 AM ET / 12:47 UTC. ClawSweeper reviewWhat this changesAdds a shared 60-second HTTP client to both CLI runtime constructors and tests their client settings. Regression provenancePossible regression — suspected (reviewed change). No predecessor PR is attributed. Merge readiness⛔ Blocked before merge - 6 items remain Request changes. The patch overrides established provider timeout ownership, and the supplied evidence does not establish an uncovered control-plane hang. Priority: P2 Review scores
Verification
How this fits togetherCrabbox’s CLI passes a runtime to provider adapters for remote lifecycle operations, command execution, and file transfers. Those adapters distinguish short control requests from longer commands and uploads. flowchart TD
A[CLI commands] --> B[Runtime constructors]
B --> C[Provider adapters]
C --> D[Finite control requests]
C --> E[Commands and file transfers]
D --> F[Provider request deadlines]
E --> G[Caller controlled lifetimes]
Before merge
Findings
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Retain adapter-owned control deadlines and caller-owned streaming lifetimes, adding a narrow timeout fix only for a demonstrated uncovered request. Do we have a high-confidence way to reproduce the issue? Yes for the introduced regression: the CLI-supplied client reaches E2B’s process stream and imposes a 60-second limit on a longer command. The reported current-main hang is not established by the named providers’ deadline-bearing request paths; no runtime reproduction was executed. Is this the best way to solve the issue? No. A shared whole-request timeout overrides existing control/data separation; any remaining gap should be repaired at the specific adapter request boundary. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 834004498309. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
|
steipete
left a comment
There was a problem hiding this comment.
Thanks for following through on the timeout work. The remaining issue is the scope of the default: a client installed on Runtime.HTTP is also an injected data-plane client, not only a control-plane client.
In current main, E2B's e2bHTTPClients returns the injected client for both control requests and envd operations. Daytona's archive upload and toolbox paths also inherit Runtime.HTTP. A global 60-second http.Client.Timeout therefore caps legitimate longer commands, response streams, and archive uploads even when their own operation budget is longer; that timeout includes reading the response body. This reverses the control/data separation preserved in #1750.
Please keep the runtime injection contract unchanged and narrow the fix to a demonstrated finite provider request that escapes its operation-specific bound. Nil Runtime.HTTP by itself is not evidence of an unbounded operation: adapters also impose request contexts and SDK deadlines. The named examples need to be checked at that boundary against current main.
The regression test should drive the production runtime/backend wiring: a stalled finite request must end, while a stream or upload that legitimately outlives the control timeout must finish, and caller cancellation must still work. Printing the constructor's timeout and running --help do not establish those behaviors.
I cannot land #1881 with this global timeout because of the long-operation regression. A focused adapter fix with that behavior proof remains welcome.
Agreed. Closing this PR. A client installed on I rechecked the named nil-HTTP fallbacks on current
I did not find a finite control request that escapes those bounds. If one shows up later, I will send an adapter-only patch with stalled-request and long-operation HTTP proof. |
What Problem This Solves
Fixes an issue where users running
crabbox run,connect,status, and other product commands against HTTP providers could hang forever when the provider control plane stopped responding. Those commands load backends throughruntimeForApp, which leftRuntime.HTTPunset. Providers such as opencomputer, crownest, superserve, opensandbox, unikraftcloud, and cloudrunsandbox then fell back tohttp.DefaultClient, which has no timeout.The same hole exists on
RuntimeForProviderOperation, used for provider lifecycle work that is not already bound to a configured backend.Why This Change Was Made
Give both constructors a shared 60-second
http.Client, matching the finite-control clients already used in Azure and Hetzner helpers. A singledefaultRuntimeHTTP()helper keeps the two paths from drifting.This does not clone
http.DefaultTransport. #1750 already bounded Daytona control-plane calls inside the Daytona adapter. This change covers the remaining unsetRuntime.HTTPconstructors on the product CLI path.User Impact
Stalled provider control-plane requests on the main CLI path now fail after 60 seconds instead of hanging until the process is killed. Operators get an error they can retry or diagnose. No config or flag changes.
Evidence
Live
goprogram calling the exported provider-operation runtime after the patch, plus the builtcrabboxbinary from this tree:The same helper is used by unexported
runtimeForApp, which is whatrun,connect, andstatuspass intoloadBackend. Before the patch,HTTPwas nil on both constructors, so those providers usedhttp.DefaultClient(Timeout0s, no deadline).Real behavior proof
Behavior or issue addressed: Product CLI runtimes omitted HTTP, so provider control calls could hang forever on a stalled host via
http.DefaultClient.Real environment tested: Windows 11, Go 1.27.0 windows/amd64, worktree at
fix/runtime-timed-http-clientbased on origin/main6172f518.Exact steps or command run after this patch: Built
crabboxfrom this tree withgo build, then ran a smallgoprogram in the same module that callscli.RuntimeForProviderOperationand prints the client timeout next tohttp.DefaultClient.Evidence after fix: terminal output from the patched tree:
Observed result after fix: The exported provider-operation runtime now carries a 60-second HTTP client that is not
http.DefaultClient. DefaultClient remains unbounded (0s), which is the hang that providers used when HTTP was nil.What was not tested: Live opencomputer, crownest, or superserve control-plane outage. Multi-minute Daytona archive uploads and E2B envd streams against a real host.
Related
runtimeForAppin494f3a4d(2026-05-06)RuntimeForProviderOperationadded in feat(provider): add Machine0 support #1405