fleet

Config

The one config.jsonc that describes the Fleet.

The config lives at ~/.config/fleetfizz/config.jsonc, the same path on every Machine. The copy on the Machine you ship from is the one a Ship reads, and the Ship copies it (and schema.json, for editor validation) to every other Machine.

config.jsonc
{
  "$schema": "./schema.json",
  "machines": {
    "connors-mac-studio": { "clones": "/Volumes/StudioStack SSD/fleetfizz/clones" },
    "connors-macbook-pro": {},
  },
  "repos": [
    {
      "origin": "connorch/t3code",
      "ref": "main",
      "platforms": ["darwin"],
      "install": "pnpm install && pnpm build",
      "outputs": ["~/Applications/T3 Code.app"],
    },
  ],
  "vp": {
    "version": "1.1.0",
    "node": "24.18.1",
    "pnpm": "^10",
    "globals": {
      "vercel": "^59.25.4",
      "wrangler": { "range": "^4.136.3", "machines": ["connors-mac-studio"] },
    },
  },
  "brew": {
    "items": {
      "gh": "*",
      "oven-sh/bun/bun": "latest",
      "tmux": { "range": "*", "platforms": ["linux"] },
    },
  },
  "cask": { "items": { "ghostty": "latest" } },
}

machines

The Fleet, keyed by each Machine's tailnet name. A tailnet member that is not listed is not part of the Fleet.

FieldDescription
clonesWhere this Machine keeps its Managed Clones. ~ expands on that Machine. Default: ~/.local/share/fleetfizz/clones.

repos

Each Tracked Repo is cloned on every targeted Machine and installed from its origin ref.

FieldDescription
originThe GitHub owner/repo. Required.
refA branch, or a full 40-character commit SHA to pin it. Required.
installThe Install Command, run in a login shell inside the Managed Clone. Required.
nameNames the clone's directory and the repo in output. Default: <owner>-<repo>.
outputsPaths outside the clone the Install Command produces. If one changes by hand, the repo reinstalls.
filesGitignored files written into the clone before every install, keyed by path. Never secrets.

The Install Command runs with FLEETFIZZ_MACHINE, FLEETFIZZ_PLATFORM, and FLEETFIZZ_MACHINES (every Machine in the Fleet, comma-separated) set.

vp

The vite-plus toolchain.

FieldDescription
dependsOnProviders whose Items must succeed on a Machine before vp changes there.
versionThe vp version. Pin it exactly.
nodeThe default Node version. Pin it exactly.
npmThe default npm version.
pnpmThe default pnpm version.
yarnThe default Yarn version.
bunThe default Bun version.
globalsGlobal packages, each with a Range, or { range, platforms?, machines? } to target one.

A Ship installs the newest version in a Global's Range when it is missing or below the Range, and leaves it alone anywhere inside it. latest always installs the newest version, and * installs only when missing. A Global not in the config is never uninstalled.

A Global above its Range usually means someone updated it by hand, so the Ship asks before changing anything: bump the Range, skip it on that Machine, or downgrade into the Range.

Node and the package-manager defaults only take effect in vp's managed mode, where node, npm, pnpm, yarn, and bun run vp's version instead of a system install such as Homebrew's Node. A Machine with any tool in system-first mode gets a Question: switch it to managed mode (vp env on), or skip, which leaves the mode alone and skips that Machine's Node and package-manager defaults. Skip is the default, since switching changes which node every shell there runs.

Repos run last, after vp and the Homebrew packages, so an Install Command can use a Formula installed in the same Ship; when vp, Node, or a Global fails on a Machine, that Machine's repo changes are skipped ("depends on vp").

brew

Homebrew Formulae. Needs Homebrew on the Machine; a Ship never installs it.

FieldDescription
dependsOnProviders whose Items must succeed on a Machine before this one changes there.
itemsFormulae, each "*" or "latest", or { range, platforms?, machines? } to target one. A tapped name (owner/tap/name) taps on install.

cask

Homebrew Casks, macOS only. Needs Homebrew on the Machine.

FieldDescription
dependsOnProviders whose Items must succeed on a Machine before this one changes there.
itemsCasks, each "*" or "latest", or { range, platforms?, machines? } to target one. A tapped name (owner/tap/name) taps on install.

brew and cask need Homebrew on the Machine; a Ship never installs it, and brew:brew fails on a Machine without it. Homebrew installs only a package's newest version, so a Range there is only "*" (install when missing) or "latest" (run brew update, then upgrade on every Ship). A "latest" package always shows as planned in a dry run; whether there is something newer is known only after brew update, and the Ledger then shows the version it reached.

Targeting

repos entries, the vp, brew, and cask sections, and each Global or Homebrew package take two optional fields that narrow where they apply. They combine with AND, so a Global or package applies where both it and its section do, and an unset field means every one.

FieldDescription
platformsdarwin, linux, or both.
machinesMachine names from machines.