Skip to content

feat: move idle channels between a host and a hosting service - #194

Merged
iamnbutler merged 1 commit into
mainfrom
cloud-pilot-ops/cloud-move
Oct 7, 2026
Merged

iamnbutler merged 1 commit into
mainfrom
cloud-pilot-ops/cloud-move

Conversation

@iamnbutler

@iamnbutler iamnbutler commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

The owner can now move an idle channel between its host and a team-deployed hosting service, and back again, with ace move <channel> --hosted <url> | --local or the channel's Move to: context menu. The channel keeps its ID, lanes, and workspace. All pi state comes back to the host, including work done while the channel was in the cloud. This is the first slice of #181 and does not close it. Refs #181, #13.

  • @ace/channel/transfer: a runtime-neutral snapshot of pi's SQLite store.
    • It covers an explicit allowlist of tables, pinned to pi schema 1, up to 32 MiB of UTF-8, with a SHA-256 digest.
    • Import creates the schema on an empty store and checks the digest before committing.
    • Export refuses unfinished tasks or queued submissions, so a move never carries an active run.
  • services/channel: an authenticated POST /channels/:id/transfer endpoint.
    • It takes five actions: import (stored inactive), activate (only for the imported digest), freeze (drains admitted requests, refuses if busy), export (only while frozen), and thaw.
    • Actions run one at a time on each channel.
    • The inactive and frozen states are stored, so they survive restarts and alarms. In those states every other request gets 409, and freezing closes all sockets with close code 4009.
  • apps/host:
    • A moving fence on the channel record stops workers from starting and clients from connecting while a move runs.
    • A worker can be frozen: it stops admitting requests, drains the ones already admitted, and exits only when idle.
    • Outbound: the move exports the local store and imports it into the service; it switches the record only after activation succeeds. The local file is left in place.
    • Return: the move freezes the cloud copy, verifies the export into a new local file, and swaps that file in atomically. The frozen cloud copy is kept.
    • An activation that gets no answer keeps the fence, and running the same move again finishes it. Only the move's own lock is ever released.
    • Host ops move-targets and move are limited to the owner on the local gateway. Destinations are deduplicated from the hosted URLs in this host's channel records plus services.json, which keeps the URL after a channel returns.
  • App and UI: the context menu gains a Move to: group with loading, empty, and failure states plus progress toasts. An open transcript watches again when the channel's location changes.
  • Docs: a new "Moving channels" section in the architecture doc.

Out of scope for this slice: moving a channel while it is running, automatic backups, general recovery or restore of an interrupted move, streaming large channels, and a service-setup UI.

Real deployed roundtrip. I ran one roundtrip on the dedicated ace-channel-pilot Worker, using an isolated pilot host and a fresh idle fixture channel with model anthropic/claude-sonnet-5-5:

  1. A local run executed a shell command, and the transcript was captured.
  2. ace move --hosted moved the channel in about 1s. The workspace reconnected to the cloud copy, and its transcript events, channel info, and usage matched the local copy exactly.
    • The first attempt, made 24s after the deploy, got a 404 that was likely from the previous version still being served (not proven). It failed safely: the worker drained, the fence lifted, and the local store was untouched.
  3. A cloud run then executed a shell command on the Mac through the workspace.
  4. The return used the actual Move to: → This host menu in the Chrome app. Afterwards the transcript, info, and usage all matched the cloud copy, and the cloud object answers 409 "The channel moved".
  5. A follow-up run on the host saw both earlier tests and ran its shell command there.

The existing deployed ace-channel and ace-directory services were not touched. bun types and bun run ci pass.

Built in Ace

  • Ace channels did all the coding, deploys, and real runs.
    • The host, CLI, protocol, and docs, plus the integration, pilot deploy, real roundtrip, checks, commit, push, and this PR, were done by the Ace channel cloud-pilot-ops in lane cloud-move, using Ace's shell and file tools.
    • The transfer module and service endpoint were done by the Ace channel cloud-move-service, and the app and UI menu by the Ace channel cloud-move-ui, in the same lane.
  • Codex was used outside Ace for coordination and review. It dispatched the channels, set the contract, did the independent reviews, and organized tracker issues. Ace cannot host the Codex harness yet (Run and resume Codex and Claude Code in Ace channels #9).
  • The UI spot check was automated by Codex in Chrome. Codex's computer-use tool clicked Move to in the native Chrome window outside Ace, because the Ace channel doing this work runs on a source host without Ace's native desktop tools. Run and resume Codex and Claude Code in Ace channels #9 covers the external-harness gap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant