Cloud fast start: Leo's open Cloud branches plus fast-start work, one PR to iterate on

AU
17h ago
+5861-654

One integration PR for Cloud workspace fast start (https://github.com/manaflow-ai/cmux/issues/36050). It combines Leo's open Cloud branches with the fast-start work on top. Do not merge without the user's word. This is the single place to iterate.

Leo's branches are merged in as ordinary merge commits (no rebase, no force-push; his branches are unchanged). As his PRs move, this branch merges their new heads; when one lands on feat-cmux-next, this branch merges the base.

Included (Leo's Cloud PRs, head as last merged)

PRHeadStateWhat
https://github.com/manaflow-ai/cmux/pull/36008deaba48d3clanded on basea refused Cloud stream tells the Mac why; a failed policy read keeps the lease; a rejected reply closes at once
https://github.com/manaflow-ai/cmux/pull/360142e0d5180c7landed on basereceiver local admission while the control plane is slow
https://github.com/manaflow-ai/cmux/pull/36015f83e44edb4landed on basea chat on this Mac never says it belongs to another machine after a backend switch
https://github.com/manaflow-ai/cmux/pull/360291d22e2cfedlanded on baseCloud send from a new tab opens the chat at once
https://github.com/manaflow-ai/cmux/pull/36033fbd62b0a26landed on basemachine access exec takes the machine exec route
https://github.com/manaflow-ai/cmux/pull/36041f54fc4d6f0landed on basecmux-cloud-client, the Rust Cloud client, generated at build time
https://github.com/manaflow-ai/cmux/pull/36044c1d917354blanded on basethe owner's Native dial carries the folder copy
https://github.com/manaflow-ai/cmux/pull/36048e1cf87d69dlanded on baseCloud start: no visible login, one cached login per person
https://github.com/manaflow-ai/cmux/pull/3607193459825c6landed on basea Mac signed in to Codex gives its own machines access
https://github.com/manaflow-ai/cmux/pull/36075e0b7bf0eb5landed on base (merge 5fbb6bedf0)cmux-api: no Cloud code; cloud-env leaves cmux
https://github.com/manaflow-ai/cmux/pull/36251db0ef2f9a4landed on baseone-command dev loop on a kept Cloud machine (merge 1d1e915ba7 kept its --machine reuse with this branch's --seed)
https://github.com/manaflow-ai/cmux/pull/3626571b9f70c8blanded on basecmux-cloud-client: CI fails a new hand-written call to a route the generated client offers
https://github.com/manaflow-ai/cmux/pull/3623917334b268alanded on basedelete NEXT's dead legacy /api/vm link source
https://github.com/manaflow-ai/cmux/pull/3622272522459eelanded on base (merge ec1c0fc5fb)Pair Another Machine: invite-code pairing on the Mac
https://github.com/manaflow-ai/cmux/pull/36185landedlanded on base (merge 7aa18601f9)machines update their cmux-tui from a signed channel (see the process-scan fix below)
https://github.com/manaflow-ai/cmux/pull/36178af6636f2c8closed (the landed cloud-native-4171772 image supersedes it)cmux-vm: approve the cloud-native-a6d444a image (wrangler.jsonc takes 36178's version; nothing deploys from this branch)
https://github.com/manaflow-ai/cmux/pull/36176b786b47cdclanded on baseCloud receiver listens before its first policy read, 20 s hello, reads early on a newer token (6e96dc0a14: native_token.rs keeps the behind-policy hook with the named refusal peer_not_in_policy; host_inbound.rs reads the hello with read_line_within(&mut *stream, …); both new security tests kept)
https://github.com/manaflow-ai/cmux/pull/36136f93982dfeelanded on baseacpmux: a new remote Claude session needs a checkout or scratch folder
https://github.com/manaflow-ai/cmux/pull/35858148c57fe77openheadless Cloud link credentials (no file changes against this branch)
https://github.com/manaflow-ai/cmux/pull/3577007abc50343drafta chat moves from the relay to the direct link without a gap or a repeat
https://github.com/manaflow-ai/cmux/pull/354867d39926bbbopenacpmux: an agent never inherits another harness's login
https://github.com/manaflow-ai/cmux/pull/3607600c68920falanded on basea connect waits in the app server for its machine to run
https://github.com/manaflow-ai/cmux/pull/3609849da7bfba0landed on basecmux-vm: receiver policy names its device owners' earliest key expiry
https://github.com/manaflow-ai/cmux/pull/361086db5cacc69landed on baseCloud page: no raw machine id; Loading… only once the first read lasts
https://github.com/manaflow-ai/cmux/pull/36846563db483abdraftTakes in: relay chat gives the machine the person's harness login before the send (cloud.relay.login_* refusals). Merge 7b04461b18 keeps both new start-chat tests.
https://github.com/manaflow-ai/cmux/pull/368470681c10e9alanded on base (a4f480dbc1)Takes in: the machine's typed relay error reaches the app's answer and cloud start-chat. Merge 2f922bbf85 puts its relayFields change in CloudStartSession+RelayChat.swift, where 36846 moved the binder. (https://github.com/manaflow-ai/cmux/pull/36828, its first half, had already landed.)
https://github.com/manaflow-ai/cmux/pull/36888ea12eb0301landed on base (92ef68eb14)Takes in: after a refused start's update, start again on the new link.
https://github.com/manaflow-ai/cmux/pull/36617ded10e13b9openTakes in: cloud start-chat --name for a name no machine has yet goes relay-first (plain names only). Merge keeps 36847's typed_error and 36617's name helpers.
https://github.com/manaflow-ai/cmux/pull/36864ad44457407openTakes in: cmux-host redials its relay when the machine wakes from a pause.
https://github.com/manaflow-ai/cmux/pull/3685497a63f215eopenTakes in: the cmux-host updater never restarts acpmux while an agent is starting.
https://github.com/manaflow-ai/cmux/pull/367120a6a6e9b11openTakes in: no Cloud config request before the first window (launch perf).
https://github.com/manaflow-ai/cmux/pull/3648734bf29e038openTakes in: cmux cloud start-chat/start-session --from-folder PATH. Overlap with this branch's folder copy, where this branch wins: 36487's shared copyFolder now runs this branch's copy path (the "Copy the folder to ?" ask on a refused git seed, update-then-copy on an older machine, progress lines, the store access), so the composer and --from-folder copy one way. A refused seed under --from-folder now asks in the active window; Cancel or no window refuses the start (36487 alone copied silently). Strings follow 36487's move to CmuxNextCloudUI (public), with 36846's relay-login strings carried there; the relay Rust follows its move to cloud_start_chat/relay.rs with 36846/36847/36617's code. Merge daadd25e26.

https://github.com/manaflow-ai/cmux/pull/36165 (login under every team, scopes read at once) landed on base and comes in through the merge at 0f28294154, which resolves its conflicts to the landed code. https://github.com/manaflow-ai/cmux/pull/36037 was already merged into feat-cmux-next (f5f0e816) and comes in through the base.

Where the branch stands

The first block of Leo PRs above has landed except https://github.com/manaflow-ai/cmux/pull/36075; the PRs from 36251 down were merged at their open heads on 2026-11-05. Each landed one came in through a merge of the base, and its files take the landed version. At 0281bcf9ba the branch differs from feat-cmux-next only by 36075 (cmux-api Cloud code removed, cloud-env leaves cmux) and the fast-start commits below; no Swift differs.

Earlier conflict resolutions are superseded by the landed code:

  • The 36014 × 36008 receiver-policy merge.
  • The 36015 old head's formerLocalHosts. 36015 landed rewritten, on AgentPaneHostKind.
  • The 36008 head this branch had merged (deaba48d3c, with receiver_owner_renewal.rs). It landed from da154c125e.

0281bcf9ba returns those files to the base.

Take-backs (from https://github.com/manaflow-ai/cmux/pull/36075)

Takes back #25682: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #27920: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #28227: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #28276: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #28446: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #28781: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #29457: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.
Takes back #30469: these cmux-api Cloud files are deleted on purpose by Leo's https://github.com/manaflow-ai/cmux/pull/36075 (included here): NEXT Cloud's backend lives in cmux-platform, CLOUD_DO already names cmux-platform's class in every environment, and the Cloud paths already forward there, so the local copies served nothing.

Left out, and why

Fast-start work on top

Plan and measured timings: https://github.com/manaflow-ai/cmux/issues/36050#issuecomment-6648986000. Measured on dev (7 journeys, medians): create machine 16.5 s, running 2.7 s, first exec 3.0 s, login copy 10.9 s, start agent 3.7 s; raw Freestyle create 0.4–3.7 s. Each authenticated hop through cmux-api → cmux-platform → cmux-vm costs 2–3 s, which is where the create time goes.

CommitWhat
e629413a1d (red), 1fd90bedeaSecurity: a chat's folder copy now keeps every untracked secret the git seed keeps. Before, only .env* stayed; an untracked id_rsa, *.pem, .p12, .netrc, or a token .npmrc/.pypirc went to the machine.
6201ddfd7c, 1447ab7dccThe shared secrets rule never opens a registry file name on a FIFO, device or symlink (it hung the walk), reads at most 1 MiB and keeps a longer file here; Share mode never reads.
2fa3b4465f, 5b4e28b3d8acpmux flushes the guest's writes (sync, ≤ 2 s, one at a time) before it reports nothing at work, so a paused machine's saved disk has the whole transcript (measured: 1 of 7 lines without, 7 of 7 with). An idle line whose flush overlapped a new turn waits for the next tick.
2252b3ffa3, 5b4e28b3d8cmux_cloud_client::transcript: tails a chat transcript (JSONL) from its machine's files by byte range (VM API files route over Freestyle fs/read), not a live stream through the machine. Closed 4 MiB chunks, partial lines kept, overlong lines dropped, shrunk / missing files handled, 206 start checked.
f16d6343f5(superseded) a codegen flag to drop progenitor's panicking Client::new; moot once 36041 generates the client at build time, and dropped in the merge of its new head.
d8f468019fcmux-vm: the receiver projection test's fixture carries the ownerExpiresAt field that https://github.com/manaflow-ai/cmux/pull/36098 makes required (it failed cmux-vm-schema-gate).
6fc2c750ddCloud journey: --seed none|auto, and ready_ms (create machine → agent running) in the summary and probe row.
3cc0b882bdrustfmt only.
af45281748CI guard entries for the merged branches (36071's namespace type and auth.json bound, 36075's removed backend files) and the idle flush's 2 s budget.
d6ea3ea9f5cmux-cloud-client (a new crate here) has no unchecked arithmetic or truncating casts: 0 hits for the panic ratchet, 19 lib tests pass.
0f28294154merge of origin/feat-cmux-next (36008, 36033, 36041, 36048, 36098, 36108, 36165 landed); on rbx it checks clean and the cmux-cloud-client (19) and link refusal (22) tests pass.
ee21c373f0ghostty-next gitlink back to the base pin 12ec155e1e (https://github.com/manaflow-ai/cmux/pull/36036); this branch had kept the older 6cc749a8a5.
1037d84b5cmerge of origin/feat-cmux-next; client_key's fallback resolved to the landed fallback_key_bytes (https://github.com/manaflow-ai/cmux/pull/36205).
1e6346ebfethe merge left two identical copies of native_token_receiver_names_the_check_that_refused (https://github.com/manaflow-ai/cmux/pull/36008 landed it at another spot); keeps the landed one. rbx: clippy --all-targets -D warnings on the changed packages is clean, and cmux-link native_token_receiver passes 23 tests.
dee6d35a30merge of origin/feat-cmux-next: https://github.com/manaflow-ai/cmux/pull/36071 landed, and its five conflicted files take the landed version.
0281bcf9bamerge of origin/feat-cmux-next (36015 landed); the superseded heads of 36015 and 36008 go back to the base (see above).
5363468d6athe App-lines exception goes: 36048 and 36071 landed, this branch is at app_lines −18, and the gate passes with no skip.
89b612211athe transcript tail takes after.get(1..), not &after[1..] (panic ratchet, indexing_slicing).
71527109c8 (red), 9b78828d47from the correctness review (https://github.com/manaflow-ai/cmux/pull/36084#issuecomment-6651669303): a failed later chunk hands over the lines already read, with more set; a failed first request is still the error.
0a7f5dd320https://github.com/manaflow-ai/cmux/pull/36185's restart after a self-update walked every process in /proc (the gate's NO-PROCESS-SCAN rule refused the branch). systemd now lists the running cmux-*.service units and each one's ExecStart; a unit whose program is a cmux-tui restarts. The health check's single-pid exe reads are a reviewed keep row. rbx: cmux-host clippy clean, updater 13 passed.
e0d868a9cc (red), 333ca2c0aathe git folder copy leaves untracked heavy folders (node_modules and the like, the rule the plain copy already used) on the Mac. Red: a_git_copy_leaves_untracked_heavy_folders_here ("node_modules/left-pad/index.js went to the machine"); green: link::sync_cli 36 passed, clippy clean.
641e8e9a21 (red), 206cea36e4a copy too big for the machine is refused before it starts, in words (HQ's 12.8 GB copy, mostly an untracked out/, hung the Cloud start on a 16 GB machine).
f90f92ef1d, df5fefb766, c4518d3539merges of origin/feat-cmux-next. CloudService.unavailableReason keeps the managed-team check, the debug fixture and the file-URL backend, and drops the legacy link source that https://github.com/manaflow-ai/cmux/pull/36239 deletes. bake.ts imports both aptUpdateCommand and SELF_UPDATE_OFF. wrangler.jsonc and the send-path cap allowlist take the base (the landed cloud-native-4171772 image supersedes 36178's).
f81f25e639 (red), 506e19b2e8a manual Claude recovery installs the cached login unless the machine already runs it.
f4678dc49c (red), af0d8941faa new Cloud workspace gets the person's cached Claude login with no dialog. cmux-ci green at df5fefb766 (job 4b16826385ba78e76e30ff69).
b6671424c6, b62589bf8d, 4122af8c67 (red); 2029d12db3, b96e534643, 4e454dcea2, 6d351cf673, 933ce53113, 6109f9bacc, 4c594cfb18a Cloud chat start's refused git seed answers seed_refused instead of copying silently, and the Mac asks "Copy the folder to ?" first. A submodule's uncommitted changes no longer refuse the seed.
9f22bf8def, 87c7fc4e4e (red), 9609f1d504the seed leaves untracked heavy folders and files over 5 MiB on the Mac and names each one.
67602b39d5, 61c569d3cd (red), 524ebf3f8c, 24b3da190aa refused seed answers its fallback_code and the copy size, and the App logs them.
6037fe315c (red), b650189ed6a private repo's refused seed offers Connect GitHub, then Retry.
761b2ff5ca (red), ea01486debCloud picker: one Cloud row. "Sign in for Cloud" shows only once the app says signed out, and it takes Cloud's place (before, it showed next to Cloud while signed in). Red: 25 pass, 5 fail; green on Freestyle: 154 pass.
4c450cf1d3, 6b4093e939CloudSeedRefusal imports its public API's modules publicly (the fleet build at c4518d3539 failed on it); its tests import Foundation.
e3efb347c1panic ratchet: five new slices read with .get() (cmux-host updater/role.rs ×2 and updater/store.rs; cmux-tui link/start_chat_cli.rs and link/sync_cli.rs). rbx: fmt clean, clippy -D warnings clean, updater 13 passed, link:: 267 passed plus one flaky test (a_retry_of_a_copy_that_did_not_finish_resumes_its_folder) that then passed 3 of 3.
81bdc1d002, e0c42c59ddmerge of origin/feat-cmux-next (bake.ts keeps the base's ./layers import and this branch's SELF_UPDATE_OFF); the merge kept two identical copies of native_token_receiver_reports_a_newer_peer_binding_as_policy_behind_and_still_refuses (E0428), and e0c42c59dd drops the second. rbx: clippy -D warnings clean on every changed crate, cmux-link native_token_receiver 24 passed.
5fbb6bedf0merge of origin/feat-cmux-next: https://github.com/manaflow-ai/cmux/pull/36075 landed at its final head e0b7bf0eb5, and its four backend/apps/api files take the landed version (every branch commit on them was 36075's own); https://github.com/manaflow-ai/cmux/pull/36136 landed.
7aa18601f9merge of origin/feat-cmux-next: https://github.com/manaflow-ai/cmux/pull/36185 landed with Leo's later changes (the COMMIT lock, daemon_running in mod.rs, a --no-block acpmux restart). The updater files take the landed version, then this branch's systemd restart (0a7f5dd320) and .get(..12) reads (e3efb347c1) again. rbx: cmux-host fmt and clippy clean, updater 14 passed; check-no-process-scan ok.
e9c2a97d34, ec1c0fc5fbmerge of origin/feat-cmux-next: https://github.com/manaflow-ai/cmux/pull/36222 landed, and its files take the landed version (the TRANSITIONAL marker on PairInviteCode.swift goes with it).
02f44ec2c1AgentSessionCarrierSwitchTests wait on heldCalls == 1 instead of a 50 ms and a 100 ms sleep (determinism gate). cmux-ci step 997d39806aff08805c82024d passed.
8c3b745916, fcd98f7944merge of origin/feat-cmux-next (https://github.com/manaflow-ai/cmux/pull/36495 landed short_hex and the streamed file hash in the updater; the base's version is kept); computer-picker-onboarding.test.ts expects one "Sign in for Cloud" row in Cloud's place when signed out.
3e9d593348merge of origin/feat-cmux-next for the base fixes of two reds: clippy's missing ; (https://github.com/manaflow-ai/cmux/pull/36597) and the Cloud journey's Zig 0.17 (https://github.com/manaflow-ai/cmux/pull/36618).
53042b2512, abbfb12577merge of origin/feat-cmux-next, then 48439bf6c6 from https://github.com/manaflow-ai/cmux/pull/36647: the macOS build of acpmux failed on the base since https://github.com/manaflow-ai/cmux/pull/36564 (crate::platform is Windows-only; dev build job 4aed088bf06e589b164bc0f8, E0433).
ce308df8b6merge of origin/feat-cmux-next: acpmux/src/cli/entry.rs takes the base (https://github.com/manaflow-ai/cmux/pull/36647 landed); updater/tests.rs keeps the branch's two systemd restart tests and the base's linux gate.
7b04461b18 … daadd25e26merges of the eight Leo PR heads above. rbx at daadd25e26: fmt and clippy -D warnings clean, cloud_start_chat 47 passed.
24cb4e863amerge of origin/feat-cmux-next: 36847 and 36888 landed squashed with the content already here; the three conflicted files keep this branch's version.
0880f5ed84one shared login-refusal mapping from CloudHarnessLoginDecision.Reason to CloudStartSessionStrings.LoginRefusal (Swift compile fix after the 36487 string move).
ebee84a363call the mapping as Self.loginRefusal from the instance method.
6b10de72a7regenerated plans/cmux-next/action-surfaces.json for the fromFolder argument on start-session.

Freshness of the transcript read: a running machine's writes are readable at once (30–100 ms a read); a paused machine is read without waking it, from its saved disk (complete with the flush above).

Owners (plans/cmux-next/OWNERS.md)

  • PL-acpmux (cmux-tui/crates/acpmux/): daemon/cloud_activity.rs now runs sync(2) (≤ 2 s, one at a time) before it reports nothing at work, and holds the idle line when a turn starts during the flush. The single-flight flag is cleared inside the blocking closure.
  • PL-daemon (cmux-tui/crates/cmux-tui-core/): git_seed/secrets.rs reads a registry file name only if it is a regular file (symlink_metadata, then O_NOFOLLOW|O_NONBLOCK with an fstat recheck), and reads at most 1 MiB; a longer file stays on the Mac. The chat folder copy now uses this same rule.

Tests (rbx, Freestyle)

  • RED at e629413a1d: a_copy_keeps_every_untracked_secret_the_seed_keeps fails ("git=true: id_rsa went to the machine").
  • GREEN: see the latest comment for the exact head's run (cmux-tui-core git_seed::secrets, cmux-tui sync_cli + native_process, acpmux cloud_activity, cmux-link native_token_security + receiver_owner_renewal, cmux-host cloud::receiver, cmux-cloud-client transcript, clippy -D warnings, regenerate-and-compare of generated.rs).
  • bun test scripts/cmux-next/cloud-journey.test.ts on a Freestyle node VM: 16 pass, twice.

Guardrail skip (ended)

Pushes from 333ca2c0aa to e9c2a97d34 skipped action-wait and app-size for hits only in https://github.com/manaflow-ai/cmux/pull/36222; it landed, and pushes since then run every guardrail.

Known red: Check the app FFI pin

The branch changes app FFI sources (cmux-cloud-start-core/src/machine_login.rs and the cloud_recovery_refreshes call in cmux-layout-reducer-ffi, from 506e19b2e8). app-ffi-release.yml publishes only from feat-cmux-next, and its repin job pins the new release after the landing push, so this check stays red on the PR. Until then the app's pinned FFI does not know cloud_recovery_refreshes, and CloudMachineLogin.recoveryRefreshes falls back to true (a manual recovery runs setup-token, the earlier behavior).

Dev build

Job 58395e1c65c6ed685ee2c028 at ebee84a363, tag 36050-cloud-fast-start-master, published to the Tag Opener; development Cloud environment. The head 6b10de72a7 only adds a regenerated JSON file, so the app code is the same.

Next

  • Open decision: keep one paused machine per person (about 7 s to a running agent, about $1 per person per month) or fresh machines only (about 31.6 s median; a spare claim is 22–25 s).
  • Start the keyed agent session while the folder copies (75 s prepare today, run after the copy).
  • cmux-platform stage timings and fewer serial cmux-vm hops in the create pass (private repo, dev only).
  • Render the Cloud chat from the transcript tail in the app.

🤖 Generated with Claude Code