cmux NEXT Cloud: the Cloud page works on the classic link instead of saying it waits to reconnect
Leo, 2026-10-29: "let's just get the cloud and its respective ui up to snuff" and "as well as the ui usability of the cloud stuff... plus having less stupid spacing on this. imo, project and cloudness at the top."
Friction
On a fresh signed-in launch on the classic Cloud link (the release and default path), the Cloud page says "Cloud is not available until cmux reconnects." and Retry does nothing. In staging capture run 41004738189 (build of cd927d88), the page stayed that way for the whole 7-minute run, and two fresh page instances failed the same way. Over the same run the CLI listed machines, opened a terminal, paused and resumed, and the sidebar listed both machines.
Root cause
The Cloud page sends every op to the Cloud app server (apps-run cmux/cloud). That server reaches its backend only through the host credential relay, and the relay registers only on the app server link path (CloudService.startCredentialProvider, guarded on linkSource == .appServer since the cx-t2rz security review, P3-1). On the classic link, cloud.auth.status and cloud.machine.list therefore always fail with relay_unavailable, which CloudPageProvider turns into cmux.protocol.transport, which the page shows as "disconnected". Retry runs the same ops, so it fails the same way every time. The CLI and the sidebar work because they use CloudService's /api/vm path. The fixture's machines also live only in /api/vm, so even a working relay would list none of them.
Fix
On the classic link, the page now reads what the CLI and the sidebar read. The relay policy doesn't change.
CmuxNextCloud/Page/CloudClassicPageOps.swift(new, in the Cloud package): answerscloud.auth.status,cloud.machine.list,cloud.machine.watch, andcloud.plan.get(only when the list carries a machine cap), and runs start (resume), pause, rename and delete. Delete runs only with originuser, after the native sheet, as on the app server. Every other op answerscmux.cloud.unsupported, which the page already shows as not available (snapshots, files, ports, migration, teams). That includes create: the page's create is not one of the Cloud page's confirmed ops (PageDescriptor.cloud.confirmedOps), so it never reaches the provider on either link.- The app's Cloud gate holds: while
CloudService.unavailableReasonis set (turned off by the organization, no managed team, a local-only backend), every op exceptcloud.auth.statusis refused withcmux.cloud.unavailableand the reason, no/api/vmcall goes out, and the feed lists no machine. - Classic errors keep the codes the page acts on:
plan_required(withdetails.plan, so "See plans" shows),not_found(a machine that's already gone counts as done) andauth_required. Anything else iscmux.cloud.failedwith the localized text. - Live state: while the page watches, an observation of the registry's machines (no timer) turns each change into
upsert/removedevents at a new revision. That covers refreshes, CLI pause/resume, and machines added or removed. It stops when the last watcher leaves. CloudPageProvidertakes an optionalfeedfor those events. With a feed it doesn't also announce a mutation's answer, so each change reaches the stream once.PageFactory.cloudProviderpicks the classic ops on.legacyand keepsaccount.appOpson.appServer(no change there).CloudAppOps.swift: the App adapter (JSON in and out, refusal codes) and the backend built fromCloudServiceandMachineRegistry.
#27834 (open, Leo lane) makes the page re-read on its own after a link refusal and hides the disconnected state during a grace period. That's complementary: on the classic link the re-reads would still never succeed without this PR.
Risk
Medium, contained to the Cloud page on the classic link. The .appServer path is unchanged. Delete keeps the native confirmation and the person-only gate. Plan usage on the classic link shows the list's machine cap and running count. It shows no snapshot allowance (0 of 0) because /api/vm names none.
Evidence
- Red on base:
CloudClassicPageRedTests(branchleo/cloud-page-legacy-link-red, base 7986c3fb plus one test file) builds the provider the app built before (account.appOps) and callscmux.cloud.auth.statuson the classic link. Red: fleet step 2379e0df8eda6402d9f6ac48 (aws-m4pro-7) failed withPageError(code: "cmux.protocol.transport", message: "not connected to cmux-tui", retryable: true), the page's "disconnected" state. - Green at head 6d6d998f:
CloudClassicPageTests(9 tests: the app's own provider answers with no relay; list records; signed-out refusal; registry changes reach the watch stream once each and stop with the last watcher; pause/start; delete person-only and create not served; unavailable Cloud refuses every call and lists no machine; classic errors keep the page's codes; plan cap and unsupported ops), plus the existingCloudPaneTestsandPageProviderTests. All 3 suites passed: fleet step 72c14ce01e551d17fe101135 (aws-m4pro-1). - Before: staging capture run 41004738189 (build of cd927d88). The page reads "Cloud is not available until cmux reconnects. Retry" for the whole run.
- After: cloud-capture fixture route (HQ pr-capture-cloud.yml), state
cloud-audit-abwith--video, build e11de3346d005244e468837f of this head. Pending: the first attempt found no Blacksmith macOS runner on any of the four labels within 240 s each. - UI review checklist: no webview or native UI code changed. The page's existing states now get real data.
Agent session: agent session
š¤ Generated with Claude Code