Deploy over-the-air updates
Fourth of four onboarding steps: run the installed app, ship a second version, watch the OTA cycle, and preview multisig.
This is part 4 of 4 in the getting started path. You start from the v1.0.1 you shipped in part 3:
- a provision link in
package.json#upgrade - an active
pear seedon that link - a packaged build that embeds the same field.
From here you put the live OTA cycle to work:
- Run the installed app
- Change something visible in the renderer
- Stage v1.0.2 onto your stage link
- Provision it onto the provision link
- Watch the OTA cycle fire — see the OTA update event lifecycle
- Preview multisig, the production gate on top of provision.
Full production-ready reference: hello-pear-electron. The complete version of this chat lives at holepunchto/hello-pear-electron — Holepunch's official Electron template, the same shape Keet and PearPass ship. Clone it any time to see the finished structure or to crib code. For a guided tour of the template, see Start from the hello-pear-electron template.
You do not need cosigners, a Windows machine, or Apple signing credentials to follow along. The deeper guides cover the production-only material:
- Deploy a Pear desktop app — every command, every release line.
- Build desktop distributables — code-signing, notarization, MSIX publisher details.
- Release pipeline — the conceptual picture.
Before you start
You need:
- The shipped v1.0.1 from part 3 — ship. That part covered
pear touch, the stage and provision links,npm version patch,electron-forge make,pear build,pear stage, andpear provision. - The
pear seedon your provision link from part 3 still running. Without an active seeder, peers cannot fetch the new version. - The same
pearCLI from part 3 —pear buildships with it (npm i -g pearornpx pear ...).
Open the installed build
open ./pear-chat/out/PearChat-darwin-arm64/PearChat.app
# Linux: ./pear-chat/out/PearChat-linux-x64/PearChat.AppImage
# Windows: install PearChat-1.0.1.msix and launch from Start menuThe header shows v1.0.1. Send a few chat messages so you can confirm the transcript survives the restart. Leave the app open.
This must be the installed .app, not npm start. The dev script forwards --no-updates, which intentionally disables OTA. Only the packaged build polls for updates.
On macOS, the first launch may prompt Gatekeeper because this build is unsigned. Right-click the .app → Open → Open to bypass it once. Production signing and notarization is covered in Build desktop distributables.
Make a visible change
Edit renderer/index.html and change the header so the new version is obviously different:
- <h1 class="text-sm font-semibold">Pear chat</h1>
+ <h1 class="text-sm font-semibold">Pear chat — v2</h1>Any visible tweak works (text, color, emoji). The point is to confirm the renderer asset on disk swaps after the update.
Bump, make, build, stage, provision
Same release cycle as part 3, with a patch bump. Run from your terminal in the project root — your packaged app keeps running in the GUI:
Bump the version
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease]Make the distributables
npm run makeBuild the deployment directory
cd ..
pear build \
--package=./pear-chat/package.json \
--darwin-arm64-app ./pear-chat/out/PearChat-darwin-arm64/PearChat.app \
--target pear-chat-1.0.2Stage and provision the deployment directory
pear stage pear://<stage-link> ./pear-chat-1.0.2
pear provision \
pear://0.<length>.<stage-key> \
pear://<provision-link> \
pear://0.0.<provision-key>Use the versioned link from the pear stage output for the first argument. <provision-key> is the key segment from <provision-link> (everything after pear://), same as in part 3.
On every cycle, the --target value and the deployment dir you pass to pear stage must match the version you just bumped to. The example above assumes you went from 1.0.1 to 1.0.2; if you've already iterated past that, use pear-chat-<current-version> everywhere. Otherwise pear build writes vX into a directory named after vY, which makes the next bump confusing to debug. You can also omit --target and pear build will auto-name the dir as <name>-<version> from package.json.
Watch the OTA cycle fire
Within seconds the running app reacts. The events come from part 2's wiring (pear.updater.on('updating'|'updated') → preload bridge → renderer):
- The version label in the header flips from
v1.0.1toupdating…. - The yellow Update ready! button appears.
- Click it.
applyUpdate()swaps the application drive,appAfterUpdate()restarts the process. - The header now reads "Pear chat — v2" and the version label shows
v1.0.2. The Corestore-backed chat transcript replays from disk.
If nothing happens for more than a minute, check that pear seed is still running on your provision link and that upgrade in package.json matches <provision-link> — see App did not update.
Part 2 wires the runtime with delay: 0, so updates fire as soon as the new content reaches the local drive. The default pear-runtime-updater delay is a random value up to one hour after the 60s boot grace period — great for production (seeders avoid a thundering herd) but invisible in a tutorial. If you keep that default and the Update ready! button never appears, restart the app to reset the grace period — see Tune PearRuntime delay for live OTA visibility.
The walkthrough stops here. Multisig changes real production links and requires coordinating with at least one other signer, so a tutorial walkthrough is the wrong format. The next section summarizes what it does and links to the production guides.
Why stage and provision are separate links
A stage link keeps the full append-only history — every file you ever staged, even ones you later deleted. A provision link is the lean snapshot peers poll: pear provision block-syncs from a versioned stage link onto the provision target, compacting deletions along the way. Part 3 and the iterate loop above already use that split; Deploy your application covers recovery if a stage or provision link is lost.
Multisig (production releases)
A multisig drive is a Hypercore where write access is gated by a quorum of signing keys instead of a single owner machine. This is what production Pear apps use so no single laptop can push a malicious update.
The setup, in 30 seconds:
- Every signer generates a signing key:
pear multisig keys get. Each signer's public key goes in a shared list. - One person sets the
multisigobject inpear.jsonwith the signers'publicKeys, aquorum(e.g. 2 of 3), and anamespace. The provision link is not stored here — it is supplied as the source when you prepare and commit each request. pear multisig linkoutputs the newpear://link, derived from the namespace, public keys, and quorum. Set this as yourupgradefield.
The release flow becomes:
pear multisig request <versioned-provision-link> # prepare a signing request
pear multisig sign <signing request> # each signer runs this; shares response
pear multisig verify <source-link> <signing request> [...responses]
pear multisig commit <source-link> <signing request> [...responses] # commits when quorum is reachedRelease pipeline and Deploy a Pear desktop app cover the full quorum lifecycle, key rotation, recovering from lost write-access, and the release lines pattern (development → staging → rc → prerelease → production) that real teams use.
What you've learned
You now have an end-to-end mental model for iterating a Pear Electron app:
| Stage | What it is | Reversible? |
|---|---|---|
| Iterate loop | npm version patch → make → pear build → pear stage → pear provision | Yes — reprovision from a different stage length |
| OTA cycle | pear.updater emits updating/updated; renderer button calls applyUpdate + appAfterUpdate | Yes — restart loads the previous bundle until the next swap |
| Stage vs provision | Stage link for append-only sync; provision link for what peers poll | History is permanent on the stage core |
| Multisig commit | Quorum signs and publishes | Cryptographically committed |
Every release iteration after part 3 is the same pattern: npm version patch, npm run make (on each OS you ship), pear build, pear stage --dry-run, pear stage, pear provision. Once multisig is wired, the four-step multisig flow replaces publishing directly to the provision link for production.
Where to go next
- Deploy a Pear desktop app — the canonical how-to with every command, every flag, and every recovery procedure.
- Build desktop distributables — code-signing, notarization, MSIX publisher details.
- Troubleshoot desktop releases — "the app did not update", lost write-access, stage size blowups, OTA polling cadence.
- Release pipeline — the conceptual picture, deployment layers, and release lines.
- Release pipeline glossary — terminology.
hello-pear-electron— the upstream template every snippet in this getting started path is based on.