Ferry

Getting started

Ferry keeps the agent setup of your machine in sync with one or more Linux boxes where remote agents run. Your machine is the source of truth. It is the operator machine. Ferry publishes your setup to a private git repository, the snapshot. Each box clones the snapshot and links its harness directories to it.

your machine ── ferry sync ──> private snapshot repo ──> box a, box b, ...
                                                         (pull and link over SSH or Tailscale)

What you need

Install Ferry

On the operator machine:

curl -fsSL https://raw.githubusercontent.com/dlhck/ferry/main/install.sh | sh

Or with npm. Do not use --omit=optional, because the executable comes from an optional dependency:

npm i -g @dlhck/ferry

install.sh lists its environment variables, such as FERRY_VERSION, at the top of the file.

Set up the first box

ferry init --ssh-destination user@box.example \
  --snapshot-url git@github.com:you/ferry-snapshot.git
ferry install        # gh, jq, the agent CLIs, your tools, and Ferry on the box
ferry sync           # publish the snapshot and apply it on the box
ferry auth claude    # start a login on the box, finish it in a browser here
ferry watch install  # sync each change automatically, as a user service
ferry status         # check the link, the snapshot, the logins, and the tools
  1. ferry init records the box and the snapshot URL, seeds the snapshot, and links this machine to it. For a Tailscale box, use --host <tailscale host> --ssh-user <user> instead of --ssh-destination. Without flags, ferry init asks. Ferry asks before it trusts the Git host key on the box.
  2. ferry install prints the plan for each tool and asks before it runs a command on the box. The Ferry on the box is a box install. It runs only ferry expose and ferry whoami.
  3. ferry sync checks the carried files against the deny rules, publishes the snapshot, and applies it on the box. See What Ferry carries and the Security model.
  4. ferry auth <tool> starts a login for gh, claude, codex, or cursor on the box. You finish it in a browser on this machine. The token stays on the box. Pi has no remote login. Run pi on the box and use /login.
  5. ferry watch install runs ferry watch as a launchd or systemd user service. It syncs each change one second after the change is stable.
  6. ferry status shows the state of the snapshot and of each box. See Status and the menu bar.

Add --dry-run to init, sync, update, or move to see the plan first.

ferry auth gh also creates ~/.ssh/id_ed25519 on the box if it is missing, and adds it to your GitHub account, so agents on the box can push.

Next steps

Update Ferry

Run ferry self-update on the operator machine. It restarts installed watch and tunnel services that use the updated Ferry. On macOS, it also updates an installed release menu bar app. Then run ferry update to put the new version on the boxes. When ferry self-update says that the release is not ready for download, see Troubleshooting.

On a terminal, Ferry also asks to update when a newer release is there. It checks at most once a day, and never with --json, with CI set, or with FERRY_NO_UPDATE_CHECK=1.

Scripts and agents

Add --json to any command. Then stdout has only JSON, and Ferry asks nothing. ferry --help describes the output.

The Ferry agent skill tells agents how to work with Ferry on both machines, for example not to edit a Ferry-managed file on the box. ferry init writes it to ~/.agents/skills/ferry, and the snapshot carries it to the boxes. ferry self-update writes the skill of the new version. ferry init --no-skill does not install it.

Remove Ferry