Codex Remote

Codex Remote

Operator’s Guide
VERSION 0.2.0
MACOS 15 OR LATER
APPLE SILICON / INTEL
MIT · OPEN SOURCE

As simple as it gets to put Claude or Codex on a remote machine.

Give it a token for a cloud you already pay for. It builds the server, installs Codex and Claude Code, carries your MCP servers over, and hands the machine to the tools you already work in.

  1. Install it
    $ brew install --cask pandelisz/tap/codex-remote
  2. Or download it
    Download Codex Remote 0.2.0 Unzip, drag to Applications, then clear the quarantine flag as below. The app keeps itself up to date after that.
  3. Add a cloud token
    Hetzner, AWS, DigitalOcean, Linode, Vultr or Scaleway. It goes to your login keychain, never to a file or to OpenTofu’s state.
  4. Press New machine
    Or hand the job to an agent, which can create and configure machines itself.
Caution

This build is ad-hoc signed, not notarised, so macOS calls a downloaded copy “damaged”. That is the quarantine flag rather than a corrupt download, and right-click → Open does not clear it. Homebrew removes the flag for you; after a manual download, run xattr -dr com.apple.quarantine /Applications/CodexRemote.app.

The Codex Remote menu bar panel. One machine named demo on Hetzner shows a blue idle indicator and the line idle, CPU 0 percent, RAM 0.5 of 7.6 GB, beside a power toggle and an Open button.
Figure 1. A machine running on Hetzner. Blue means idle — nothing is using it, so stopping it interrupts nothing. Green means sessions are running, and the row says how many.
2

Renting a box takes a minute. Making an agent live on one doesn’t.

None of this is hard. All of it is fiddly, none of it is interesting, and you do every bit of it again for the next machine.

By hand

Edit ~/.ssh/config and hope

An Include won’t do: Codex parses that file with a library that never expands them, so a host behind one is invisible and nothing says why.

Codex Remote

The block is written where it’s read

Inline, above your Host * so first-match cannot override it, inside markers it can cleanly remove again.

By hand

Install, log in, answer the wizard

Node, the CLI, an OAuth round trip, then a first-run prompt that blocks forever under a service manager because nobody is there to pick a theme.

Codex Remote

Bootstrapped and signed in

Wizards pre-answered. Claude Code runs as its own claude account with its own login — two installs cannot share one refresh token.

By hand

Keep a terminal open

Close the lid and lose the session. Reboot the box and find out days later that the agent never came back.

Codex Remote

Units, enabled at boot

Everything it installs shares one prefix, so systemctl status 'codex-remote-*' shows the lot, and a stop/start brings all of it back.

By hand

Leave your project and your tools behind

A clone arrives without the gitignored .env, so the code is all there and the project still does not run.

Codex Remote

Both travel

codex-remote push clones or copies, then sends the untracked config separately. Portable MCP servers come across with it.

3

What happens when you add a machine

It creates the server. OpenTofu applies a small per-cloud module and makes one machine with your key on it. Each gets its own state file, so one can never disturb another.

It bootstraps over SSH. Base packages, the Codex CLI at /usr/local/bin/codex, Claude Code under its own account, your MCP servers, and a systemd unit for each agent.

It registers the host. A Host codex-remote-<name> block goes into your SSH config, which is how the Codex app finds a machine and starts codex app-server on it over SSH.

It connects. Codex picks it up under Connections; Claude Code dials out and appears in your account. Pair it once and the box answers from your phone too.

4

Letting an agent do it

The agent usually knows what it needs better than you do while you are picking from a dropdown. Connect it and it can read your machines; allow more and it can build them.

$ claude mcp add codex-remote -- codex-remote mcp serve
Table 1. What an agent may do, and what you have to allow first.
ToolNeedsDoes
list_machines—Health, running sessions, CPU and memory
list_sizes—Regions, sizes and images, with prices
run_commandChangesAny shell command, as root
sync_projectChangesPut a local project on a machine
create_machineChangesBills to your cloud account
destroy_machineDestroyCannot be undone
Why three levels

These tools spend money, and an agent calls things in loops. Reading is always available. Changing is off until you turn it on. Destroying has its own switch, because creating the wrong machine costs pence and deleting the right one loses work — and destroy_machine needs the machine’s name twice, so a name a model produced but never verified fails instead of deleting something real.

5

Adding a cloud doesn’t need a release

A cloud is already data: some OpenTofu HCL, the environment its credentials map onto, and the lists that fill the New machine form. So the catalogue is a JSON file fetched at runtime from codexremote.io/registry.json, and adding a provider is a pull request rather than a release.

Point the app at your own registry, under Settings → Providers, and you get your own catalogue: a homelab module, a provider nobody has added yet, or a fork with the packages and volumes your team wants baked in.

HCL can live in its own .tf file, pinned to a SHA-256. That hash is the point: this HCL runs against your cloud credentials, and without it whoever serves that URL — or takes the domain over in two years — could change what gets applied long after the registry was read.

Adding one

No Swift, and no waiting for a release. Start from the registry that ships:

$ curl -O https://codexremote.io/registry.json
  1. Write the entry
    An id, the OpenTofu provider at a pinned version, the credentials to ask for and the environment they map onto, the HCL for one machine, and fallback lists of regions, sizes and images.
  2. Give the HCL the outputs it owes
    It is handed name, region, size, image, ssh_public_key, ssh_key_ids, user_data and tags, and must declare at least instance_id and public_ipv4 — without those a machine comes up with no way to reach it.
  3. Check it before anyone else has to
    swift test --filter ProviderRegistryDocumentTests validates the shipped registry, so a malformed entry fails on your machine rather than in review.
  4. Host it, or send it
    Point Settings → Providers at your own copy and it is yours alone — right for a homelab or an internal cloud. Or open a pull request against site/registry.json and every install picks it up on its next refresh, no update needed.

Full format, the HCL contract and what the loader refuses: docs/registry.md · CONTRIBUTING.md