Cloud VMs: private per-user Freestyle VPC + WireGuard tunnel from the cmux app
Merged
JA
2mo ago+2857+2857+0123456789012345678901234567890123456789-52-52-01234567890123456789
What
Cloud VMs no longer expose any public port. Every Freestyle machine joins the one private VPC that belongs to its owner, and the owner's computers join the same VPC over WireGuard tunnels that the cmux app sets up — so the cmux-tui daemon is reachable only from the owner's own machines and computers.
Backend
- One VPC per (user, provider), provisioned idempotently on first machine create (
services/vms/privateNetwork.ts,cloud_vm_networks); hashed slugs so provider-side listings don't leak user ids. - Machines (ad-hoc
createVmand Base open/reset/reopen) attach to the VPC at create and state outbound-only firewall rules — nopublic → 1337rule at all. The VPC's single members-reach-each-other rule is what admits the owner's tunnels and other machines. - Attach routes resolve to the machine's private VPC address (
ws://[fd…]:1337/v1/link), never falling back to public for a VPC machine. POST/GET/DELETE /api/vm/tunnel: WireGuard tunnel enrollment per (user, device). The client generates the keypair and sends only the public half; a mismatched key rotates the tunnel in place, keeping the device's address on the network. Deliberately not Pro-gated: a lapsed subscription must still reach machines it owns.- Account deletion deletes provider tunnels + the VPC after machines are destroyed.
- Rollback:
CMUX_VM_PRIVATE_NETWORK_ENABLED=0reverts later creates to the public-IPv6 posture; existing machines keep working either way because reachability is resolved from the addresses each machine actually holds.
Mac app + CLI
VMTunnelManager: per-installation device id + Curve25519 keypair (private key never leaves~/.cmuxterm/wireguard, 0600), idempotent enrollment on demand, completed wg-quick config, root-free liveness detection (interface addresses, since wg-quick's name file is root-only on macOS).cmux vpn up|down|status|revokeover newvm.tunnel_*socket verbs — the shipping bring-up path viasudo wg-quick(the NetworkExtension path is gated at runtime on the packet-tunnel entitlement and will take over with no CLI change once release signing carries it).- New user-facing strings localized (en/ja).
Fixes found in dogfood
- Base machines were the only ones left publicly exposed (base create path skipped network placement).
vpn uphung: FoundationProcessdetaches its child's process group, so sudo's tty read got SIGTTIN. Foregrounded like the feed TUI does.- Enrollment approve loop now stops on 404 (machine destroyed mid-setup previously flooded the control plane for 5 minutes).
- Stats on providers without
getStatsnow answer typed 501 (was retryable 502 → infinite panel polling). remove_blaxelmigration made fresh-replay-safe (enum literal → text comparison).
Verified
- 14 new backend tests (
vm-private-network.test.ts) + updated Freestyle provider tests; full affected suite green (247 tests). - 9 new Swift
VMTunnelManagerTests(keypair stability, config completion, liveness parsing), wired into the test target. - Live end-to-end: from a Mac with no public IPv6, enrolled via the app,
cmux vpn up, created machines that landed in the VPC with zero public inbound rules, and drove their terminals through the WireGuard tunnel (typed a command over the cmux-remote link and read the output back). The old public-IPv6 path was unreachable from that network — exactly the failure this removes.
Notes for reviewers
- Freestyle devbox snapshot: local verification used
cmux-devbox-20260902b(sh-749d7644…) viaFREESTYLE_SANDBOX_SNAPSHOT; the manifest still needs that entry recorded for deployed environments. - Follow-up: the activity panel still polls stats after the 501; client-side stop is a small separate change.
- The NetworkExtension entitlement work (app-managed tunnel, no sudo) is scaffolded behind
VMTunnelManager.networkExtensionAvailable()and intentionally not part of this PR.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Cloud VMs no longer expose any public port. Every Freestyle machine joins its owner's private VPC, and the owner's computers reach it through a WireGuard tunnel set up by the cmux app, so the cmux-tui daemon is reachable only from the owner's own machines and computers.
Backend
- One VPC per (user, provider) is provisioned idempotently on first machine create.
- Machines attach at their private VPC address with outbound-only firewall rules; no public inbound port at all.
POST/GET/DELETE /api/vm/tunnelenrolls WireGuard tunnels per (user, device); the client sends only the public half of its keypair, and a mismatched key rotates the tunnel in place.- Tunnel enrollment is deliberately not Pro-gated so a lapsed subscription can still reach machines it owns.
- Account deletion removes tunnels and the VPC after machines are destroyed.
CMUX_VM_PRIVATE_NETWORK_ENABLED=0reverts later creates to the public-IPv6 posture; existing machines keep working either way.- Base machines now join the owner's network too, closing the last public-exposure path.
- Stats on providers without
getStatsanswer 501 instead of retryable 502; theremove_blaxelmigration is replay-safe, and the private-network migration is ordered after the E2B/Daytona provider removal so fresh databases apply cleanly.
Mac app + CLI
VMTunnelManagermints a stable per-installation device id and Curve25519 keypair and writes the completed wg-quick config to~/.cmuxterm/wireguard/cmux.conf.cmux vpn up|down|status|revokebrings the tunnel up viasudo wg-quick; the NetworkExtension path is gated at runtime and will take over with no CLI change once release signing carries the entitlement.vpn upno longer hangs because the child's process group is foregrounded so sudo can prompt.- The enrollment approve loop stops on 404 instead of flooding the control plane when a machine is destroyed mid-setup.
Summary by CodeRabbit
- New Features
- Added
cmux vpncommands to enroll, connect, disconnect, inspect, and revoke private VM network tunnels. - Cloud VMs now support private networking through WireGuard, with secure device enrollment and tunnel management.
- Added tunnel status details, JSON output, key rotation, and localized CLI messages.
- Added
- Bug Fixes
- Improved handling when a cloud machine is deleted during enrollment.
- Account deletion now cleans up associated private networks and tunnels.
- Documentation
- Updated VM and VPN help to explain private network access and setup.