Browse documentation

Happy Desktop docsGuides

Run Claude Code and Codex on a Remote Server

Install Happy Agent on a server, a VPS, or a Mac mini. It is one agent harness for Claude, Codex, and Grok, signed in with the subscriptions you already pay for. It keeps working while your laptop sleeps, and you steer it from Happy Desktop or the Happy phone app, alone or with your team.

The problem

A coding agent runs where you start it. Start Claude Code or Codex on your laptop, and the work stops when the lid closes. The usual workarounds each solve part of that:

  • Keep the laptop open. It has to stay awake, plugged in, and online.
  • Claude Code Remote Control. A window into a Claude Code session on your computer, from the Claude app or a browser. While the laptop sleeps, the work pauses until it wakes.
  • A server with tmux and SSH. The work keeps going, but you steer it through a terminal, one CLI per provider with its own login, and type approvals on a phone keyboard.
  • Working with a teammate. Usually means sharing the machine, a shell account or a VM, rather than the agent's sessions.

Happy Agent takes the server route without the terminal: one runtime on the server, a desktop app and a phone app in front of it, and a team mode for sharing it.

Alternatives

tmux + SSH or Mosh + Tailscale + TermiusClaude Code Remote ControlA VM with its own agent (exe.dev and Shelley)Happy Agent on a server
AgentsAny terminal agentClaude CodeShelley; Claude Code, Codex, and Pi preinstalledClaude, Codex, and Grok models in one harness, on your existing subscriptions
Runs onYour serverYour computer, or a server inside tmuxAn exe.dev VMYour server, VPS, or Mac
While your laptop sleepsKeeps runningOn a laptop, pauses until it wakes; on a server, keeps runningKeeps runningKeeps running
Steer it fromA terminal app on any device, over SSH or MoshThe Claude app, claude.ai/code, or Claude DesktopShelley's web page; SSH or the browser terminal for the other agentsHappy Desktop and the Happy phone app
ApprovalsTerminal prompts, typed by handPermission prompts in the app, with push notificationsDepends on the agent; the VM is the boundaryAuto mode reviews actions itself, inside the OS sandbox
With a teamShell accounts on the serverOne person's sessions; organization controls on Team and EnterpriseShare the VM with Web or Root access, per person or with the whole teamTeam mode: each member signs in as themselves, in shared projects and sessions
In betweenTailscale's coordination and relays; traffic WireGuard-encryptedAnthropic stores the transcript while connectedexe.dev runs the VM and its HTTPS proxyTailcat relay for Desktop, WireGuard-encrypted; Happy's relay for the phone, end-to-end encrypted
SetupInstall and connect four toolsOne commandCreate a VMAsk the Chief of Staff, or about a dozen steps

Where each is better:

  • tmux + SSH or Mosh. Works with every terminal agent and tool, with nothing new to trust. Mosh survives sleep and network changes, and Termius speaks it on iOS and Android.
  • Remote Control. Nothing to install if you already use Claude Code and the Claude app, Anthropic supports it, and permission prompts reach your phone as push notifications. Team and Enterprise get admin controls. See Happy vs Remote Control.
  • exe.dev. A persistent VM with agents already installed, Shelley, a web agent that works on mobile, and VM sharing that follows team membership. No service to set up. You can also run Happy Agent on it, below.
  • Happy Agent. One runtime for three model families under one sandbox and review, a desktop app and a phone app built for agent sessions, and a team mode where everyone keeps their own identity. The cost: more setup than the others, and you run and upgrade the server.

Sources, checked October 10, 2026: Remote Control docs ("your computer has to stay on and the claude process has to keep running"; "if your laptop sleeps or your network drops, Claude Code reconnects automatically"; "start it inside tmux or screen" on a remote machine), exe.dev docs (claude, codex, and pi preinstalled) and sharing, Mosh, Termius, How Tailscale works.

Share it with your team

The same server can serve a team. In team mode:

  • Each member signs in with their own Happy Social account from their own Happy Desktop, and pairs their own phone.
  • Everyone works in the server's shared projects and sessions. Whenever the speaker changes, the agent is told who it is; drafts and each person's task order stay their own.
  • The same sandbox and review rules apply to everyone: commands run inside the OS sandbox unless a session is switched to Full access, and in Auto each action that would cross it is reviewed first.
  • The server stops accepting its API token. Only Happy Social sign-ins issued for this team get in.

Before you do: all work spends the server's Claude, Codex, and Grok accounts, not each member's, and any member can choose Full access. There are no per-session invites, and Happy ships no remove-member action yet.

To set it up, ask the Chief of Staff, "Create a team called Acme on my server", or follow Multiplayer & Teams, which has the steps and a diagram of how sign-in, sharing, and the sandbox fit together.

How the connection works

Desktop reaches the server over Tailcat, built into every release. Tailcat is Tailscale's open source data plane without its control plane: no account, no open port, no public IP. SSH is used only to install.

  • The address is the server's key. A tc… address encodes the server's WireGuard public key and its relay region. The private key stays on the server in ~/.happy/agent/tailcat/default.private.json, so only that machine can answer at that address.
  • WireGuard, end to end. Your machine makes a throwaway key each time it opens the tunnel and runs the standard WireGuard handshake with the server. Everything after that is encrypted between the two machines, and decrypted only on the server, where Tailcat hands it to Happy Agent.
  • Relays carry ciphertext. Both sides meet first at a DERP relay that Tailscale runs for free, rate-limited and without an uptime promise. They then try a direct path through NAT, and the relay keeps carrying traffic if that fails. A relay sees IP addresses, timing, sizes, and the public keys it routes by. It cannot read or change the content.
  • The address is not the lock. Anyone who has the address can open a tunnel; Happy does not use Tailcat's client allowlist. Every request inside still needs the server's API token, or a team sign-in. A relay operator sees enough to open a tunnel too; the token, not the address, is what keeps them out.

Your phone does not use Tailcat. It pairs with the server through Happy's own end-to-end encrypted relay.

Ask the Chief of Staff

Get a Linux machine you can SSH into (providers), then tell the Chief of Staff in Desktop:

Put Happy Agent on my server ubuntu@203.0.113.10. Call it Build box. Use my Codex and Claude accounts, and set up GitHub for acme/api.

It runs the deployment end to end and stops only for what is yours to decide:

  • SSH: the account to use and the host key.
  • Provider accounts: which ones the server may use. A copied credential gives that machine your account and its limits, so it prefers a fresh sign-in on the server where it can.
  • Sign-ins: browser or device codes for Claude, Codex, Grok, or gh, and any macOS Keychain prompt.
  • Exposure: turning on Tailcat on the server.
  • Ubuntu 24.04: an AppArmor allowance for the Happy Agent binary, so the sandbox can start.
  • Git identity: it proposes your name and email; say if the server should use different ones.

It copies your profile name and email, installs the service, registers the connection, runs a test task in a throwaway project, restarts the service, and checks again. An existing installation is inspected and kept, not replaced. The handoff lists what passed and anything still waiting on you.

Later: "Upgrade Happy Agent on Build box."

By hand

The steps the Chief of Staff follows, for a fresh Debian or Ubuntu machine with systemd. Run them over SSH as a user with sudo. Never paste tokens or auth files into chat or command arguments.

1. Check the machine

uname -m          # x86_64 → linux-x64, aarch64 → linux-arm64
systemctl --version

Releases ship linux-x64, linux-arm64, darwin-arm64, darwin-x64, and win32-x64. Inbound, only SSH for setup; Tailcat needs outbound internet only.

2. Create the service account

The daemon, provider sign-ins, Git, and repositories all belong to happy-agent, not root.

sudo apt-get update
sudo apt-get install -y ca-certificates curl git jq tar
sudo useradd --system --create-home --home-dir /var/lib/happy-agent \
  --shell /bin/bash happy-agent
sudo chmod 0700 /var/lib/happy-agent
sudo install -d -m 0700 -o happy-agent -g happy-agent \
  /var/lib/happy-agent/happy/config \
  /var/lib/happy-agent/.happy \
  /var/lib/happy-agent/.config/happy-agent/credentials \
  /var/lib/happy-agent/projects

3. Install the binary

Latest stable release, checksum verified:

set -eu
cd "$(mktemp -d)"
TAG="$(curl -fsSL 'https://api.github.com/repos/slopus/happy-agent/releases?per_page=100' |
  jq -er '[.[] | select(.draft == false and .prerelease == false) |
    select(.tag_name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))][0].tag_name')"
VERSION="${TAG#v}"
case "$(uname -m)" in
  x86_64) TARGET="linux-x64" ;;
  aarch64|arm64) TARGET="linux-arm64" ;;
  *) echo "Unsupported architecture" >&2; exit 1 ;;
esac
ARCHIVE="happy-agent-$VERSION-$TARGET.tar.gz"
URL="https://github.com/slopus/happy-agent/releases/download/v$VERSION"
curl -fLO "$URL/$ARCHIVE"
curl -fLO "$URL/$ARCHIVE.sha256"
sha256sum --check "$ARCHIVE.sha256"
tar -xzf "$ARCHIVE"
sudo install -m 0755 "happy-agent-$TARGET" /usr/local/bin/happy-agent
/usr/local/bin/happy-agent --version

Expect Happy Agent <version>. The binary embeds Tailcat and the sandbox; no Node.js or separate Tailcat install. Stop on a checksum failure.

4. Ubuntu 24.04: let the sandbox start

Ubuntu 24.04 can block the sandbox's user namespaces. If sysctl kernel.apparmor_restrict_unprivileged_userns reports = 1, grant the Happy Agent binary, and only it:

sudo tee /etc/apparmor.d/happy-agent >/dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>

profile happy-agent /usr/local/bin/happy-agent flags=(unconfined) {
    userns,
}
EOF
sudo chmod 0644 /etc/apparmor.d/happy-agent
sudo apparmor_parser --skip-kernel-load /etc/apparmor.d/happy-agent
sudo apparmor_parser --replace /etc/apparmor.d/happy-agent

Do not disable AppArmor, change the sysctl, or switch to Full access instead. If the machine already confines Happy Agent, amend that profile.

5. Provider credentials

Only the providers you chose. Install each file as the service account, mode 0600:

sudo install -m 0600 -o happy-agent -g happy-agent \
  /PRIVATE_STAGING_PATH/auth.json \
  /var/lib/happy-agent/.config/happy-agent/credentials/codex.json
ProviderCredentialhappy.toml
CodexYour file-backed ~/.codex/auth.json, whole, copied with scp to a mktemp -d staging folder[providers.codex] auth_file = ".../credentials/codex.json"
ClaudeA long-lived token from claude setup-token[providers.claude] oauth_token = "..."
GrokYour ~/.grok/auth.json, whole[providers.grok] auth_file = ".../credentials/grok.json"
BedrockAn instance role on AWS; otherwise a Bedrock key[providers.bedrock] region, bearer_token
API keyAny of the above, billed per useapi_key = "..." instead

Codex and Grok rewrite their files when they refresh, so keep the file and its folder writable by happy-agent. Two machines sharing one OAuth login may interfere when tokens rotate; if that happens, sign in on the server instead (sudo -u happy-agent -H codex login, with the provider's CLI installed there) and leave out auth_file: Happy reads each CLI's usual location. Claude tokens expire; renew with claude setup-token.

6. Write happy.toml

/var/lib/happy-agent/happy/config/happy.toml:

[providers]
default_enable = false

[providers.codex]
enabled = true
auth_file = "/var/lib/happy-agent/.config/happy-agent/credentials/codex.json"

[profile]
name = "Ada Lovelace"
email = "ada@example.com"

[feature.team]
enabled = false

[feature.tailcat]
enabled = true
port = 24779

[defaults]
permission_mode = "auto"
  • [profile] is your name and email. Both fields are required, and they stand in for the first-start profile screen. Write it before the first start.
  • Add provider and model under [defaults] if you want a default model, for example provider = "codex" and model = "openai/gpt-5.6-sol".
  • Keep Auto. Do not pick Full access to make setup checks pass.

Give the file to the service account:

sudo chown happy-agent:happy-agent /var/lib/happy-agent/happy/config/happy.toml
sudo chmod 0600 /var/lib/happy-agent/happy/config/happy.toml

Then append a fresh 43-character API token. This writes it without ever printing it, so it stays out of your terminal, chat, and shell history. Step 9 needs it again; copy it from that file privately, never by printing it into a chat or tool log:

sudo -u happy-agent -H sh -c 'umask 077; printf "\n[api]\ntoken = \"%s\"\n" "$(head -c 32 /dev/urandom | base64 | tr "+/" "-_" | tr -d "=\n")" >> ~/happy/config/happy.toml'

7. Run it under systemd

/etc/systemd/system/happy-agent.service:

[Unit]
Description=Happy Agent (standalone)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=happy-agent
Group=happy-agent
WorkingDirectory=/var/lib/happy-agent
Environment=HOME=/var/lib/happy-agent
EnvironmentFile=-/var/lib/happy-agent/.config/happy-agent/service.env
ExecStart=/usr/local/bin/happy-agent run
Restart=on-failure
RestartSec=2
UMask=0077

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now happy-agent
sudo systemctl is-active happy-agent

service.env is optional: private NAME=value lines, owned by happy-agent, mode 0600, for credentials given as environment variables. Do not also run happy-agent start; systemd owns the daemon.

8. Verify

sudo -u happy-agent -H bash -c 'curl -s --unix-socket ~/.happy/agent/server.sock \
  -H @<(printf "Authorization: Bearer %s\n" "$(cat ~/.happy/agent/token)") \
  http://localhost/v0/health'
sudo cat /var/lib/happy-agent/.happy/agent/tailcat/address
sudo cat /var/lib/happy-agent/.happy/agent/tailcat/port

Expect "status":"ready" in the health response, an address starting with tc, and 24779. The Tailcat files can take up to 90 seconds to appear. If health fails, read sudo journalctl -u happy-agent -n 100.

9. Add it to Desktop

Ask the Chief of Staff on your own machine to register it. It calls set_remote_connection with the name, address, port, and token, and needs no restart. Then check_remote_connection_health must report reachable, authenticated, and ready all true.

By hand, in your machine's global happy.toml (not the server's):

[connections.build-box]
name = "Build box"
address = "tc..."
port = 24779
token = "THE_SERVER_API_TOKEN"

Use the address exactly as printed, not a tailcat:// URL. The ID starts with a lowercase letter, then letters, digits, - or _. Restart Happy Agent on your machine to apply a file change. The server then appears in Desktop's connection rail beside your own machine.

Then send the server a small task in a new, throwaway project and confirm it answers and runs a command. Health alone does not prove the model or the sandbox works.

10. Pair your phone

Select the server in Desktop, then open Settings → Mobile Access. The server has its own pairing; pairing your local machine installs nothing on it.

11. Git and GitHub

Install GitHub CLI, then set up Git as the service account:

sudo apt-get install -y gh
sudo -u happy-agent -H git config --global user.name "Ada Lovelace"
sudo -u happy-agent -H git config --global user.email "ada@example.com"
sudo -u happy-agent -H gh auth login --hostname github.com --git-protocol https --web
sudo -u happy-agent -H gh auth setup-git --hostname github.com

Clone repositories into /var/lib/happy-agent/projects as happy-agent.

Upgrade

From an SSH session, not from an agent running on that server:

sudo -u happy-agent -H /usr/local/bin/happy-agent drain
sudo systemctl stop happy-agent
# back up /usr/local/bin/happy-agent, /var/lib/happy-agent, and the unit file
# download and verify the new release as in step 3, then:
sudo install -m 0755 "happy-agent-$TARGET" /usr/local/bin/happy-agent
sudo systemctl start happy-agent

drain waits until running work reaches a safe stopping point and prints Daemon drain is complete. Keep /var/lib/happy-agent/.happy/agent: it holds the data, the token, and the Tailcat identity, so the address and connection survive. Keep the AppArmor profile; it is bound to the binary's path.

Uninstall

In Desktop, ask the Chief of Staff to remove the connection (remove_remote_connection), or delete the [connections.<id>] entry. Then on the server:

sudo systemctl disable --now happy-agent
sudo rm /etc/systemd/system/happy-agent.service
sudo systemctl daemon-reload
sudo rm /usr/local/bin/happy-agent
sudo apparmor_parser -R /etc/apparmor.d/happy-agent   # only if you added it in step 4
sudo rm /etc/apparmor.d/happy-agent
sudo userdel happy-agent
sudo rm -rf /var/lib/happy-agent   # projects, history, credentials: back up first

Sign the server out of anything it held, or revoke those sessions from the provider's side: copied OAuth logins, claude setup-token tokens, and the gh login.

Providers

The steps above apply everywhere. Only what differs is listed here. We have not tested every provider; follow their own docs to create the machine.

Hetzner

A Cloud server with Ubuntu 24.04 or Debian. The Arm (CAX) types use linux-arm64. With a Hetzner firewall, allow inbound SSH and add no outbound rules; with none, all outbound traffic is allowed. Creating a server.

DigitalOcean

A Droplet with Ubuntu 24.04 LTS. A Cloud Firewall needs only inbound SSH. Create a Droplet.

AWS EC2 and Lightsail

Ubuntu 24.04 LTS; the login user is ubuntu. Graviton instances use linux-arm64. The security group needs only inbound SSH. For Bedrock on EC2, attach a least-privilege instance role, so there is no key to copy. EC2, Lightsail.

Google Cloud

A Compute Engine VM with Ubuntu 24.04 LTS or Debian. Arm machine types use linux-arm64. No firewall rule is needed for Tailcat. Create a Linux VM.

exe.dev

Persistent VMs, created with ssh exe.dev new. Claude Code and Codex come preinstalled, and you have sudo. Use linux-x64 after checking uname -m. Provider logins must belong to happy-agent, not your login user: sign in with sudo -u happy-agent -H codex login, or install copied credentials as in step 5. Use SSH with your own key; the exe.dev API token cannot SSH and is not a Happy credential. Skip first-boot scripts: they run once as your login user, not the service account. exe.dev's own agent, Shelley, keeps running; Happy Agent is a separate service beside it. exe.dev docs.

A Mac mini or old laptop at home

A laptop running Linux follows the steps above. Keep it plugged in and the lid open; a closed lid usually sleeps the machine.

On a Mac, use the darwin-arm64 or darwin-x64 archive and check it with shasum -a 256 -c. The config is ~/Happy/Config/happy.toml for the user that runs Happy Agent, and the sandbox is macOS's own, so there is no AppArmor step. Run happy-agent run as a launchd service under that user; Happy does not ship the plist, so the Chief of Staff writes one. Stop the Mac from sleeping with sudo pmset -a sleep 0.

Raspberry Pi and other ARM boards

Use linux-arm64, which needs a 64-bit OS: uname -m must print aarch64. A 32-bit OS (armv7l) is not supported.

Status

Remotes connect over Tailcat only. Desktop has no "add remote" button yet; register one with the Chief of Staff or happy.toml.