feat: move idle channels between a host and a hosting service #194
- IA
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 (#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. #9 covers the external-harness gap.
iamnbutlermerged 11e4f4053486last month on GitHub
iamnbutlerreferenced this in Move channels between local and hosted with the same workspace #181last month on GitHub
iamnbutlerreferenced this in [Meta] Run hosted channels in the cloud with local workspaces #13last month on GitHub
iamnbutlerreferenced this in chore(desktop): prepare canary 0.0.17 #195last month on GitHub
iamnbutlerreferenced this in chore(desktop): prepare canary 0.0.18 #199last month on GitHub