Blog

Codecast in the cloud

Every cloud feature, end to end: a host in your own AWS account that starts from your uncommitted work, carries your agent config and logins, mirrors its edits back to your laptop, takes whole batches of running sessions mid-flight, and sleeps when nothing is happening.

the codecast team16 min read

Most cloud agent products give you a fresh clone of main in somebody else's sandbox. Your uncommitted change is not there, your .env.local is not there, your agent instructions and your logins are not there, and the agent spends its first ten minutes rebuilding a world you already had. Codecast went the other way. A cloud host is a machine in your own AWS account that codecast sets up to look like your laptop, and a session sent there starts from the folder you are standing in.

This post covers all of it: the host itself, what travels when a session starts, how the host is made to feel like home, how pushes are authorized, the live mirror of an agent's edits, moving running sessions between machines, the laptop capabilities a host can borrow, cloud Macs with iOS simulators, sleep and wake, what happens when something breaks, and the other companies' clouds codecast also reads from. Every command here is in the CLI today, and the limits are at the end, stated plainly.

your laptopMacBook-Prothe checkout you are inyour logins, keychainthe host registryyour Chromeyour SSH keysyour cloud hostEC2, your AWS accountone worktree per taskthe codecast daemonagents in tmuxChrome on Xvfb :99an idle watchdogcast spawn --cloudyour checkout, uncommitted work includedhome mirroragent config, instructions, memoryloginsone way; the laptop stays the sourcelive syncevery 3 s while the agent workscast migraterunning sessions, messages heldbrowser sync · reach · vnca site's login, a folder, the screencodecastinbox, commands, tokensthe laptop does the workthe host asks for itfiles and logins ride SSH from the laptop; codecast carries commands and short-lived tokens
Everything that moves between your laptop and your host. Paper is the laptop, night is the host.

One idea runs through the whole design: the laptop is the source of truth, and the host is a pair of hands. Files and logins go from the laptop to the host over SSH. When the host needs something only the laptop has, such as a site's login or a file outside the repository, it asks through codecast, and the laptop does the work. Codecast's servers carry commands and short-lived tokens, never your source.

A host is an EC2 instance you own

A host is Ubuntu 24.04 on any instance type, or macOS on an AWS dedicated host. You pay AWS directly, at AWS prices. cast hosts create linux launches one with sensible defaults (a t3.medium with an encrypted 80 GiB disk in us-west-2), and cast hosts add <instance-id> --provision adopts one you already run. In the web app, Settings, Devices, Add a cloud machine asks a few questions and builds that command for you to paste on the laptop; nothing is provisioned from a browser, because the AWS credentials and the SSH key live on your machine.

Provisioning installs what a session needs and nothing exotic: tmux, git, a virtual display with Chrome on it, bun, the codecast CLI, your agent CLIs at the same versions as your laptop, and a codecast-daemon service that runs unattended. From then on the host shows up next to your laptops. Here is the codecast team's own list this afternoon, one Linux host and one Mac:

cast remote hosts
$ cast remote hosts
this device: macOS - MacBook-Pro-182  (76e7d3d6801fca2b)
  i-084309c56a91e15ff  [email protected]  aws running  [default]
  i-021a32d2d254c07d3  [email protected]  aws running  [default]

cast hosts ls is the fuller picture: whether each host is awake, which sessions run there, every worktree on it, whether it can push, which tools are missing, and what it costs. Fifty-one sessions on a t3.medium for about four cents an hour is the number that made us stop running long jobs on laptops.

cast hosts ls
$ cast hosts ls
i-084309c56a91e15ff  aws · us-west-2
  state      awake  203.0.113.24
  device     Linux - ip-172-31-40-243 — online  28d80e225ed21f45
  sessions   51
    jx72k01  codecast                       line                               active
    jx79p8f  cloud-5646a5  origin/main@641432e UI polish audit                    active
    …
  worktrees
    codecast  cloud-4213bb  ready  main-4213bb@07a081b, uncommitted changes
    codecast  cloud-5646a5  ready  codecast/cloud-5646a5@eccc7c6, uncommitted changes
    codecast  cloud-54e335  ready  codecast/cloud-54e335@5831cb2, uncommitted changes  no session (orphan)
    codecast  shared-checkout  main@fb2fe9d, uncommitted changes  main checkout, free
    …
  git        device-key: push access to [email protected]:codecast-sh/codecast.git, checked 1d ago
  tools      7 ok, 0 installed, 4 missing, 0 unsupported, 3 MCP servers disabled (codex: node_repl, codex: paper, claude: ios-simulator)
  setup      in step: nothing declared beyond the tools step (applied 2026-10-06T05:33:54Z)
  cost       awake: about $0.0416/hour running, about $6.40/month disk

i-021a32d2d254c07d3  aws · us-east-2
  state      awake  203.0.113.80
  device     macOS - Mac-mini — online  0c621d6e4176bed7
  sessions   12
  …
  cost       Mac dedicated host billing continues while the instance is stopped; 24-hour minimum allocation.

Two rows are worth a second look. orphan marks a worktree whose session ended; a disk sweep on the host releases those once they are clean, with no stash and no unpushed commits, and keeps anything else with a logged reason. And the tools line names three MCP servers that were switched off on the host because they cannot run on Linux (one is a macOS app, one drives an iOS simulator), so an agent there gets a clear absence instead of a server that crashes on start.

A session starts from your screen, not from main

Starting work on the host is the flag you would expect. In the web composer it is a toggle labeled run in the cloud, with start from set to my checkout or origin/main. From a terminal or from another agent:

starting work on the host
$ cast spawn --cloud "port the v1 routes to the new router"
$ cast spawn --cloud --from origin-main "audit the public API docs"
$ cast spawn --cloud --shared "run the data migration"
$ cast fork --cloud "try it with a queue" "try it with a cron"

What happens next is the part that took the longest to get right. The laptop takes one snapshot of your folder: a commit built through a temporary git index with every file in it, ignored ones included, so your own index and branch never move. That snapshot is pushed to the host, the host's own cast ws creates a worktree for each task with its own ports and its own dependency install, and the worktree is then reset so your uncommitted edits show up as uncommitted again. A tree hash comparison proves the host's folder matches yours before the agent starts. Any agent works: Claude Code, Codex, Cursor, Gemini, opencode, pi or Grok.

Two commit graphs. Laptop: an unpushed commit and modified files captured by a temporary index into a snapshot commit. Host: the same commit with the files uncommitted again.
The snapshot carries your exact working state, and the host restores it as uncommitted work.

Not everything should travel. Dependency folders are rebuilt on the host, because a node_modules built on a Mac is wrong on Linux anyway. Big untracked media and binaries stay home. Each file left behind is listed under the reason it stayed, so you can see the decision rather than discover it.

Travels
Captured through a temporary git index, so nothing in your checkout moves.
  • →your branch and unpushed commits
  • →uncommitted edits, staged or not
  • →untracked files
  • →gitignored files: .env.local and friends
  • →tracked files of any size
Stays, with a reason
Each skipped file is listed under the reason it stayed, so nothing vanishes quietly.
  • node_modules, .venv, dist, target, Pods…rebuilt
  • an untracked video or audio filemedia
  • an untracked native binary ≥ 256 KBbinary
  • an untracked file over 100 MBlarge
  • untracked files past 1 GB, largest firsttotal
  • sockets, pid files, database journalslive
  • a repository nested inside this onerepo
What a cloud spawn carries. Tracked files always travel, whatever their size; .env files travel too, since both machines are yours.

A fan-out of five tasks takes one snapshot and makes five worktrees, so five agents start from the same instant of your work. --shared is the exception: one task, in the host's main checkout instead of a worktree, for jobs like a migration that must run where everything else is. It refuses a dirty checkout or one a live session holds, rather than trampling either. If any step fails, the session fails loudly. It falls back to origin/main only when your folder is not a git repository at all, and says so.

The host is set up like your laptop

An agent is only as good as its context, and most of the context lives outside the repository: ~/.claude, your CLAUDE.md, the skills and hooks you wrote, the shell setup your hooks assume, the logins for the CLIs your agents call. Before any session starts, and again whenever the host wakes for one, the laptop brings the host into step, in this order:

ip-172-31-40-243 · before the agent starts
  1. ✓diskrefuses to start with under 1 GiB free
  2. ✓castbrought to the laptop's version
  3. ✓[host] setuppackages, services, run commands; skipped when in step
  4. ✓loginsClaude only while its token is live; never refreshed on the host
  5. ✓toolsagent CLIs, node, bun, gh, uv at your versions, no sudo
  6. ✓gita credential helper that asks for one token per push
  7. ✓home mirror~/.claude, ~/.codex, CLAUDE.md, shell rc, memory
stays on the laptop:ANTHROPIC_API_KEY~/.sshkeychainsGoogle loginsbrowser profiles
The host's checklist before a session starts. Every step except the disk check and the mirror is allowed to fail without blocking work.

The home mirror carries agent folders (.claude, .codex, .cursor and the rest), instruction files, your shell rc files and the files they source, small scripts from ~/.local/bin, and your Claude project memory. Laptop paths are rewritten to host paths on the way. It is checked every minute and only sends when something changed. Memories an agent writes on the host are merged back into the laptop before the next push, so what a cloud session learns is not stranded there. Credentials, transcripts, caches, .ssh, keychains and browser profiles are on a denylist and never travel.

Logins go one way, from the laptop to the host, and never back. The Claude credential is the delicate one: refreshing it would rotate the laptop's refresh token and log you out at home, so it is sent only while its access token is still live, and the host is never allowed to refresh it. It is sent again shortly after the laptop's token renews. Your ANTHROPIC_API_KEY deliberately stays home, because its presence would move every Claude on the host off your subscription and onto metered API billing.

Tools are installed without sudo, into your user's home: agent CLIs at your laptop's versions, a recent Node, bun, gh, uv, and any helper command your hooks or skills name. What the repository itself needs goes in one committed file:

.codecast/workspace.toml
# .codecast/workspace.toml (committed, read by every host)
[host]
packages = ["postgresql", "redis-server"]
services = ["postgresql", "redis-server"]
run = ["./scripts/seed-dev-db.sh"]

# this repository's own file says only:
[host]
simulators = ["iOS"]

The [host] table runs before any worktree exists, and it is skipped when the host is already in step, so a warm host pays one SSH round trip for it. A personal ~/.codecast/host.toml merges in for the things that are yours rather than the repository's, like your shell. Every login shell on a host also sees CODECAST_CLOUD=1, so a hook can tell where it is running.

Pushing from a host without leaving a key on it

A machine that runs agents unattended should not hold a long-lived GitHub token. So git on the host asks for credentials every time it needs them, through cast git-credential, and codecast answers with a GitHub App installation token for that one repository. For a team installation it first checks that you are allowed to push. The token expires within the hour and is handed to git in memory, never written to disk.

on the hostoff the hostgit on the hostcast git-credentialcodecastGitHub Appcredentials for github.com?a token for this repomay this person push?installation token, 1 repoexpires within the hourhanded over, never on disk
One push from the host. The token lives about as long as the push does.

Two fallbacks exist for setups the App cannot cover. Each host has its own ed25519 device key, which cast hosts key --grant adds to GitHub through your gh login. And cast hosts forward-agent, strictly opt-in, exposes your laptop's ssh-agent to the host over one held connection, for when the host must use exactly the keys you have.

The agent's edits, on your laptop, as they happen

A session on a host is only half useful if its work is stuck there. Pick Sync with MacBook-Pro in the session's machine menu, or run cast remote sync <session>, and codecast keeps a copy of the session's folder on your laptop, both ways, a few seconds behind. Open it in your editor, run the tests locally, or fix a line yourself and let the agent see it.

Each tick snapshots both sides and compares them with the last tree they agreed on. If only one side changed, its changes land on the other. If both changed, git does a three-way merge in memory without touching either folder; the clean files land, and a file changed on both sides is held as it is on each side while everything else keeps flowing. Only files that actually changed are written, so your editor and your dev server do not see a storm of rewrites.

hostlaptop3s6s9s12s15s18s21s24sv1.tsv1.test.tsv1.tsREADME.mdroutes.ts held: changed on both sidesv2.ts still landed on the laptopagent goes quietpaused: laptop read every 15 s● host edit ● laptop edit ● same file on both sides ┆ sync tick
Thirty seconds of a synced session. Ticks run every 3 s while the agent works, and pause when it goes quiet.

A held file never gets conflict markers written into it. You pick a side, from the menu (Keep the laptop's, Keep the cloud's) or with cast sync keep laptop|cloud. When the agent goes quiet, the sync pauses and only reads the laptop's copy every 15 seconds; a laptop edit is sent only if the host is already awake, so a forgotten sync never keeps a billed machine running. --watch-only makes it one way, from cloud to laptop, and if you then edit the copy anyway it stops and asks what you meant.

The machine menu for a synced cloud session: synced both ways, one file held because it changed on both sides, and a list of what stayed on the host.
A conflict holds one file, not the sync. Heavy and machine-specific files stay on their own side.

The verbs work from either machine: cast sync status, diff, pull and push, even without a running sync. From the host, cast sync pull ~/data/export.csv fetches a file from outside the repository, up to 2 GB, and --ref fetches a branch only the laptop has. One rule keeps the two mirrors from fighting: every file has exactly one carrier. Folder sync carries the working folder; the home mirror carries your agent config.

Moving twenty sessions before you close the lid

The most common reason to want a host is the moment you need your laptop back: a flight, a meeting, a machine pinned at full load. cast migrate moves running Claude Code sessions between a laptop and a host as one batch, in either direction, and the session continues on the other side with its transcript, its working tree and its place in your inbox.

cast migrate
$ cast migrate start --to linux --label rollout --dry-run
$ cast migrate start --to linux --label rollout
$ cast migrate start --to macbook --from linux

Each row goes through the same steps. New turns are blocked first, so steady traffic cannot keep a session busy forever; anything you send it meanwhile is held and delivered on the other side. A session in the middle of a turn gets to finish it, up to --wait minutes (ten by default). One stopped at a permission prompt moves at once and asks again on the destination. Then the agent is stopped, the work is transferred, ownership flips in one transaction, and the session resumes.

queuedwaiting for turnstoppingtransferringhanding offresumingdonejx7k2pdidle at a promptjx79tnwmid-turn, finishes first2 messages heldjx72ss5at a permission promptasks again on the hostjx77bkwhost checkout busyfailed · restarted where it wasjx75me8queued behind twosessions transfer two at a time by default; a turn gets 10 minutes to finish before it is interrupted
A batch of five, schematic. Failures restart the agent where it was; nothing is left half moved.

Going out, the transfer reuses the spawn snapshot, plus the gitignored files and the transcript with its paths rewritten. Coming home, the host's work is applied to your checkout as uncommitted changes with a three-way merge. If it does not apply cleanly, the row fails with the reason and nothing in your folder changes. Here is our own history from the past few days, failures included:

cast migrate ls
$ cast migrate ls
mg-03p71vdf  → Linux - ip-172-31-40-243 (cloud)  done      1/1 done  41h ago
mg-k28o8e34  → macOS - MacBook-Pro-182  done      1/1 done  3d ago
mg-f7qy8sqs  → macOS - Mac-mini (cloud)  done      1/1 done  3d ago
mg-x0ocus1i  → macOS - MacBook-Pro-182  done      3/3 done  3d ago
mg-st7mdwnx  → macOS - MacBook-Pro-182  partial   5/8 done, 3 failed  3d ago
mg-lnu1wdnm  → macOS - Mac-mini (cloud)  done      2/2 done  3d ago
mg-2uvjb2z1  → Linux - ip-172-31-40-243 (cloud)  partial   1/2 done, 1 failed  3d ago
…

The partial batches are the honest part. Their failed rows read handoff refused: the host checkout is in use by session jx70vhm and CONFLICT: the host's changes do not apply to this folder as it is now; nothing was changed here. In both cases the session kept running where it was. When a move succeeds, the agent is told, so it does not go looking for a dev server it left behind:

what the agent reads after a move
[codecast] This session just moved to a different machine. It now runs on
Linux - ip-172-31-40-243 in /home/ubuntu/work/codecast (previously
macOS - MacBook-Pro-182). Processes, ports, and any files outside the working
tree from the previous machine are not here.

There are three other ways in. The machine chip in a session's header lists every machine you can use and moves the session with one click; a sleeping host reads asleep, wakes on move. Run here on a laptop brings a cloud session home without ever interrupting a running turn. And when your laptop is under sustained pressure (memory, CPU, or load at three times its core count for a minute or more), the Resources page offers to offload sessions and spread them for you, for you to review and confirm.

A Codex session running on a cloud host, with its browser tab, in the same inbox as the laptop's sessions.
A session on a host sits in the same inbox as everything on your laptop, browser tab and all.

Borrowing the laptop from the cloud

Some things only exist on your laptop, and some work needs a screen. The host has its own Chrome on a virtual display, so cast browser on a host drives that Chrome and never yours. When an agent there needs to be signed in somewhere, the laptop decrypts its own cookies for that one site and injects them into the host's browser over an SSH tunnel. Google is refused outright, requests are rate limited, and a request never wakes a host on its own.

from the host, or about it
$ cast browser sync linear.app       # one site's login, from your laptop's Chrome
$ cast hosts vnc                     # the host's whole screen, inside codecast
$ cast hosts reach ~/notes           # a laptop folder, mounted on the host, no copy
$ cast computer get-app-state --app gedit   # native apps on the host, over AT-SPI

cast hosts vnc opens the host's whole screen inside codecast, with mouse and keyboard, for anything outside the agent's tab. The stream reaches your browser through the laptop, one SSH channel per connection, and closes ten minutes after the last viewer leaves. cast computer, which drives native apps through their accessibility tree, works on Linux hosts too, over AT-SPI on the virtual display.

cast hosts reach ~/notes is the strangest of these and our favorite. It mounts a folder from your laptop on the host at the same path, with no copy. The laptop serves it through an sftp-server locked in a macOS sandbox that cannot touch the network, run programs, or read any file outside that folder, and the host mounts it over the SSH connection the laptop already holds. The host never connects to your laptop. Your home folder and anything holding keys are refused. When the laptop sleeps, the mount drops and leaves a note in its place.

Underneath all of these is one small relay. The host writes a request to codecast, the laptop daemon picks it up and does the work over SSH, and the host reads the outcome. There is no inbound port on your laptop and no relay server holding your data. When both machines are on the same Tailscale network, the laptop dials the host's tailnet address instead of its public one.

Cloud Macs and iOS simulators

A Mac host is for work that needs Xcode. Add simulators = ["iOS"] to the [host] table and setup copies your laptop's Xcode to the host, downloads the iOS runtime without an Apple ID, and installs the tools cast sim drives. After that an agent on the Mac host runs cast sim acquire, installs its build, taps through it and screenshots it into the thread, exactly as it would on your laptop, from a pool of three simulators that are reaped when nobody holds them. The first Xcode copy is slow, close to an hour for us; it happens once.

It sleeps when nothing is happening

A host that runs all night for nothing is the fastest way to stop trusting the feature, so a Linux host watches for real work and stops itself without it. A timer checks every two minutes. Real work means an SSH session, someone watching the screen, an agent mid-turn, a headless claude -p or codex exec, a live process under an agent, or CPU moving in one. An agent idling at a prompt is not work. After twenty idle minutes (configurable, or zero to disable) the host powers off, and a stopped instance costs only its disk.

0 min10 min20 min30 min40 min48 minagentturn ends; an idle prompt is not workwatchdogchecks 5 min after boot, then every 2idle for20 idle minutesstoppeddisk onlypoweroffa trigger fires for ita laptop sees the request,starts the instance;answered 60 s later
Schematic, except the wake: on September 5 a queued trigger reached a stopped host's session 60 seconds later.

Waking is automatic in the common case. Work queued for a stopped host, a message or a trigger, marks it for waking, and the next heartbeat from a laptop that manages it starts the instance and waits for SSH. For a deliberately quiet job, such as a long download with no CPU, cast hosts keepalive 30 on the host takes a lease that holds it awake for thirty minutes. The host's panel in Settings shows all of it: Awake, Waking, Going to sleep or Asleep, how long until it sleeps, and buttons to wake it, put it to sleep, re-apply setup, or save an image to start new hosts from.

When something breaks

Machines fail in boring ways, and most of the work here went into making the failures boring too. Every daemon, on a host or a laptop, runs a watchdog that looks for sessions whose agent process died mid-work. It restarts them with a note explaining what happened, at most three times in six hours per session, and never one you stopped yourself. Each host reports its readiness (mirror, logins, tools, setup) on its heartbeat, and the laptop collects a cost and state report every fifteen minutes, so the panel tells you which logins were held back and why before an agent finds out the hard way. A migration row that stops reporting for thirty minutes is failed by the server, and the session stays where it was.

The other clouds

Your host is not the only cloud your agents run in. Codecast also reads, and mostly drives, the cloud agents other companies run, so they land in the same inbox and the same search as everything else. Cursor Cloud agents appear as sessions once you add a Cursor key; you can launch one from the composer with run in Cursor Cloud, and your messages become its follow-ups. Codex Cloud tasks sync from your own codex login when you turn it on, with up to four attempts per task, a draft pull request, or the result applied to your checkout. Claude Code sessions you started on the web are mirrored with their full transcripts, and you can reply to them from codecast.

Whose machineStarts fromLaunch from codecastFollow-upsMoves to your laptop
Your cloud hostYours, in your AWS accountYour checkout as it is, or origin/mainYes, any agentYesClaude Code sessions, both ways
Cursor CloudCursor'sThe branch as it is on GitHubYesYesNo
Codex CloudOpenAI'sThe repository on GitHubYes, 1 to 4 attemptsDraft PR, apply locallyNo
Claude Code on the webAnthropic'sA session you started on claude.aiNo, mirrored onlyYesNo
Four kinds of cloud session, one inbox. Only your own host starts from your uncommitted work.

The difference is the starting point. A vendor's cloud starts from what is on GitHub. Your host starts from what is on your screen, which is why we built it.

What it needs, and where it stops

  • Waking a sleeping host needs a laptop online. Queued work marks the host for waking, and the next laptop heartbeat starts it. A server-side waker exists, but it is enabled per host by the operator, not from the app.
  • Only Linux hosts stop themselves. A Mac host stays up, and AWS bills its dedicated host for at least 24 hours either way.
  • Only Claude Code sessions move. Any agent can be started on a host, but migrating a running session between machines works for Claude Code only, and never directly from one host to another.
  • Most tool logins travel when they exist. Claude, Codex, Gemini and a few others only travel while their tokens are live. Logins for gh, aws, npm and similar CLIs travel whenever the file is present. Your Anthropic API key and Google logins never do.
  • Opening a pull request from a host may need a gh login. The per-push token is for fetching and pushing. Most hosts already have a gh login copied from the laptop; if not, run gh auth login there.
  • [host] packages and services are Ubuntu-only. On a Mac host, only run commands and simulators apply.
Codecast is where your team sees, steers, and remembers every coding agent session, on any agent and any machine, including the ones you rent.

The terminal captures are genuine output from the codecast team's laptop and its two hosts on 2026-10-07, with the hosts' public IP addresses replaced by documentation addresses and long lists trimmed where marked. The spawn, migrate and borrow blocks show commands, not their output. The 60 second wake was measured on 2026-09-05 and is written up in the CLI's cloud workspaces doc. The figures are drawn from the code's own constants (tick rates, timeouts, idle minutes); the migration batch and the sync timeline are schematic. The three screenshots come from the field manual's cloud chapter.