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://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5oAnnounce the link on the swarm:
pear seed pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5oRun 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.
1. Set the upgrade link
Every shipped build polls a pear:// link from its package.json upgrade field. Set it once per release line:
npm pkg set upgrade=pear://qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5oOr 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 WindowsThe full coverage — entitlements, code signing, notarization, MSIX publisher matching, signing certificates — is in Build desktop distributables.
The package.json checklist before a make:
authorpopulatedlicensepopulateddescriptionpopulatednameset per brandproductNameset per brandbuild/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.1If 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.1Look 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.1The 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:
- Change a file.
npm version patch.npm run make.pear build.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.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8oRead the diff and run for real:
pear provision \
pear://0.1079.qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o \
pear://q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o \
pear://0.0.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8oSwitching 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.q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8oProvision 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 pointupgradeat 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-updatesTo 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:
- Run step 0 to mint and seed a new link.
- Run step 1 in a build dedicated to that line.
- Skip step 2 — the line starts at the current
version. - Run steps 3 and 4 to produce a build.
- The build is now pointed at the new line; share it with whoever should follow that line.
See also
- Set up multisig — one-time signing-key, config, and link setup for the production gate.
- Sign with multisig — the per-release prepare → sign → verify → commit flow.
- Troubleshoot multisig — refused requests, interrupted commits, and recovering lost write access.
- Publish with GitHub Actions — the simplest publish/update workflow, staging from CI on every push.
- Release pipeline — conceptual diagrams for stage, provision, multisig, and release lines.
- Build desktop distributables — per-OS makes, signing, notarization, and MSIX publisher details.
- Desktop release npm scripts — the
package.jsonscripts that drive this flow. - Troubleshoot desktop releases — OTA, seeding, deployment-folder, and lost write-access pitfalls.
- Release pipeline glossary — terminology.
pear-ciGitHub Action reference — inputs, outputs, and the CLI behind CI publishing.pear-runtimereference — the runtime that drives OTA on the client.- Storage and distribution — how releases reach users.