Examples
Recipes for the things you'll actually do, with real transcripts trimmed to the lines that teach something.
Sync a project to a GPU box #
When you want your laptop's edits to land on a beefier build/train box in real time.
From inside the project, initialize it and name the peer once:
$ tomo init initialized Tomo project in .tomo $ tomo sync dev@gpu-box:~/proj recorded remote dev@gpu-box:~/proj connecting to gpu-box over SSH pushed remote binary tomo 0.2.0 (x86_64-unknown-linux-musl, 48234112 bytes) peer connected synced src/main.rs synced Cargo.toml
That one command did a lot: it recorded the peer in .tomo/config.toml, opened the SSH connection, copied a matching static binary into the remote's .tomo/bin/ (nothing was pre-installed there), started it, and began syncing. On a terminal it opens a live view of the session — the stream, a status heartbeat, and a conflict center on c. d detaches and leaves it syncing; q stops it (with a confirm). A bare tomo sync later reuses the recorded peer; add --plain for the classic line stream.
Get build artifacts back automatically #
When the box compiles or trains something and you want the output on your laptop without copying it back by hand.
Sync is two-way by default, so anything the remote writes flows back the same way your edits go out. Start a session, edit locally, and when the remote build drops a binary you'll see it arrive:
$ tomo sync dev@gpu-box:~/proj peer connected synced src/main.rs # your edit, going out synced target/release/proj # the box's build, coming back
The artifact lands at the same path locally. If you'd rather a path only move one way — say, pull the server's dist/ but never push your local one — that's a direction rule in config (direction = "pull" or "push").
Ignore the stuff you don't want synced #
When target/ or other generated junk shouldn't cross the wire.
node_modules is already ignored by default (along with .git, Python virtualenvs and caches, .terraform, and the rest), so you rarely need a rule at all. For something Tomo doesn't ignore out of the box — like target/, which is left syncable on purpose so build artifacts can flow back — add an ignored rule to .tomo/config.toml (a trailing slash expands to match everything under the directory):
[[rules]] pattern = "target/" class = "ignored"
Now only real source crosses over — target/ because of your rule, node_modules for free:
$ tomo sync --local-peer /tmp/peer # (any sync; local peer shown for brevity) $ ls /tmp/peer/src → main.rs $ ls /tmp/peer/target → No such file or directory # your ignore rule $ ls /tmp/peer/node_modules → No such file or directory # ignored by default
You don't need rules for the usual suspects: .git, .tomo, node_modules, Python virtualenvs and caches, .terraform, editor swap/backup files, and .DS_Store are ignored by default. To sync a normally-ignored tree anyway it takes two rules (one to un-ignore the directory, one for its contents) — the re-include nuance covers it.
See what changed and roll back #
When you want your history: what changed, when, and how to undo it.
Every save is a version. List a file's history newest-first:
$ tomo log notes.txt history of notes.txt (newest first): #3 present 32 B replica 10f71c82 local 2s ago {10f71c82:3} #2 present 29 B replica 10f71c82 local 4s ago {10f71c82:2} #1 present 18 B replica 10f71c82 local 6s ago {10f71c82:1}
Drop the path for repo-wide recent activity (tomo log). Compare two versions with diff:
$ tomo diff notes.txt --version 2 --against 3 diff notes.txt: version #2 → version #3 (- base / + target): line one - line two + CHANGED two line three
Roll back. With no version it undoes the last save; pass --version for an exact one:
$ tomo restore notes.txt restored notes.txt to version #2 (29 B) $ tomo restore notes.txt --version 1 restored notes.txt to version #1 (18 B)
The restore is written safely (staging + atomic rename). If a session is running, the restored bytes sync to the peer as an ordinary edit. Use --stdout to inspect an old version without touching your working copy.
Deal with a conflict #
When the same file got edited on both machines and you want to know what happened to the other version.
Say both sides changed config.txt while briefly disconnected. Sync doesn't stop and nothing is lost — both machines independently pick the same winner, and you get a non-blocking heads-up:
$ tomo status conflicts 1 ⚠ 1 unresolved conflict — see `tomo conflicts list`
$ tomo conflicts list 1 unresolved conflict(s) (oldest first): #1 [OPEN ] config.txt 4s ago winner #2 present 26 B replica e4a0830e local loser #3 present 26 B replica 1b47b618 remote
show gives you the full picture, including a diff of the two heads:
$ tomo conflicts show 1 conflict #1 on config.txt (unresolved) recorded 11s ago (2026-07-20 23:40:35Z) winner #2 present 26 B replica e4a0830e local loser #3 present 26 B replica 1b47b618 remote diff (loser → winner, - loser / + winner): shared config - port = 7070 + port = 9090
Then pick a resolution. Keep what's on disk and just acknowledge it:
$ tomo conflicts resolve 1 --keep-current acknowledged conflict #1 on config.txt (kept current file)
Or take the version that lost (it was preserved in history all along):
$ tomo conflicts resolve 1 --take-loser took loser #3 of conflict #1: wrote 26 B to config.txt
Either way sync never paused, and the version you didn't keep is still recoverable. Mass-acknowledge everything with tomo conflicts resolve --all --keep-current.
Same thing, in the TUI. If a session is attached, the ⚠ badge in the status line already told you a conflict landed. Press c for the conflict center: the winner-vs-loser diff is right there, framed as "on disk now" vs "in history". Enter keeps the current file, t takes the version in history, b keeps both — and the bottom line shows the exact tomo conflicts resolve command it just ran, so you never have to leave the keyboard to learn the CLI. u undoes a verdict you change your mind about.
Detach and attach from another terminal #
When you want the session running in the background and a window onto it from wherever you happen to be.
A sync session is a server; the terminal you started it from is just one client. Start it detached and it returns to your shell, still syncing:
$ tomo sync -d dev@gpu-box:~/proj session started (pid 48120) — attach: tomo attach · stop: tomo stop
Now open a second terminal — another SSH pane, a tmux split, anywhere on the same machine — and attach. You get the same live view; detaching never disturbs the session, and several terminals can attach at once:
$ tomo attach # the TUI, from any terminal $ tomo attach --plain # the line stream instead $ tomo logs -f # or just tail the session log
When you're done, stop it from anywhere (idempotent — a second stop is a friendly no-op):
$ tomo stop stopped session (pid 48120)
A bare foreground tomo sync is exactly this — sync -d plus an implicit attach — so there's one session, whichever way you start it.
Script the event stream #
When you want CI, a hook, or a notifier to react to what the session is doing.
Everything the TUI shows rides a versioned event feed on the session's local control socket. tomo events --json streams the raw records — pipe them through jq and build whatever you like. Ping yourself the moment a conflict lands:
$ tomo events --json | jq -r 'select(.event=="conflict") | .path' src/train.py
Or watch what's arriving from the peer without opening a UI:
$ tomo events --json \
| jq -r 'select(.event=="synced") | " ↓ \(.path) \(.size)B"'The records carry a "v":1 version and are additive-only, so a script written today keeps parsing tomorrow's sessions. It's the same contract as --json on status, log, and conflicts. More on the feed in The interface.
Work offline and reconnect #
When the network drops mid-session and you keep working.
A dropped link isn't fatal. The session keeps watching, indexing, and versioning locally, and reconnects on its own:
$ tomo sync dev@gpu-box:~/proj peer connected peer disconnected — queueing changes (peer disconnected) # …you keep editing; nothing blocks… reconnected peer connected
On reconnect, the normal handshake is the offline queue: it reships every change the peer doesn't already have, so the two sides converge to identical trees. If both machines edited the same file while apart, that's just a conflict — handled the same non-blocking way, nothing lost.
Sync through a jump host or nonstandard SSH #
When the box is only reachable via a bastion, on a weird port, or with a specific key.
Tomo reads ~/.ssh/config the way your own ssh does, so if ssh vm1 works, so does Tomo. Given a block like:
Host vm1
HostName 10.0.0.5
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_ed25519
ProxyJump bastionyou just use the alias — the jump, port, user, and key all come from the config:
$ tomo sync vm1:~/projWhen it doesn't work, tomo dev ssh-route shows exactly the route Tomo resolved — the analogue of ssh -G — so you can spot a missing key or an unpicked jump:
$ tomo dev ssh-route vm1 route to vm1: vm1 (10.0.0.5 via bastion) proxy jump: bastion [jump] bastion hostname bastion.example.com port 22 user jump identity files /home/you/.ssh/id_ed25519 [target] vm1 hostname 10.0.0.5 port 2222 user deploy identity files /home/you/.ssh/deploy_ed25519 stricthostkeychecking yes
The full directive matrix (ProxyJump chains, known-hosts semantics, IdentitiesOnly, and more) is in Configuration.
Try it on two local folders #
When you want to see how it behaves without a second machine.
--local-peer syncs against a directory on the same box (it spawns a served peer rooted there). No SSH, no remote — handy for a first look:
$ tomo init $ tomo sync --local-peer /tmp/peer local peer at /tmp/peer peer connected
Edit a file in your project and watch it appear under /tmp/peer; delete one and watch it go. Everything else on this page — history, conflicts, ignore rules — works exactly the same way against a local peer.