This guide sets up musubi from nothing. One machine runs a hub, which every other machine reaches over ssh. Each repository you share becomes a hive on the hub, and a daemon on each machine keeps that machine’s copies in sync with it. How musubi works says more, and the README has the short version of this guide.

Before you start

You need, on every machine:

  • musubi. See Install.
  • jj 0.46.0, and git. musubi runs the git that jj’s git.executable-path names; the hub runs the git on its PATH.
  • ssh access to the hub’s machine: if ssh devbox works, so does musubi. Your ~/.ssh/config applies, with its aliases, ProxyCommand and ProxyJump.

You also need a repository with at least one commit and all of its history. musubi refuses a shallow or partial clone, since it couldn’t send other machines everything they need. A jj git clone without --depth is fine.

Choose the hub’s machine

Run the hub on a machine that is usually on and that every other machine can reach over ssh: a devbox, a home server, or a desktop that never sleeps. A laptop is a poor choice, since the others can’t sync while it sleeps. The hub’s machine can have copies of the repositories too: a devbox you work on is a fine hub.

Start the hub

Other machines run musubi on the hub’s machine over ssh, so it must be on the PATH of non-interactive shells there. Check from another machine:

ssh devbox musubi --version

If that says musubi: command not found, put its directory on the PATH in ~/.zshenv, or near the top of ~/.bashrc, before any line that returns early for non-interactive shells.

Then, on the hub’s machine:

musubi hub

The first time, it creates ~/musubi-hub and prints what to run elsewhere:

Created a new hub in /home/you/musubi-hub
To share a repository:               musubi create --hub devbox
To add another machine's copy of it: musubi join --hub devbox
To make a new copy of a hive:        musubi clone <name> <dir> --hub devbox
Other machines reach this hub over ssh: use the name they ssh to this machine by, if it isn't devbox.
On this machine, leave out --hub.

The --hub it suggests is this machine’s host name. If other machines reach it by another name, such as an alias in their ~/.ssh/config, use that instead; musubi hub --advertise <name> makes the hub print that name from then on. On the hub’s own machine, the name it prints, or localhost, reaches the hub musubi hub serves without --dir through its directory, without ssh.

To serve the same hives later, run musubi hub again, with the same --dir if you gave one. Machines reconnect by themselves.

--dir keeps the hub’s directory somewhere else. Leave it out unless you run more than one hub on the machine: other machines’ ssh connections reach only the hub last started without it.

Keep the hub running

musubi hub runs in the foreground until you stop it. To run the hub at login from now on, and now, stop it and run instead:

musubi hub install

This installs a launchd agent on macOS, or a systemd user unit on Linux, which runs musubi hub in the hub’s directory, and again if it fails. It serves the directory musubi hub would, taking --dir and --advertise as musubi hub does, and prints what to run elsewhere. On Linux, the hub stops when you log out unless you also run loginctl enable-linger; a server you reach only over ssh needs it. Windows has no hub install.

The service runs the musubi that hub install found, with the PATH you ran it with. If you move musubi, or the git it runs, or want the hub to serve another directory, run musubi hub install again. musubi hub uninstall stops the hub, and stops it running at login.

The hub logs to hub.log in musubi’s state directory on macOS, and to the journal on Linux: journalctl --user -u musubi-hub.

Share a repository

In the repository on one machine, start a hive from it (on the hub’s machine, leave out --hub):

musubi create --hub devbox

The hive is named after the repository’s origin remote (app for github.com/you/app), or its directory if it has no remotes. To choose another name, pass it: musubi create work-app.

create starts the daemon if it isn’t running, and returns once the repository has synced:

Created an ssh key for musubi: /home/you/.local/state/musubi/hub_key
Adding it to ~/.ssh/authorized_keys on devbox, allowed only to run `musubi hub connect`. ssh may ask for your password.
Created hive app
Syncing...
Synced with the hub (hive at #1).

The first hub a machine uses becomes its default, so later commands need no --hub. The ssh key is explained below.

Add other machines

On each other machine, in its copy of the repository:

musubi join --hub devbox

You don’t need to name the hive: the hub picks the one that has some of the copy’s history and one of its remotes’ URLs.

Joined hive app (matched remote origin), 2 machines
Syncing...
Synced with the hub (hive at #3).

The copies’ histories are merged as jj merges concurrent operations: commits both copies have are sent only once, and a bookmark that points to different commits in the two copies is conflicted until you move it.

If the copy matches no hive, or more than one, join refuses and says why. See Joining is refused.

For a machine with no copy yet, clone the hive instead. This makes a new colocated repository, joins it to the hive, syncs it, and checks out the hive’s trunk, as jj git clone checks out a remote’s default branch:

musubi clone app ~/src/app --hub devbox

The ssh key musubi adds

The first time a machine uses a hub over ssh, musubi creates an ssh key for itself, hub_key in its state directory (~/.local/state/musubi on Linux and macOS), and adds it to ~/.ssh/authorized_keys on the hub’s machine:

restrict,command="musubi hub connect" ssh-ed25519 AAAA... musubi@laptop

command= means the key can reach the hub and nothing else on that machine. musubi adds it through your own ssh, so ssh may ask for your password that once. After that, the daemon uses this key and never prompts. It needs an ssh agent only for a ProxyJump host, or to sign commits with a key the agent holds; it uses the agent of the latest session that ran a musubi command.

Anyone who can reach a hub can read and write every hive on it, so other machines reach it only over ssh, with keys that can do nothing else (SECURITY.md).

If musubi isn’t on the PATH of non-interactive shells on the hub’s machine, you can give its absolute path in that line instead: command="/opt/musubi/bin/musubi hub connect".

Start the daemon at login

create, join and clone start the daemon, but it doesn’t survive a reboot. To start it at login from now on:

musubi daemon install

This installs a launchd agent on macOS, a systemd user unit on Linux, or an entry under the Run registry key on Windows, and starts the daemon. On Linux, the daemon stops when you log out unless you also run loginctl enable-linger; a server you reach only over ssh needs it.

On macOS and Linux, the service runs the musubi that daemon install found, with the PATH you ran it with. If you move musubi, or the git it runs, run musubi daemon install again.

Name machines

Other machines show each machine’s working copy as a workspace named after it, such as laptop/default@. The name defaults to jj’s operation.hostname, usually the host name. To choose another, pass --machine to create, join or clone:

musubi join --machine work-laptop

Two machines may share a name: musubi tells them apart by an id of its own, and shows them as laptop-1a2b/default@ and laptop-3c4d/default@ from then on, even once one has left.

Bring the operation history

By default, a machine that creates or joins a hive shares its repository’s current state, not its past operations, so other machines’ jj op log starts from the join. This keeps old operations out of the hive: a repository used for years can have tens of thousands, whose views keep alive commits nothing else needs, such as old working-copy snapshots.

To share the whole operation history instead, pass --full-history to create or join.

More repositories, forks, and hubs

One hub serves any number of repositories, each its own hive, and one daemon per machine serves them all. Run musubi create in each repository you want to share, and musubi join in its other copies.

A fork shares history with the original but has remotes of its own, so musubi join won’t put it in the original’s hive. Run musubi create in it to give it a hive of its own, or musubi join --hive <name> to join a particular hive whatever its remotes.

A machine can use more than one hub. Pass --hub to create, join or clone to use one other than the default. Each repository remembers its own hub.