LogoPear Docs
How ToRelease & distribute your appManual deployment

Deploy your application

Operator how-to for the eight Foundational Steps of the desktop release flow: touch, upgrade link, version, make, pear build, stage, provision, and multisig.

This is the operator reference for the desktop release flow. The conceptual picture lives in Release pipeline; the type-along path is in Ship your app and Deploy over-the-air updates. Use this page when you need exact commands for the eight Foundational Steps documented in hello-pear-electron's Foundational Steps.

For the simplest publish/update workflow, let CI stage for you. Publish with GitHub Actions turns shipping a new version into a git push. This page is the full-control path when you need release lines, provision, and multisig.

Each step is independent — you can stop at any point if your project is still in proof-of-concept. The first three (touch, upgrade link, version) are setup; the next four (make, pear build, stage, provision) are the release cycle you repeat for every shipment; the final step (multisig) gates production.

0. Touch and seed

Mint a new pear:// link backed by a fresh Hypercore:

pear touch
# pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o

Announce the link on the swarm:

pear seed pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o

Run pear seed on at least one always-online machine so peers can fetch updates while developer laptops are asleep. On every other machine that should reseed, run the same command — additional seeders raise availability and shorten the first-connection latency.

Every shipped build polls a pear:// link from its package.json upgrade field. Set it once per release line:

npm pkg set upgrade=pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o

Or edit package.json directly:

{
  "version": "1.0.0",
  "upgrade": "pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o"
}

For production, set upgrade to the multisig link (see Set up multisig). For internal preview, set it to the provision link (step 6). For developer-team builds, set it to a stage link (step 5). The link decides which release line a binary follows.

2. Version

pear-runtime only swaps the application drive if the new build advertises a higher version:

npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease]

npm version rewrites package.json and creates a vX.Y.Z git tag. If this is the first release, leave version at 1.0.0 and skip to step 3.

3. Make distributables

Run npm run make on each OS you ship (macOS, Linux, Windows, or CI workers per platform):

npm run make   # .app + .dmg on macOS; .AppImage on Linux; .msix on Windows

The full coverage — entitlements, code signing, notarization, MSIX publisher matching, signing certificates — is in Build desktop distributables.

The package.json checklist before a make:

  • author populated
  • license populated
  • description populated
  • name set per brand
  • productName set per brand
  • build/icon.icns (macOS), build/icon.ico (Windows), build/icon.png (Linux) are per brand

4. Build the deployment directory

pear build assembles per-OS makes into the multi-architecture deployment directory layout pear stage expects. It is part of the pear CLI, so there is nothing extra to install — run it as pear build (or npx pear build).

The expected layout:

pear-chat-1.0.1/
├─ package.json
└─ by-arch/
   └─ <platform-arch>/
      └─ app/

Run pear build from outside the application folder (see stage size increases). Pass each make's output via --<platform-arch>-app and pick a --target name:

pear build \
  --package=./pear-chat/package.json \
  --darwin-arm64-app ./pear-chat/out/PearChat-darwin-arm64/PearChat.app \
  --darwin-x64-app   ./pear-chat/out/PearChat-darwin-x64/PearChat.app \
  --linux-arm64-app  ./pear-chat/out/PearChat-linux-arm64/PearChat.AppImage \
  --linux-x64-app    ./pear-chat/out/PearChat-linux-x64/PearChat.AppImage \
  --win32-x64-app    ./pear-chat/out/PearChat-win32-x64/PearChat.msix \
  --target pear-chat-1.0.1

If you omit --target, the deployment directory is named <name>-<version> from package.json. Each make produces a single platform-arch on its own machine — transfer the build outputs to the build machine first, then assemble.

5. Stage

pear stage <upgrade-link> <deployment-directory> syncs the directory into the Hypercore behind the link.

Always dry-run first and read the file-by-file diff:

pear stage --dry-run pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o ./pear-chat-1.0.1

Look for:

  • Files you expect to ship
  • No surprise additions (.DS_Store, editor swap files, secrets, deployment directories nested inside the app)
  • Sensible byte counts

Run the real stage:

pear stage pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o ./pear-chat-1.0.1

The output should match the dry-run output diff for diff. If anything differs, abort and investigate before continuing.

Stage checklist

  • Always dry-run first.
  • Check the dry-run output diff.
  • Compare the live stage output to the dry-run output diff.

Confirm stage updates

Open the app on a second machine. The seeding process from step 0 should show peers joining as instances connect. To verify the update flow:

  1. Change a file.
  2. npm version patch.
  3. npm run make.
  4. pear build.
  5. pear stage.

If upgrade points to the stage link, every connected instance should fire the updated event from pear.updater (and, in the template, surface an "Update ready!" button).

6. Provision

pear stage is append-only — every file you ever staged stays in the core history. pear provision resyncs from a stage link onto a target link while compacting deletions, producing a leaner drive for prerelease distribution.

The signature is:

pear provision <versioned-source-link> <target-link> <versioned-production-link>

A versioned link has the form pear://<fork>.<length>.<key>. Read the <length> from the most recent pear stage output line.

The target link is a fresh pear:// from step 0. While bootstrapping, set the third argument (the versioned production link) to pear://0.0.<target-key>; after a multisig drive exists, use pear://<fork>.<length>.<multisig-key> instead.

Dry-run first:

pear provision --dry-run \
  pear://0.1079.qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o \
  pear://q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o \
  pear://0.0.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o

Read the diff and run for real:

pear provision \
  pear://0.1079.qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o \
  pear://q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o \
  pear://0.0.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o

Switching builds to provision

Update package.json:

  • upgrade → the provision link (step 1)
  • version → bumped (step 2)

Make, pear build, and stage again. Then provision a second time so the source link points at the provision target:

pear provision pear://0.1080.qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o pear://q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o pear://0.0.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o

Provision checklist

  • Always dry-run first.
  • Check the dry-run output diff before provisioning.

7. Multisig

Production releases are gated by a multisig drive — a Hypercore whose write access is controlled by a quorum of signing keys instead of one machine's private key. This is what removes the single point of failure from a typical desktop release flow, and unlike stage and provision drives, a multisig drive is not machine-bound.

Multisig has a one-time setup and a repeatable per-release flow, each covered in its own guide:

  • Set up multisig — generate signing keys, write multisig.json, compute the multisig link, and point upgrade at it. Do this once.
  • Sign with multisig — prepare a request, collect a quorum of signatures, verify, and commit the production drive. Repeat for every release.
  • Troubleshoot multisig — refused requests, interrupted commits, INCOMPATIBLE_SOURCE_AND_TARGET, and recovering lost write access.

Disabling updates

Pass --no-updates per run to skip the OTA check (matches hello-pear-electron's default npm start script):

npm start -- --no-updates

To disable updates for every run of a build, spread the package config into new PearRuntime({ ... }) and set updates: false:

const pkg = require('../package.json')
const pear = new PearRuntime({
  ...pkg,
  updates: false,
  // ... other options
})

This is useful for kiosk binaries or air-gapped distributions where OTA must be off.

Release lines and multiple stage drives

A typical project keeps several stage drives in parallel — development, staging, rc, plus any number of custom lines for experiments or hotfixes. Each line has its own pear:// link from step 0 and its own seeded reseeders.

Production has exactly one chain: rc → provision (prerelease) → multisig (production). The rc build's upgrade field points at the multisig link, so rc builds do not receive OTA updates — every rc iteration requires a new installer. The Release pipeline explanation has the conceptual picture.

To bootstrap a new release line:

  1. Run step 0 to mint and seed a new link.
  2. Run step 1 in a build dedicated to that line.
  3. Skip step 2 — the line starts at the current version.
  4. Run steps 3 and 4 to produce a build.
  5. The build is now pointed at the new line; share it with whoever should follow that line.

See also

On this page