musubi makes several machines’ copies of one jj repository behave like workspaces of a single repository. This page explains what moves between machines, what stays on each, and what you see when machines change the same thing at once. For the design behind it, see docs/internals/design.md, and for how it’s checked, How musubi keeps your work safe.
The short version
- Every machine has the whole repository. Each machine’s copy is an ordinary jj
repository, with every commit, bookmark and change from every machine. You can unplug any of
them and keep working. Other machines’ working copies show up in it as workspaces, such as
laptop/default@: names for the commit each is on, not bookmarks, so nothing of theirs is ever pushed unless you push it. - Your work moves when jj records it. jj records your edits each time you run a
jjcommand, and musubi sends each record to the other machines within a second or so. Edits you’ve made since your lastjjcommand stay on that machine until the next one, or until jj records them by itself. - Your files are never touched. musubi never writes to your working copy. When another machine rewrites the commit you’re on, jj says so, and you update your files when you choose.
- You still push and fetch. musubi never talks to GitHub or any other remote for you. When you push or fetch on one machine, though, every machine sees the result.
- The hub is a meeting point, not a master. It keeps a copy of each repository it serves, in its directory on its machine, and decides the order in which machines’ work lands. Other machines reach it over ssh, with a key that can reach the hub and nothing else on its machine. It holds nothing the machines don’t have too. Anyone who can reach the hub can read and write every repository it serves, so treat it as you would any machine with your repositories on it.
- Leaving costs nothing.
musubi leavetakes a repository out of its hive, and it keeps everything it has, its working copy included.
Hubs, hives, and machines
flowchart LR
subgraph devbox["devbox"]
H["musubi hub"]
D1["daemon"] --- R1["~/src/app"]
end
subgraph laptop["laptop"]
D2["daemon"] --- R2["~/src/app"]
end
subgraph desktop["desktop"]
D3["daemon"] --- R3["~/code/app"]
end
D1 <-->|local socket| H
D2 <-->|ssh| H
D3 <-->|ssh| H- A hub is a
musubi hubprocess on a machine that is usually on. It keeps a full copy of each repository it serves and decides the order in which updates are accepted. It never makes changes of its own. - A hive is one repository as the hub knows it: a name, such as
app, and every machine’s copy of it. One hub serves any number of hives. - Each machine runs one daemon, which keeps every copy on that machine in sync with
its hive. The hub’s machine can have copies too, as
devboxdoes above.
Other machines reach the hub over ssh, so wherever you can ssh devbox, musubi works. The
daemon reconnects by itself after a network change or a sleep.
What moves between machines
Every jj command that changes the repository records what it did as an operation, in jj’s
operation log: the record that jj undo takes
back and jj op log shows. musubi syncs that record, not just branches. The daemon sends each
new operation, with the commits it needs, to the hub, which passes it on to the other
machines, within a second or so. So every machine has not only the same commits, but the same
history of how they got there.
| Shared across the hive | Each machine’s own |
|---|---|
| Commits, including ones no bookmark points to | Working-copy files |
| Bookmarks, tags, and remote bookmarks | Untracked and ignored files |
| The operation log | User config (~/.config/jj/config.toml) |
| Each machine’s working-copy commit, shown as a workspace | Most repo config |
| Git remotes: their names and URLs | The commit index, which jj rebuilds |
| Repo config that decides what is safe to rewrite or push |
Share settings says which remotes and repo config the hive shares, and how machines agree on them.
Every snapshot of a working copy replicates too, so a file that jj snapshots reaches every machine in the hive, even if you abandon it at once. Keep secrets in ignored files.
Because that history is shared, the rest of jj’s behaviour carries across machines:
jj op logshows what every machine did, and when.jj evologfollows a change through rewrites made anywhere.jj op revertandjj op restorework on operations from any machine.jj undorefuses to undo another machine’s operation unless you pass--allow-cross-workspace, as it refuses another workspace’s.- A fetch or push on one machine updates the remote bookmarks on every machine, so you only need to fetch once.
Working copies stay put
musubi never writes to working-copy files. It changes only what jj keeps (the operation log
and commits, and in a colocated repository, the .git refs that track bookmarks), and the
git remotes and repo config that the hive shares.
When another machine rewrites the commit your working copy is on, by rebasing its parent, say,
jj treats your working copy as stale, as it would after a rebase in another workspace on the
same machine, and your next jj command stops until you update the files. When another
machine rewrites your working
copy says
what to do, and why musubi doesn’t make jj do it by itself.
Other machines’ workspaces
Every machine has its own workspaces, its own default@ among them, and also shows every
other machine’s working copy as a workspace named after that machine:
@ vruxwmqv me@example.com 2026-10-23 10:14:02 default@ 3f2a1c9e
│ (empty) (no description set)
│ ○ kkmpptxz me@example.com 2026-10-23 10:12:40 laptop/default@ 8d0b4e11
├─╯ Add retry logic to the fetcher
◆ zzzzzzzz root() 00000000
laptop/default@ is the commit the laptop last snapshotted: edits it hasn’t snapshotted yet
stay on the laptop, as they would in any workspace. You can diff it or build on it like any
revision; see See what other machines are
doing.
A machine’s name defaults to jj’s operation.hostname; choose another with --machine when
you join. A machine that leaves with musubi leave, or that another machine forgets with
musubi forget, stops showing, and comes back if it’s used again. See Take a repository or
machine out.
When machines change the same thing
Changes made on different machines combine exactly as jj combines changes made by concurrent
jj commands on one machine, and every machine ends up with the same result. A bookmark moved
to different commits on two machines is conflicted everywhere, and a change rewritten on two
machines is divergent everywhere, until you resolve it once, on any machine.
Work across machines
says how.
When two machines send updates at once, the hub accepts the first and turns the other away; that machine merges its update with the first and sends the result. So each merge happens once, on the machine that made the later change, and the hive never gets two copies of the same rebase. docs/internals/races.md lists every case musubi tests, with its outcome.
Offline machines
A machine that loses its connection to the hub keeps working: jj never needs the hub. When it
reconnects, its daemon fetches what it missed, merges its own work with it, and sends the
result, as for any concurrent change. Meanwhile, musubi status shows its syncs failing, which
is expected.
Disk space
Every machine keeps every operation and commit of its hive, so the daemon cleans up what
jj util gc would: operations that nothing reachable from the current one needs, and files jj
no longer reads, none less than two weeks old. It runs once enough has built up, at most once
a week, and not on battery. Unlike jj util gc, it deletes no git object.
Moving and copying repositories
A copy is the repository that joined, wherever you move it: the daemon follows it. A repository made by copying a copy’s files is a duplicate, which musubi doesn’t sync, since two repositories syncing as one would overwrite each other’s working copy. See Move, copy or restore a repository, and for the cases musubi handles and leaves out, docs/internals/copies.md.
Operation ids differ between machines
Each machine records its own git refs and workspaces in its operations, so the same operation
has a different id on each machine and on the hub. Anything that names an operation, such as
jj op restore <id>, takes this machine’s id. To find the same operation on another machine,
run musubi debug op-id <id> here, which prints the hive’s id for it, then musubi debug op-id with that id on the other machine.
The hub needn’t be durable
Every machine has every operation it has seen, so a hub holds nothing that isn’t on the machines too. A hub that loses its state, through a power cut or a deleted directory, loses no work: start a new one, and make its hives again from the machines’ copies. See Replace or move a hub.