{"slug": "show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes", "title": "Show HN: Ferry – Sync your coding agent setup to remote Linux boxes", "summary": "Developer dlhck released Ferry, an open-source tool that syncs coding agent setups — skills, instructions, MCP servers and supported settings — from a local machine to remote Linux boxes via a private git snapshot repository. Ferry installs and updates the claude, codex, pi and cursor-agent CLIs on each box, starts logins there without copying tokens, and offers ferry watch, ferry tunnel --follow, ferry move and ferry auth commands, installable via curl or npm i -g @dlhck/ferry on macOS or Linux, arm64 or x64.", "body_md": "# Your agent setup.\n\n *Wherever you run.*\n\nRun agents on your laptop or a remote Linux box with the skills, instructions, MCP servers and supported settings you already use. Ferry keeps them in sync as you work, so every box gets your setup without you rebuilding it by hand.\n\n**Change a skill locally. Your remote agents get it too.**\n\n`curl -fsSL https://raw.githubusercontent.com/dlhck/ferry/main/install.sh | sh`\n`npm i -g @dlhck/ferry`\nmacOS or Linux, arm64 or x64. With npm, do not pass `--omit=optional`. Update later with `ferry self-update`.\n\n## Stop setting up the same workflow on every machine.\n\nYou've taught your agents how you work. Moving to a remote box should carry that work with you.\n\n- \n### Configure once. Keep every box in sync.Update your skills and instructions on your machine. Ferry's watcher sends those changes to your boxes automatically. [`ferry watch install`](#cmd-watch)\n- \n### Open remote previews in your local browser.Forward remote dev server ports to localhost, with automatic forwarding for servers announced through Ferry. [`ferry tunnel --follow`](#cmd-tunnel)\n- \n### Continue your project on another machine.Move a Git project to a box and back, with its untracked files and its Claude and Codex sessions, so you can resume the conversation there. [`ferry move`](#cmd-move)\n- \n### Logins stay where they are.Ferry starts logins on the box and never copies a token. [`ferry auth`](#cmd-auth)\n\n## Your machine publishes. Each box pulls.\n\nYour machine is the source of truth. The boxes follow it through a private git repository that Ferry calls the snapshot.\n\n1. \n### Your machineYour skills, `~/AGENTS.md` , Claude subagents, commands and hook scripts, some Claude, Codex, Pi and Cursor Agent settings, and remote and stdio MCP servers.\n2. \n### Private snapshot repoAn empty private git repository you create. Ferry publishes the carried files to it.\n3. \n### box a, box b, ...Each box clones the snapshot and links its harness directories to it.\n\n### It also sets up the box\n\nFerry installs and updates the agent CLIs on each box (`claude`, `codex`, `pi`, `cursor-agent`), plus `gh`, `jq` and the tools you list. It starts logins there. It never copies one.\n\n### See the plan first\n\n`--dry-run` on `init`, `sync`, `update`, `move`, `revert` and `box remove` shows what Ferry will do. `--json` on any command gives output for scripts and agents.\n\n## Four agents, ready on every box\n\nFor each agent, Ferry installs and updates the CLI on the box and starts its login there. Codex, Pi and Cursor Agent read the shared skills in `~/.agents/skills`, and Claude Code reads its own in `~/.claude/skills`. The instruction files of Claude Code, Codex and Pi link to your `~/AGENTS.md`. On a box, a header that names the box and your instructions for that box come first.\n\n- \n### Claude Code`claude`\n  - Carries\n  - Skills in `~/.claude/skills` ,`~/.claude/CLAUDE.md` , subagents, custom commands, hook scripts in`~/.claude/hooks` , and the remote and stdio MCP servers in`~/.claude.json` .\n  - Settings\n  - `enabledPlugins` ,`extraKnownMarketplaces` ,`permissions` ,`hooks` ,`attribution` ,`includeCoAuthoredBy` ,`model` ,`alwaysThinkingEnabled` . The box installs the plugins.`env` and`apiKeyHelper` stay on your machine.\n  - Login\n  - `ferry auth claude` , finished in a browser here. MCP logins with`ferry auth claude --mcp <server>` .\n- \n### Codex`codex`\n  - Carries\n  - `~/.codex/AGENTS.md` and the remote and stdio MCP servers in`mcp_servers` . Skills come from`~/.agents/skills` .\n  - Settings\n  - `model` ,`model_reasoning_effort` ,`model_reasoning_summary` ,`model_verbosity` ,`features` ,`web_search` . Providers, profiles,`notify` and project trust stay on your machine.\n  - Login\n  - `ferry auth codex` with a device code.\n- \n### Pi`pi`\n  - Carries\n  - `~/.pi/agent/AGENTS.md` . Skills come from`~/.agents/skills` .\n  - Settings\n  - `defaultProvider` ,`defaultModel` ,`defaultThinkingLevel` ,`enabledModels` ,`thinkingBudgets` ,`enableSkillCommands` in`~/.pi/agent/settings.json` . Packages, resource paths, the shell path, and the npm and shell commands stay on your machine.\n  - Install\n  - Ferry installs and updates Pi. It installs Node and npm first if the box has none, or a Node that is too old.\n  - Login\n  - By hand. Ferry cannot drive the Pi login. SSH to the box, run `pi` , then use`/login` .\n- \n### Cursor Agent`cursor-agent`\n  - Carries\n  - Remote and stdio MCP servers, merged into `~/.cursor/mcp.json` . Skills come from`~/.agents/skills` .\n  - Settings\n  - `model` ,`maxMode` ,`hasChangedDefaultModel` ,`attribution` in`~/.cursor/cli-config.json` . Permissions, the status line and the login state stay on your machine.\n  - Login\n  - `ferry auth cursor` .\n\n### GitHub CLI and jq\n\nFerry installs `gh` and `jq` on every box. `ferry auth gh` logs `gh` in on the box and sets up the box SSH key for GitHub. The box uses `jq` to merge the carried settings keys and MCP servers into its own files. It sends back only a status, never the file.\n\n### Your own tools\n\nAdd any other tool in a `[tools.<id>]` table with a version policy: `\"operator\"`, `\"latest\"` or an exact version. With `auth_status`, `auth_login` and `auth_hosts`, `ferry auth <id>` logs it in.\n\n### Integrations\n\n### Paseo\n\n`ferry integrations enable paseo`\nRuns the Paseo daemon on a box. Ferry carries your agent profiles, managed Git plugins at their installed commit, npm plugins at their installed version, the portable fields of provider definitions, metadata model preferences, shared system instructions, and portable terminal profiles. These go to the box directly, not through the snapshot.\n\nWith `paseo_auto_archive = true`, Ferry also carries the auto-archive-after-merge switch. When `ferry move` puts a project on a box with Paseo, Ferry registers the project and imports each carried session as a Paseo agent. A move back does the same in the Paseo on your machine.\n\nPlugin settings, provider env blocks and commands, terminal env blocks and paths, and credentials stay on each host. When one agent process runs out of memory, the daemon and the other agents keep running. Set `paseo_relay = true` for relay pairing.\n\n### Sherlock\n\n`ferry integrations enable sherlock`\nWith [Sherlock](https://github.com/michaelbromley/sherlock) on your machine, Ferry adds `ferry sherlock add <name> --box <box> --target <host:port> --type <type>`. It registers a Sherlock connection that tunnels through the box on the first query, so Sherlock can query a database on the box, or one that only the box can reach.\n\nSherlock keeps the password in the keychain of your machine. Ferry never stores it and never edits the Sherlock config. `ferry status` checks that each box can reach its target.\n\n`ferry integrations` lists the part of each integration: `box` for a service on the box, `operator` for commands and checks on your machine. More agents and integrations are planned. [Request one on GitHub](https://github.com/dlhck/ferry/issues).\n\n### Works with\n\n- **Tailscale or plain OpenSSH** The link to each box.\n- **Any private git host** Holds the snapshot. An SSH URL needs an SSH agent with a key that can push.\n- **`npx skills add`** Through` ferry skills add` , as a global copy that Ferry carries.\n- **launchd and systemd** User services for watch and tunnel, on macOS and Linux.\n- **The macOS menu bar and waybar** The Ferry menu bar app on macOS, and a[waybar module](https://github.com/dlhck/ferry/blob/main/docs/linux-status-bar.md) on Linux.\n\n## After setup, you keep working here\n\nThe commands you use after setup, with the details.\n\n1. ferry watch installSyncs each change after one second. It runs as a launchd or systemd user service.\n2. ferry status --briefShows only what needs action: offline boxes, low disk or memory on a box, logins, MCP logins, stdio MCP servers that lack something on the box, tool drift, and hooks that run a home file Ferry does not carry. Plain `ferry status` checks the link, the snapshot, the logins and the tools.\n3. ferry doctorChecks the SSH agent, push access to the snapshot, SSH and Tailscale to each box, the box deploy key, linger and the installed services. It changes nothing and prints a fix for each failed check.\n4. ferry revert <commit>Undoes one snapshot commit on your machine, including the carried settings keys, then syncs all boxes. `ferry history` lists the last 20 commits and the paths each one changed.`--no-sync` skips the sync.\n5. ferry box add <name>Adds another box. `sync` ,`status` and`update` act on all boxes, or on one with`--box` .\n6. ~/.ferry/boxes/<name>/AGENTS.mdInstructions for one box, for example \"this box runs the staging database\". On a box, each instruction file is a Ferry header that names the box, then this file, then your `~/AGENTS.md` . Agents run`ferry whoami` to check where they are. The file never goes into the snapshot.`ferry box add` creates it empty. For your first box, create it by hand.\n7. ferry box remove <name> --uninstallRemoves Ferry from a box, then the box from your config. Ferry prints the plan and asks you to type the box name. Its services, links, checkout and binary go. Logins, SSH keys, projects and installed tools stay on the box.\n8. ferry tunnel 3000Opens a box port on 127.0.0.1 on your machine. `ferry tunnel db.example:5432:15432` opens port 5432 of a host that the box can reach, such as a database that only accepts connections from the box network, on local port 15432. Ferry checks first that the box can connect to it. Run`ferry expose` on the box and`ferry tunnel --follow` here, and each dev server port opens on its own.\n9. ferry move <path> --to-box <name>Carries a git project to a box, untracked files included, with the Claude and Codex sessions of the project and the Claude project memory, so `claude --resume` and`codex resume` find them there.`--from-box` brings the project back. A session that fails the deny rules stays on the source. The scan cannot find a password that you typed in free prose, so use`--no-sessions` when a session can hold one.\n10. ferry adopt --from-box <name> <skill>Copies a skill that an agent wrote on a box to your machine. The Ferry on the box runs the deny rules first, and you see the diff or the file list before Ferry asks. Then `ferry sync` publishes it to every box.`ferry status` lists the box-only skills.\n11. ferry skills addInstalls skills with `npx skills add` as a global copy, so Ferry carries them.\n12. ~/.agents/skills/ferryThe Ferry skill. It tells your 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 here and the snapshot carries it to the boxes.`ferry self-update` refreshes it.`ferry init --no-skill` leaves it out.\n13. [tools.<id>]Each tool in the config has a version policy: `\"operator\"` ,`\"latest\"` or an exact version. The menu bar and`ferry status` report drift.\n\n## What boards, and what stays ashore\n\nFerry carries configuration. It does not carry anything that proves who you are.\n\n### Boards the ferry\n\n- Skills\n- Agent instructions in `~/AGENTS.md` , and your instructions for one box\n- Subagents\n- Custom commands\n- Hook scripts\n- Agent CLI settings, without the keys that can hold secrets\n- Remote and stdio MCP servers, without env values\n- ADE setup, such as agent profiles, plugins and providers, through an integration\n\n### Stays ashore\n\n- Logins\n- Credential files\n- Tokens and API keys\n- `.env` files\n- Env values of MCP servers\n- A credential in the origin URL of a project\n- Whole settings files\n\nThese never leave the machine that has them.\n\n### Checked before each publish\n\nDeny rules catch secret files, private keys, tokens, secret config keys and executable binaries. Scripts, such as hook scripts, pass and keep their executable bit. A match stops the sync. The error names the file, never the value.\n\n### Stdio MCP servers carry no values\n\nFerry carries the command, the arguments and the names of the env keys. You set the values on the box, and Ferry keeps them. A token, a secret flag with a value, or a URL with a credential in the command or the arguments stops the sync. Ferry skips a server that runs an inline script, such as `sh -c` or `node -e`, because it cannot check the script, and a server that refers to a path in your home. It never installs a command.\n\n### What leaves a box is checked on that box\n\nFor `ferry move --from-box` and `ferry adopt --from-box`, the Ferry on the box runs the deny rules. It reads each file once and sends exactly the bytes that pass. A refused file sends no content and no hash, and a name that looks like a token arrives as `[token]`. Your machine then applies its own rules to the bytes that arrive.\n\n### Boxes do not trust each other\n\nFerry connects to each box from your machine. `ferry move` from one box to another goes through your machine too, and the same deny rules apply.\n\n### No new doors\n\nFerry opens no public port and never turns off SSH host-key checks. It forwards your SSH agent only for snapshot checks, updates and Claude plugin installs. A box with `git_auth = \"box\"` gets no agent and reads the snapshot with its own read-only deploy key.\n\n### The rule needs an honest box\n\nNo credential leaves the machine that has it. That holds for a box that runs an unchanged Ferry. The check on the box protects against mistakes and against files that change. It does not protect against a box account that an attacker controls, because that Ferry can give any answer and any bytes. Ferry runs commands as your SSH user, so use a box and a user that you trust with the agents that run there.\n\n`ferry auth claude`\nStarts a login on the box. You finish it in a browser on your machine. The token stays on the box.\n\n## Six commands to a synced box\n\nRun them on your machine. Ferry reaches the box over SSH or Tailscale.\n\n### You need\n\n- **A Linux box you can reach with SSH** With`curl` or`wget` . Debian and Ubuntu are tested.\n- **A way to reach it** Tailscale on both machines, or an OpenSSH destination like`user@box.example` .\n- **An empty private git repository** This becomes the snapshot. For an SSH URL, you also need an SSH agent with a key that can push to it.\n\n```\nferry init --ssh-destination user@box.example \\\n  --snapshot-url git@github.com:you/ferry-snapshot.git\nferry install        # gh, jq, the agent CLIs, your tools, and Ferry on the box\nferry sync           # publish the snapshot and apply it on the box\nferry auth claude    # start a login on the box, finish it in a browser here\nferry watch install  # sync each change automatically, as a user service\nferry status         # check the link, the snapshot, the logins, and the tools\n```\n\nAdd `--dry-run` to `init` or `sync` to see the plan before anything changes.", "url": "https://wpnews.pro/news/show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes", "canonical_source": "https://dlhck.github.io/ferry/", "published_at": "2026-10-01 19:23:31+00:00", "updated_at": "2026-10-01 21:33:22.945776+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "agent-protocols"], "entities": ["Ferry", "dlhck", "Claude Code", "Codex", "Pi", "Cursor Agent", "GitHub", "npm"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes", "markdown": "https://wpnews.pro/news/show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes.md", "text": "https://wpnews.pro/news/show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes.txt", "jsonld": "https://wpnews.pro/news/show-hn-ferry-sync-your-coding-agent-setup-to-remote-linux-boxes.jsonld"}}