LogoPear Docs
How ToOperating an app

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.

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 (a small VPS or a CI runner with pear seed --daemon works) 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 (step 7c). 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 the per-OS make scripts:

npm run make:darwin   # .app
npm run make:linux    # .AppImage
npm run make:win32    # .msix

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. Install it with npm i -g pear-build, or invoke it once with 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 (step 2).
  3. npm run make:<os> (step 3).
  4. pear-build (step 4).
  5. pear stage again.

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.

There are three setup steps and four release steps.

7a. Create signing keys

Every signer runs:

npm i -g hypercore-sign
hypercore-sign-generate-keys

The public key lands at ~/.hypercore-sign/default.public. Share it with whoever is assembling the multisig config.

7b. Create the multisig config

Write multisig.json with every signer's public key, a quorum count, an arbitrary namespace, and the provision link's key as srcKey:

{
  "type": "drive",
  "publicKeys": ["pubkey-signer-1", "pubkey-signer-2", "pubkey-signer-3"],
  "namespace": "pear-chat",
  "quorum": 2,
  "srcKey": "q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o"
}

This example needs two of three signers to release.

Install hyper-multisig-cli and compute the multisig link:

npm i -g hyper-multisig-cli
hyper-multisig link
# pear://69qwbihxj4c8te15wt3skj4j1g3ufmbo3mperedjqr1hb55mspoo

Set upgrade to that link (step 1), bump version (step 2), make, pear-build, and pear stage. Then provision again with the multisig link as the third argument:

pear provision pear://0.1082.qxenz5wmspmryjc13m9yzsqj1conqotn8fb4ocbufwtz9mtbqq5o pear://q9sopzoqgas9usoiq7uzkkwngm5pzj4zo3n4esjwwbmw6offis8o pear://0.0.69qwbihxj4c8te15wt3skj4j1g3ufmbo3mperedjqr1hb55mspoo

The provision drive's upgrade field now points at the multisig drive.

7d. Prepare the multisig request

hyper-multisig request <length>

<length> is the current length of the provision Hypercore (from the most recent pear stage output). hyper-multisig refuses to issue a request if the source drive is not healthily seeded — reseed on more peers before retrying.

7e. Sign

Each signer runs:

hypercore-sign <signing request>

and shares the response. Once a quorum of responses exists, the build is ready to verify.

7f. Verify

hyper-multisig verify [--first-commit] <signing request> <response-1> <response-2> ...

--first-commit is required only the first time you commit to a fresh multisig drive. Never commit a request that fails verify.

7g. Commit

hyper-multisig commit [--first-commit] <signing request> <response-1> <response-2> ...

The commit waits for at least two seeders to fully download the multisig drive before signalling success. Watch the output:

Committing the core...
Committed the core (key <target key>)
Waiting for remote seeders to pick up the changes...
Please add this key to the seeders now. The logs here will notify you when it is picked up by them. Do not shut down until that happens.

Once two seeders confirm, it is safe to Ctrl+C.

Do not abort a running commit. If a commit gets interrupted (crash, power loss), the production build is stuck in an intermediate state; run the commit again as soon as possible. From the second commit onwards, a misuse can corrupt the production build — if hyper-multisig ever errors with INCOMPATIBLE_SOURCE_AND_TARGET, do not work around it; reseed the provision on other peers and retry.

The commit does not have to run on the same machine that prepared the request. The request and responses are enough.

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