Help us shape the future of Go! Take the 2026 Go Developer Survey

onthego

module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2026 License: MIT

README

Start on your desktop. Finish on your laptop. A desktop runs onthego sync laptop, its work streams across to a laptop, the desktop turns off, the laptop opens and resumes the same Codex session, then the work comes back home.

onthego copies your uncommitted work, your .env and other local files,
and your Codex and Claude Code sessions to your other machine.
Then you pick up the same session there.

npm CI macOS and Linux No account, no server MIT license

Website   /   Docs   /   Quick start   /   Tailscale   /   What's new


npm install -g @jbaehova/onthego
onthego sync laptop

That's the whole idea. Staged, unstaged, and untracked work arrives exactly as you left it. So do gitignored files such as .env and local config, and this project's agent sessions with their memory and permissions. On the other device, run codex resume or claude --resume and keep talking.

How it works

Each handoff is captured, encrypted for the receiving device, sent over your own SSH, and resumed there. Your Git history stays clean, with no "wip" commits. Secrets such as .env travel encrypted and never enter a Git object. Regenerable output such as node_modules and build directories, plus Git config, hooks, and agent logins, stay on each machine.

Install

npm install -g @jbaehova/onthego

Ships native binaries for macOS and Linux (arm64 and amd64). Requires Git and OpenSSH.

Other ways to install

Without Node.js (servers, minimal machines). Downloads the release binary, verifies its SHA-256 checksum, and installs to ~/.local/bin:

curl -fsSL https://raw.githubusercontent.com/peerproblem/onthego/main/install.sh | sh

From source with Go 1.26 or newer:

git clone https://github.com/peerproblem/onthego.git && cd onthego
make build && install -m 755 dist/onthego ~/.local/bin/onthego

You only install it once by hand. onthego device add <name> --install copies the exact same version to your other devices over SSH.

Quick start

1. On your desktop, inside a Git project:

onthego init                                            # once per project; commit .onthego.yaml
onthego device add laptop --host me@laptop.local --install
onthego sync laptop

2. On the laptop, continue where you stopped. The handoff prints the exact commands:

cd ~/projects/my-app
codex resume <session-id>                               # or: claude --resume <session-id>

3. Back at the desktop, bring the work home:

onthego sync laptop

[!TIP] Run every command from the device that can reach the other one. A laptop that can reach a VPS can both send work to it and bring work back, so the VPS never needs to reach the laptop.

How sync decides

sync pushes when only this side changed, pulls when only the other side changed, and stops when both changed

sync compares both sides with their last handoff and only moves work away from the side that has not changed.

Situation sync does
The project is not on the other device yet push
Only this device changed push
Only the other device changed pull
You handed the work off and have not touched it since pull, to take it back
Nothing changed nothing
Both sides changed stops and asks you to pick push --replace or pull --replace

Nothing is merged behind your back. Whenever work is replaced, the previous state is kept as a Git backup that onthego undo restores. Preview any decision with onthego sync laptop --dry-run, or choose the direction yourself with push and pull.

Connect your devices

onthego uses your normal SSH setup: ~/.ssh/config, keys, and known_hosts. All it needs is one device that can ssh into the other without a password prompt. onthego doctor <device> checks the whole chain.

Same network

# On the laptop: turn on SSH
#   macOS: System Settings > General > Sharing > Remote Login
#   Linux: sudo systemctl enable --now ssh

# On the desktop:
ssh-copy-id me@laptop.local
onthego device add laptop --host me@laptop.local --install

An alias from ~/.ssh/config also works (--host laptop), as do --port and --identity-file. Private keys are never copied.

Anywhere, with Tailscale

When your devices are on different networks, Tailscale gives each one a stable name and onthego looks it up for you.

onthego tailscale                                       # checks Tailscale, lists your devices
onthego device add laptop --tailscale me@laptop --install

Install Tailscale on each device and sign in with the same account. Each device you pair to still needs SSH turned on (or Tailscale SSH on Linux). onthego only reads tailscale status and never changes your settings. See the Tailscale guide for MagicDNS, Tailscale SSH, and troubleshooting.

A VPS

onthego device add vps --host me@vps.example.com --install
onthego sync vps

--install detects the server's OS and CPU and copies the matching binary to ~/.local/bin/onthego. The server needs neither Node.js nor internet access. After upgrading locally, onthego device install vps brings the server to the same version.

Where the project lands

By default, the same path relative to your home directory: ~/projects/my-app on macOS becomes /home/me/projects/my-app on Linux, and agent session paths are remapped to match.

onthego device path laptop /Users/me/code/my-app        # choose another location
onthego device list                                     # devices and this project's path on each

The first destination directory should not exist yet. To adopt an existing copy, use push --replace once; a Git backup of the destination is kept.

What moves

Everything you need to keep working moves. Only device-bound credentials, output you can regenerate, and paths you exclude stay behind.

Git history Only the objects the other side is missing. No Git hosting needed.
Staged and unstaged work Kept separate, including binary files and executable bits.
Untracked files Included, subject to exclude rules.
Gitignored files Included: local config, .claude/settings.local.json, CLAUDE.local.md, fixtures, scratch files. Regenerable output is left out.
Secrets such as .env, .env.local, .npmrc Included, encrypted for the receiving device and kept outside Git objects. Written with mode 0600. Turn off with secrets: skip.
Volatile files such as dev SQLite databases Included, but changing them alone does not count as a change, so a dev server writing to its database does not block a handoff.
Agent sessions This project's Codex and Claude Code sessions from the last 7 days. Paths are remapped; content is not rewritten.
Agent project state Claude Code project memory, and this project's allowed tools, MCP servers, and trust from ~/.claude.json. Codex project trust from ~/.codex/config.toml. The settings apply only when the other device already has ~/.claude.json, and Codex trust only when it already has ~/.codex.
Regenerable output Left out of gitignored files: node_modules, .venv, venv, __pycache__, target, dist, build, .next, .nuxt, .turbo, .gradle, .terraform, coverage, caches, and *.log. Add a path to include to carry it anyway, unless exclude also lists it.
Never moves Credentials and private keys (.ssh/, id_rsa, id_ed25519, credentials.json, auth.json), agent logins, Git config, hooks, remotes, stashes, and running processes.

Each handoff is one package, encrypted for the receiving device with age and signed by the sender with Ed25519. Pairing pins both identities, so a device whose keys change is rejected.

onthego inspect and onthego push laptop --dry-run list every file outside Git history that would move, with its size, and what stays behind with the reason. Paths you list under exclude are left out silently. A handoff carries up to 2 GiB of workspace data. A gitignored directory with more than 20000 files is skipped with a warning, and any carried file over 100 MiB is reported, so you can exclude what you can regenerate.

[!NOTE] Stop agents, dev servers, and other processes that write to the project before a handoff. .env files, MCP server settings, and agent transcripts can contain secrets; they are encrypted in transit and stored on the other device the way they were stored on this one. Anything already committed to Git travels as recorded.

Everyday commands

onthego status                       # changed since the last handoff? which device has the work?
onthego status laptop                # the same, for the laptop's copy
onthego inspect                      # every file that would move, and what stays behind
onthego sync laptop --dry-run        # show the decision without moving anything
onthego push laptop --dry-run        # preview files and sessions
onthego push laptop --session-days 2 # or --session <id>, --all-sessions, --no-sessions
onthego push laptop --include coverage/lcov.info   # also carry a path that is normally left out

When something goes wrong

  • Both devices changed. sync, push, and pull stop with exit code 6. --replace overwrites the other side after saving a Git backup there.
  • Undo a handoff. onthego backups list shows backups and onthego undo restores the latest. The newest five are kept automatically; onthego backups prune --keep 3 trims them.
  • Your own copy of a file was replaced. An incoming file such as .env replaces a local copy that onthego was not managing yet, most often on the first handoff after upgrading. The local copy is kept in the backup, and onthego undo restores it.
  • The other device runs 0.4. It is refused before anything moves with peer lacks carry-all. Run onthego device install <device> to bring a paired device to your version, or pair a new one with onthego device add <device> --install.
  • Diverged agent session. The destination keeps its copy and the incoming one is saved under ~/.onthego/conflicts/. Change this with --session-conflict keep-both|theirs|skip.
  • Sessions could not be written. The workspace still arrives, the sessions are kept privately, and status tells you. Fix the cause and run onthego sessions recover.
  • SSH trouble. Errors name the problem and the fix: a missing key, an unknown host key, a refused or unreachable host, or onthego missing on the device.

Reference

Configuration

onthego init writes .onthego.yaml. Commit it so every device shares the same project ID.

schema_version: 3
project_id: generated-by-onthego-init
include:
  - .onthego.yaml
exclude:
  - .git
  - .onthego.local
  - node_modules
  - vendor
  - dist
  - build
  - .cache
  - __pycache__
ignored: carry     # carry gitignored files, or skip
secrets: carry     # carry .env and other secrets encrypted, or skip
agent: auto        # auto, codex, claude, or none (workspace only)
Key Default Meaning
ignored carry skip carries gitignored files only through include, as in 0.4. A gitignored secret that Git lists by name, such as .env, still travels while secrets: carry.
secrets carry skip leaves .env and other secret candidates behind and reports them. --include-secret <path> then carries one anyway.
volatile built-in list Files that travel but never count as a change on their own. Left out, it means *.sqlite, *.sqlite3, *.db, and their -journal, -wal, and -shm files. volatile: [] turns it off.

volatile patterns work like .gitignore. A name without a slash, such as *.db, matches at any depth. A pattern with a slash is anchored at the project root. exclude matches gitignored files the same way, so node_modules also covers apps/web/node_modules. For files Git can see and for include paths, exclude keeps the 0.4 rule: a pattern matches the path from the project root, everything under it, or the whole path as a glob. A new untracked file in a nested build/ directory therefore still travels.

Schema 2 files keep working and get the new defaults without edits. A schema 2 file that uses ignored, secrets, or volatile is rejected with a message asking for schema_version: 3. onthego init writes schema 3 and keeps the project ID.

Paths passed with --include or --include-secret stay managed in later handoffs, including deletions. Adding a managed file to exclude stops sending it and leaves the receiving copy in place with a warning.

Private state lives in ~/.onthego (or $ONTHEGO_HOME) with 0700 directories and 0600 files: paired devices in config.yaml, keys in identity/, and per-project paths under projects/. onthego carries .env files but never loads them.

Shell completion, including device names:

onthego completion zsh > "${fpath[1]}/_onthego"         # also: bash, fish
Offline handoff

Without a network path, hand off through an encrypted file on a USB drive or any other channel.

onthego identity                                        # on the receiving device: prints its public keys
onthego snapshot pack --output /tmp/my-app.otg --recipient age1...

# On the receiving device:
onthego snapshot restore /tmp/my-app.otg --output ~/projects/my-app --signing-public <sender-signing-key>
All commands
Command Purpose
sync <device> Push or pull, whichever brings both devices up to date.
push <device>, pull <device> Hand off in an explicit direction.
status [device] Changes since the last handoff, and which device has the work.
inspect Preview every file that would move, what stays behind and why, and sessions.
init Set up the current Git project.
device add / list / path / install / remove Pair devices, map project paths, install or upgrade onthego on them.
tailscale Check Tailscale and list tailnet devices to pair.
doctor [device] Check Git, SSH, Tailscale, agents, and a device connection.
undo, backups list / prune Restore or manage workspace backups.
sessions recover Retry an agent session import after a partial handoff.
snapshot pack / restore Offline handoff through an encrypted file.
identity, config path, completion, version Keys, settings location, shell completion, build info.

Every command accepts --json for structured output and errors, and --verbose for transfer phases on stderr.

Exit codes
Exit Meaning
0 Success, including a delivered workspace with pending session recovery
1 E_INTERNAL
2 E_INPUT
3 E_PRECONDITION, E_UNSUPPORTED_GIT, E_SECRET_POLICY, E_SESSION_FORMAT
4 E_TARGET_AUTH, E_TARGET_UNAVAILABLE (SSH login, host key, or reachability)
5 E_PACKAGE_INVALID, E_RESTORE_FAILED
6 E_GENERATION_CONFLICT, E_SOURCE_CHANGED
130 E_CANCELED

Upgrading

Both devices need 0.5.0. A device that still runs 0.4 is refused before anything moves with peer lacks carry-all and the command that fixes it. Upgrade every device:

npm update -g @jbaehova/onthego
onthego device install laptop                           # repeat for each SSH device

After the upgrade, .env and other gitignored files travel by default, with no change to .onthego.yaml. The first sync still decides as before: a side you have not touched counts as unchanged. If nothing changed since the last 0.4 handoff, sync has nothing to move, so run onthego push <device> once from the device that holds the work. On that first handoff, an incoming file replaces a local copy that onthego was not managing yet, and the local copy stays in the backup for onthego undo.

onthego remote set still works but is replaced by onthego device path. See the 0.5.0 release notes and the migration notes.

Documentation

The full guide lives at peerproblem.github.io/onthego/docs. The deeper notes are also in this repository:

Tailscale guide Pairing across networks, Tailscale SSH, troubleshooting.
Agent compatibility Tested Codex and Claude Code versions, what agent state travels, and how resume is verified.
Architecture File selection, capture, encryption, in-place application, and receipts.
Development Tests, demos, end-to-end checks, benchmarks, and releasing.

Handoffs are explicit on purpose: you always know which device has the work. Continuous background synchronization is out of scope.

License

MIT

Directories

Path Synopsis
cmd
onthego command
internal
agentsessions
Package agentsessions transfers project-scoped native agent histories and the project's agent state (Claude Code memory and project settings, Codex project trust).
Package agentsessions transfers project-scoped native agent histories and the project's agent state (Claude Code memory and project settings, Codex project trust).
cli
fsx
Package fsx centralizes filesystem boundaries used by handoff operations.
Package fsx centralizes filesystem boundaries used by handoff operations.
peertransport
Package peertransport runs ONTHEGO directly on a user-owned peer.
Package peertransport runs ONTHEGO directly on a user-owned peer.
syncer
Package syncer coordinates authenticated handoffs between user-owned devices.
Package syncer coordinates authenticated handoffs between user-owned devices.
tailscale
Package tailscale reads the local Tailscale client state so devices on the user's tailnet can be paired by name.
Package tailscale reads the local Tailscale client state so devices on the user's tailnet can be paired by name.
scripts
agent-compatibility command
Command adapter exposes the production native-session adapter to the real-CLI compatibility harness.
Command adapter exposes the production native-session adapter to the real-CLI compatibility harness.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL