Remove Blaxel and the Go cmuxd-remote daemon; move Freestyle to the public platform (0.2.9)

Merged
TH
2mo ago
+1342-35611

Removes Blaxel as a Cloud VM provider and collapses the two Freestyle arms into one driver on the public platform (api.freestyle.sh, freestyle@0.2.9).

Provider removal

  • Delete drivers/blaxel.ts, images/blaxel/, build-blaxel-image.sh, test-blaxel-vm-poc.ts; drop "blaxel" from ProviderId, the driver registry, the create kill switch, and the image manifest.
  • defaultProviderId() is now "freestyle" (load-dev-env.sh follows).
  • A migration rebuilds the vm_provider enum without 'blaxel', rewriting surviving rows to 'e2b' first. The trailing DROP TYPE is the interlock: a missed column aborts the whole transaction instead of splitting the schema.

Freestyle collapse

  • freestyleBeta.ts is promoted to the only Freestyle driver. The legacy 0.1.x arm (SSH gateway, cmuxd-remote WebSocket PTY on 7777) is deleted along with POST /api/vm/:id/ssh-endpoint, the openSshEndpoint workflow, the dead bakedFreestyleSignedAdmin plumbing, and wsLease.ts.
  • Every guest command pins linuxUser: "root". The 0.2 API's default changed to "uid 1000, or root if absent" and the devbox image ships such a user, so an unpinned exec would move the daemon, its install, and the model-plane write off the root layout they are baked around.
  • providerImageNotFound() also recognizes snapshot-not-found: Freestyle resolves an image to a snapshot id, so its missing-image answer is a 404 on the snapshot rather than an IMAGE_NOT_FOUND code.
  • The reaper's orphan-volume scan no longer hardcodes a provider. Volumes were Blaxel-only; it asks the registry and reports partial coverage when no driver exposes an inventory.

Go cmuxd-remote removal

The Go daemon had two arms. The cloud arm (serve --ws) is dead as of this PR:
every driver runs the cmux-tui remote daemon and openAttach throws on all of
them. The cmux ssh arm (serve --stdio --persistent) is being retired too —
terminals keep the plain SSH/mosh PTY, and proxy/egress, the reverse CLI relay
and persistent slots move onto tunnels. So the whole tree goes.

  • Delete daemon/remote/ (49 files, ~26.9k lines), build_remote_daemon_release_assets.sh, generate_remote_daemon_release_manifest.py, test_remote_daemon_release_assets.sh, and test_ci_attestation_retry.sh (it guarded only the daemon asset attestation steps).
  • release.yml / nightly.yml: drop the asset build, the CMUXRemoteDaemonManifestJSON Info.plist injection, provenance attestation and its retry, every cmuxd-remote-* upload, NIGHTLY_REMOTE_DAEMON_VERSION, and the two Setup Go steps that pinned to the now-deleted go.mod and would have failed both jobs outright.
  • ci.yml: drop the remote-daemon-tests job and, with nothing left routing on it, the entire go change area — its output, both early-exit emissions, and its entries in the linux-preflight / ci-status routing tables. The two "routed job skipped" preflight tests used remote-daemon-tests as their exemplar and now use web-typecheck, which routes on web.
  • tmux-corpus.yml: drop remote-daemon-fuzz and the fuzztime input it consumed. terminal-nightly (macOS Swift, unrelated to Go) stays, so the workflow is renamed to match what it actually runs.
  • release_asset_guard.js / prune_nightly_release_assets.py: stop treating cmuxd-remote binaries, checksums and manifests as immutable release assets or prunable nightly assets. Consequence: cmuxd-remote-*-<build> assets already published on the nightly release are no longer matched by the pruner and will need a one-time manual sweep.

Still to come, not in this PR: the Swift client stack that drove the binary —
CmuxRemoteDaemon (3.1k lines) and CmuxRemoteWorkspace (9.2k) go entirely, and
CmuxRemoteSession loses ~3.8k but survives (20 app files import it for tmux
control-mode mirroring, which never touched the daemon). That removal is
user-visible: it drops persistent remote PTY sessions, the SOCKS5/HTTP-CONNECT
proxy and egress, and the reverse CLI relay from cmux ssh, along with the
workspace.remote.pty.* socket API and its CLI verbs. Until it lands the app
still references a daemon binary that no longer ships, so cmux ssh bootstrap
will fail — this PR should not merge ahead of that follow-up.

Desktop / VNC

The VNC seam stays for Freestyle desktop support later, but the client stops claiming a desktop that isn't there: the devbox name heuristic matched only because Blaxel's devbox bundled xfce + noVNC, and the shared devbox image is shell-only. cmux vm new now defaults to --base, and the New Machine sheet offers only the kinds limits.imageKinds reports.

⚠️ Pending before deploy

The devbox snapshot sh-fb3dcf7b… was baked against the retired beta-api.freestyle.sh and is marked validationStatus: "unknown", so Freestyle creates fail closed until scripts/build-devbox-freestyle.ts re-bakes it on the public platform and the new id lands in the manifest. The env audit asserts this rather than hiding it.

Live Blaxel machines should be drained before the migration runs — afterwards nothing in the control plane can address them.

Verification

  • tsc --noEmit clean; bun test tests/vm → 301 pass / 0 fail.
  • Migration validated against a real Postgres 17: all migrations apply in order, Blaxel rows rewrite to e2b, the enum rebuilds without 'blaxel', the old type drops (interlock passes), and new 'blaxel' inserts are rejected.
  • Lint unchanged vs main (same 16 pre-existing errors, 4 fewer warnings).
  • CI guards after the daemon removal: test_ci_change_areas.py, release_asset_guard.test.js, test_ci_self_hosted_guard.sh, test_ci_release_sdk_lane.sh and the Python syntax guard all pass; ci.yml, release.yml, nightly.yml and tmux-corpus.yml all parse.
  • Not built: the Mac app — zig is not installed locally and there is no prebuilt GhosttyKit.xcframework, so the tagged Xcode build could not run. Touched Swift files pass swiftc -parse, and lint-pbxproj-test-wiring.sh is clean, but the Swift changes are unverified by a real compile.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AhaCvb89W567Qugstm8Gdq


Summary by cubic

Removes Blaxel and the legacy Freestyle arm, leaving public Freestyle (api.freestyle.sh, freestyle@0.2.9) as the only Freestyle driver. It also removes cmuxd-remote; existing Blaxel machines must be drained before migration, and Freestyle creation stays fail-closed until its public-platform snapshot is rebuilt.

Cloud VM changes

  • Rebuilds vm_provider without blaxel, rewriting surviving rows to e2b.
  • Pins Freestyle guest commands to linuxUser: "root" and maps missing snapshots to 404 image errors.
  • Removes the legacy SSH gateway, WebSocket PTY, SSH endpoint, and Blaxel image tooling.
  • Makes the New Machine sheet offer only backend-reported image kinds; cmux vm new now defaults to --base.
  • Uses provider inventory for orphan-volume cleanup and reports partial coverage when unavailable.

Removal and rollout

  • Deletes the Go daemon and its CI, release, nightly fuzz, attestation, and asset-pruning lanes; existing nightly binaries need a one-time manual cleanup.
  • The follow-up Swift client removal is required before merge because cmux ssh still expects a daemon binary that no longer ships.
  • Rebuild the snapshot with scripts/build-devbox-freestyle.ts and add its validated id to the manifest before deploying.
  • TypeScript and VM tests pass; the Mac app was not compiled because zig and GhosttyKit.xcframework were unavailable.

Summary by CodeRabbit

  • New Features

    • Cloud VMs now default to Freestyle, with E2B and Daytona available as alternatives.
    • VM creation now selects an available machine type automatically and defaults to shell-only machines.
    • Cloud VM connections consistently use the cmux remote transport.
  • Changes

    • Desktop VM creation now reports an unavailable-image error when no desktop image is configured.
    • Legacy SSH endpoints and remote daemon-based attachment capabilities are no longer available.
    • Cloud VM documentation and settings now reflect the updated provider and shell-only experience.