cloud: cmux-cloud-client, NEXT Cloud's one Rust client generated from cmux-platform's openapi.json; acpmux join moves onto it

Merged
TE
2d ago
+12565-112

Leo, 2026-11-04: "and moving off swift into rust for next's cloud" / "you guys should be willing to remove code rewrite do large scale refactors".

Step M11 of cmuxterm-hq plans/cloud/ARCHITECTURE-2026-11.md (T2, one Rust client): NEXT Cloud gets one Rust client, generated from cmux-platform's openapi.json, and acpmux's join is its first caller.

What changes

  • New crate cmux-tui/crates/cmux-cloud-client. src/generated.rs is progenitor output from a vendored selection of cmux-platform's spec (openapi.json, pinned in PLATFORM_REF). tags.txt lists the groups it keeps: machines, placement, reservations, join credentials and redemption, workspace machines, environments, auth, client config and machine vault. That comes to 56 routes and 81 schemas, out of 154 routes in the full spec. The crate also has:
    • client / anonymous_client / client_for: bearer marked sensitive, x-cmux-team, no redirects.
    • ApiError: one reading of the {_tag, code, message} refusal, the wire {error:{code}} shape and proxy HTML, with a retryable flag.
    • ops::call::<O>: the cmux.wire/1 ops (/v1/ops, /v1/read), typed by cmux-cloud-wire. It checks the idempotency rule before sending.
    • backend::Backend: the platform origin and owner API for each environment, from cmux-cloud-environment's deployment rows. cmux-cloud-config's PRODUCTION_PLATFORM (cmux-platform.debussy.workers.dev) 404s even on /v1/health, so it isn't used; the live Worker is cmux-platform-production.
    • client_key(): the idempotency key.
  • The generator is shared, not forked. cmux-vm-codegen gets --tags, --selected-out and --script, and --raw-out becomes optional. It now reads a property whose only type is null as an any-value schema; before, progenitor panicked on those (invalid type: null; platform's placement accounting fields). The cmux-vm client comes out byte for byte the same.
  • CI. cmux-vm-clients.yml also runs cmux-tui/crates/cmux-cloud-client/scripts/regenerate.sh and fails when the result is stale. scripts/update-spec.sh [REF] pulls a newer platform spec.
  • acpmux join (C12) moves onto the client. credentials/self (probe, replacement proof, status) and join-tokens/redeem now use the generated methods and types. Before, they used hand-built reqwest calls that read the answer field by field. The redeem body is checked against the spec's limits before it is sent. Exit codes, messages and the 10 s deadline are unchanged. DEFAULT_HUB now reads its origin from Backend. It stays on development for now: production serves no join yet (/v1/machines/join.sh and /credentials/self answer 404 on cmux-platform-production, and MACHINES_ENABLED is on only in development). The production flip goes in its own PR once the backend helper turns join on there (manaflow-ai/cmux-platform#830 thread).

What it does not do

  • Routes cmux-platform serves by hand and does not describe are not in the spec: /v1/ops and /v1/read, /v1/cloud/machines/{id}/exec|fs/*|client-session|acpmux*, bind and keyset, and the agent-key mint. They are listed in manaflow-ai/cmux-platform#825; no type for them is written here. The vault pull keeps its own raw read, because it zeroizes the secret body, which typed decoding would not do.
  • The CLI's cmux machine verbs, the Cloud app server and Swift move in later PRs (M11 continued, then M12).

Checks (fleet, cmux-ci test)

  • cargo test -p cmux-cloud-client: 10 passed.
  • cargo test -p acpmux --test machine_join: 3 passed, 10 ignored, the same as base.
  • cargo test -p acpmux --lib cloud_join: 6 passed.
  • cargo test -p cmux-vm-codegen: passed.
  • cargo clippy -p cmux-cloud-client -p acpmux --all-targets -- -D warnings: clean.
  • cargo fmt --check: clean.
  • Regenerating gives the same output twice; the cmux-vm client is unchanged.
    Agent session: agent session

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Introduces cmux-cloud-client, NEXT Cloud's single Rust client generated from cmux-platform's OpenAPI spec, and moves acpmux's join flow onto it in place of hand-built reqwest calls.

Changes

  • The crate generates 56 of 154 spec routes across 13 tags and adds cmux.wire/1 ops, unified error decoding, per-environment backends, and idempotency keys. The client is generated at build time via build.rs (which includes cmux-vm-codegen's normalize.rs by path), so no generated file is committed.
  • cmux-vm-codegen is shared rather than forked: new --tags, --selected-out, and --script flags, optional --raw-out, and null-only schemas become any-value instead of panicking.
  • scripts/update-spec.sh pulls newer platform specs and validates that a new selection still generates; ops::call refuses an idempotency key for a mutation whose catalog row refuses keys.
  • acpmux join now calls the generated credentials/self and join-tokens/redeem; exit codes, messages, and the 10 s deadline are unchanged, and DEFAULT_HUB comes from Backend (development, since production serves no join yet).

Not in this PR

  • No types for hand-served routes the spec omits (/v1/ops, read, exec, bind, keyset, agent-key mint); the vault pull keeps its raw read to zeroize secrets.
  • The CLI's cmux machine verbs, the Cloud app server, and the Swift migration land later.