Every machine, always current

Your jj work, on every machine, all at once.

Work on your laptop, close the lid, and sit down at your desktop: the change you were working on is already there. Coding agents on three machines see each other's work as it happens. No pushing work in progress, no fetching it back, no wondering which machine is behind.

For jj, the git-compatible version control system. All it needs is ssh to one machine that's usually on.

Watch it happen

Describe a change on the laptop. Before you could reach the desktop’s keyboard, it’s in jj log there, with no push and no fetch. In the middle, musubi top on the hub shows it going through.

laptop

laptop ~/src/app ❯ jj describe -m "Retry failed fetches"
Working copy  (@) now at: zvrozmvy 76deb1ed (empty) Retry failed fetches
Parent commit (@-)      : lpolomsm d91c0a5a main | Add a fetcher
laptop ~/src/app ❯ echo 'RETRIES = 3' >> fetcher.py
laptop ~/src/app ❯ jj diff
Modified regular file fetcher.py:
    ...
   3    3:
   4    4: def fetch(url):
   5    5:     return urllib.request.urlopen(url).read()
        6: RETRIES = 3
laptop ~/src/app ❯ jj log
○  tmzzmlto you 2026-10-27 15:29:11 desktop/default@ 32ec8de7
│  (empty) (no description set)
@  zvrozmvy you 2026-10-27 15:29:06 default@ 51a8f8be
│  Retry failed fetches
◆  lpolomsm you 2026-10-27 15:28:47 main d91c0a5a
│  Add a fetcher
~

musubi top on the hub

musubi top: 1 hub, every 1s
This machine's daemon is running, serving 0 repositories.
Hub /private/tmp/mjd.f6YvjM/hub/musubi-hub: up 32s, musubi 0.1.0, 1 hive
HIVE      HEAD   IN/s       OUT/s      OPS     COMMITS  MACHINES  ISSUES
app       #7     0 B/s      0 B/s      8       4        2/2

app since the hub started: received 9.1 KiB and sent 10 KiB; 8
operations, writing 4 commits, came in, and 10 went out; 7 pushes moved
MACHINE          HEARD     AT      MUSUBI   STATE
desktop          2s ago    #7      0.1.0    up to date
laptop           2s ago    #7      0.1.0    up to date

AGO       HEAD    FROM             OPS   COMMITS  WHAT
8s ago    #7      desktop          1     1        new empty commit
13s ago   #6      laptop           1     1        snapshot working copy
21s ago   #5      laptop           1     1        describe commit 9c4d26
31s ago   #4      desktop          1     1        new empty commit
q quit   ↑↓ choose a hive   r refresh   + slower   - faster

desktop

@  wqotzwym you 2026-10-27 15:28:48 default@ 729409a5
│  (empty) (no description set)
│ ○  zvrozmvy you 2026-10-27 15:29:06 laptop/default@ 51a8f8be
├─╯  Retry failed fetches
◆  lpolomsm you 2026-10-27 15:28:47 main d91c0a5a
│  Add a fetcher
~
^C
desktop ~/src/app ❯ jj new laptop/default@
Working copy  (@) now at: tmzzmlto 32ec8de7 (empty) (no description set)
Parent commit (@-)      : zvrozmvy 51a8f8be Retry failed fetches
Added 0 files, modified 1 files, removed 0 files
The laptop describes a change and edits a file; musubi top on the hub shows each operation arrive and go out; the desktop's jj log, run again every half second, shows the laptop's working copy, laptop/default@, change a moment later. Then the desktop starts a change on top of the laptop's, and the laptop sees it. Recorded from real jj and musubi.

What Meta and Google engineers already have

Engineers at Meta never push work in progress just to move it between machines: Sapling’s Commit Cloud uploads every commit as soon as it’s made. Engineers at Google have something like it in an internal jj server that keeps their repositories’ history in the cloud. jj-musubi brings that to your own repositories, on your own machines, over plain ssh, with no service to sign up for and nothing leaving machines you control.

What you get

Agents everywhere, all at once

Run Claude Code or Codex on your laptop, your desktop and a couple of devboxes, all in one repository. One jj log, on any machine, shows what every agent everywhere is working on, as devbox/default@: the devbox's working copy. Review it from anywhere, or hand it to an agent on another machine with jj new devbox/default@.

Every machine current

Laptop, desktop, home server: each has every commit, bookmark and change you've made on any of them, within a second or so of the jj command that made it. A machine that was asleep catches up within seconds of waking, so you never have to remember which one is behind.

Close the lid, pick up anywhere

Shut the laptop at work and go home. The desktop already has everything up to your last jj command, as laptop/default@. Open the laptop again, and it catches up with the evening's work within seconds of waking. Offline on a plane? It catches up when you land.

GitHub, everywhere at once

jj git fetch on one machine, and every machine has the new main@origin. jj git push from the desktop, and the laptop shows it pushed. Fetch once, not once per machine.

Undo that follows you

jj records every command you run, so that jj undo can take any of them back. jj-musubi shares that record, so a mistake made on one machine can be put right from any other.

Your files stay yours

jj-musubi never writes to your working copy. When another machine rewrites the commit you're on, jj says so, and jj workspace update-stale brings your files up to date when you're ready.

Start with three commands

  1. Start a hub

    On a machine that is usually on, such as a devbox or a home server:

    musubi hub
  2. Share a repository

    In the repository on one machine:

    musubi create --hub devbox
  3. Join the other machines

    In the same repository on each other machine. On a machine that doesn't have it yet, musubi clone makes it.

    musubi join --hub devbox

That’s all: from then on, keep using jj. You need jj 0.46.0 and git on every machine, and ssh from each to the hub’s machine. Get started covers installing and each step.

A hub, and a daemon on each machine

One machine that is usually on runs a hub, and a daemon on each machine keeps its repositories in sync with it. Other machines reach the hub over ssh: if ssh devbox works, so does jj-musubi. The hub decides the order in which machines’ work lands, and holds nothing the machines don’t have too: a hub that’s lost loses no work.

flowchart LR
  subgraph laptop["laptop"]
    R1["~/src/app"] --- D1["daemon"]
  end
  subgraph devbox["devbox"]
    H["musubi hub"]
  end
  subgraph desktop["desktop"]
    D2["daemon"] --- R2["~/code/app"]
  end
  D1 <-->|ssh| H
  H <-->|ssh| D2
The hub's machine can have copies of its own too, whose daemon reaches the hub through a local socket.

Built to be trusted

A sync that drops a commit, or applies the same rebase twice, is worse than none. So jj-musubi was built proof first.

Proved in Lean

The sync protocol is modelled in the Lean theorem prover, with machine-checked proofs that no work is lost, no merge is done twice, and every machine ends up the same, over a network that loses and reorders messages, with machines and the hub crashing at any point, even when a power cut sets the hub back.

Checked against the proof

In testing, the Rust protocol code runs beside an executable version of the Lean model, which checks every step it takes. Deliberately broken versions of the code get caught.

Every race, with a test

More than 70 races between machines, from the same commit rewritten on two machines to one forgetting another's workspace, each with its outcome pinned by a test on real repositories.

Searched through real jj

Every night, a generated search runs real jj commands on real repositories through crashes, lost replies and power cuts, and checks the result against what jj itself would do.

How jj-musubi keeps your work safe tells the whole story.

Why not file syncing, or a git remote?

Syncing a repository with Dropbox or Syncthing shares the working copy too, so machines fight over one checkout, and jj’s own documentation warns that repository corruption is possible. Pushing work in progress to a git remote carries only the commits you put a bookmark on, takes a push on one machine and a fetch on the other every time, and leaves jj undo behind on each machine. How jj-musubi compares goes through each approach, and when it suits.

Next steps