Ferry

Paseo sync

With the Paseo integration enabled for a box, ferry sync carries agent profiles, managed Git and npm plugins, provider definitions, metadata model preferences, shared system instructions, portable terminal profiles, and, with an explicit setting, the auto-archive switch from the operator machine. These go directly to the box. They are not stored in the snapshot repository. ferry watch detects changes to plugin commits, npm versions, enabled states, provider definitions, the preferences, and terminal profiles.

The box config stays on the box

The box ~/.paseo/config.json can hold credentials, such as the env block of a provider or of a terminal profile. Ferry never reads this file into the operator machine.

The service unit stays on the box

You can add an Environment= line with a credential to ~/.config/systemd/user/ferry-paseo.service on the box. Ferry never reads this file into the operator machine.

When an agent process runs out of memory

Each agent of the Paseo daemon runs in the cgroup of ferry-paseo.service. The unit that Ferry writes has OOMPolicy=continue and Restart=on-failure.

A box that you enabled before Ferry wrote this line gets it from the next ferry sync, or from ferry integrations enable paseo. The two commands run systemctl --user daemon-reload and do not restart the daemon for this line. systemd reads the policy of a service at the time of the OOM event, so the reload applies the line to the running daemon. This was tested with systemd 259: a running service with the default policy got the line and a reload, kept its main process, and stayed active after an OOM kill in its cgroup. systemctl --user show ferry-paseo.service -p OOMPolicy shows the value in use.

A sync adds the line only to a unit that has no OOMPolicy line, so a value that you set in the unit stays until the next enable. A value in a drop-in file has priority over the unit, and Ferry does not change a drop-in file. To keep the systemd default, put OOMPolicy=stop in a drop-in file, then run systemctl --user daemon-reload on the box.

Paseo output stays on the box

The output of a paseo or npm command on the box can hold box content, so Ferry does not read it when it needs only the result.

Git and npm plugins

Ferry reads ~/.paseo/config.json and ~/.paseo/plugins/sources.json. For a Git plugin, it also reads the managed checkout’s Git HEAD. It carries the plugin ID, repository URL, plugin subdirectory, installed commit, and enabled state. It uses paseo plugin install --ref for a missing plugin and paseo plugin update --ref for an existing plugin. An unchanged plugin needs no install or update.

For an npm plugin, Ferry reads the installed version from the plugin’s package-lock.json and checks it against the installed package.json. It carries the plugin ID, package name, plugin subdirectory, exact installed version, and enabled state. It never carries the requested tag or range. It uses paseo plugin install npm:<package>@<version> for a missing plugin and paseo plugin update --version <version> for an existing plugin. Ferry moves the box to the operator’s version, also when the box has a later version. Scoped packages, such as @acme/review, are supported.

Use ferry sync --dry-run to review IDs, commits, npm versions, enabled states, and local skip reasons without connecting to a box. Source conflicts require a box connection and are reported during sync.

Provider definitions

Ferry reads agents.providers from ~/.paseo/config.json and merges an allowlist of fields into agents.providers of the box ~/.paseo/config.json. Then it runs paseo daemon reload. Paseo 0.10.1 applies agents.providers on reload without a restart.

Field Carried
Provider ID, extends, label, description Yes
models, additionalModels Yes. The local list replaces the box list and keeps its order. Ferry copies only the model keys that Paseo knows: id, label, description, isDefault, and thinkingOptions.
disallowedTools, paseoTools Yes. paseoTools merges by key.
env, params No. They can hold credentials, endpoints, and host paths.
command No for a provider that the box defines. See the create rules below.
enabled, order No. Each host keeps its own provider state and menu order.

Preferences

Ferry reads these fields from ~/.paseo/config.json and writes them into the box ~/.paseo/config.json. Then it runs paseo daemon reload. Paseo 0.10.1 applies all three fields on reload without a restart, although the metadata generation page still says to restart after a direct edit.

Field Contents
agents.metadataGeneration.providers The ordered list of providers that Paseo tries first for workspace titles, worktree branch names, commit messages, and pull request text. Each entry has a provider, an optional model, and an optional thinkingOptionId.
daemon.appendSystemPrompt Shared instructions that Paseo adds to the system prompt of each agent on the box.
daemon.autoArchiveAfterMerge When true, Paseo archives a workspace after its pull request merges. Ferry carries it only to a box with paseo_auto_archive = true. See Auto-archive after merge.

Project scripts, setup, and metadata instructions stay in each project’s paseo.json, which travels with the project in Git.

Auto-archive after merge

daemon.autoArchiveAfterMerge changes the workspace lifecycle on the box, so Ferry carries it only when you turn it on in ~/.ferry/config.toml:

[integrations]
paseo = true
paseo_auto_archive = true

The setting is off by default. Set paseo_auto_archive in [box.<name>.integrations] to override it for one box.

Terminal profiles

Ferry reads daemon.terminalProfiles from ~/.paseo/config.json and merges the portable profiles by id into daemon.terminalProfiles of the box ~/.paseo/config.json. Then it runs paseo daemon reload. Paseo 0.10.1 applies the list on reload without a restart. Paseo starts a profile’s command directly, without a shell, with the PATH of ferry-paseo.service.

Field Carried
id, name, command, args, icon Yes. args can hold the prompt placeholder {{{prompt}}}.
env and all other fields No. Ferry skips a local profile that has one of them.

Moved sessions

After ferry move carries a project to a box with Paseo, Ferry runs paseo project create for the project directory. Then it runs paseo import <session-id> --provider <claude|codex> --cwd <project> for each carried session.

Other sync candidates

Research checked on 2026-09-29 against Paseo 0.10.1 and current upstream documentation. The entries below are proposals, not implemented sync behavior.

Candidate Recommendation Required handling
Workspace label names and colors Next candidate Merge by Paseo’s normalized, case-insensitive label name. Preserve box-only labels and workspace assignments. Local color wins for a matching name. Treat a rename as a new definition unless explicit rename history is available.
Project scripts, setup, and metadata instructions Use project Git These already live in paseo.json. Carry them with the project rather than maintaining a second copy in host sync.
Schedules Explicit migration only Map project paths, verify providers, and select one execution host. Copying an active schedule can run a task twice.
Plugin settings Defer Each plugin owns its schema and may store credentials or host paths. Need a portable-field contract first.

Do not sync daemon identity, pairing or auth data, network listeners, relay endpoints, browser sessions, running agents, heartbeats, worktree paths, or workspace state as general preferences.

Label API gap

Paseo stores label definitions as names and colors, separate from workspace assignments. Its label service currently creates a definition only when assigning a label to a workspace. The update operation rejects an unknown label. The CLI has no label management command.

The daemon caches the catalog and commits label changes with workspace transactions. Copying the catalog file while it runs would bypass that state and could lose changes. A safe implementation needs a standalone label upsert API and CLI command that accepts a name and color without a workspace ID. Ferry can then merge definitions without touching assignments or restarting the daemon.

Sources