Push app updates to your customers¶
When you ship a new version of your protected app, PyLocket can notify every customer on their next app launch — they'll see a popup offering to install the new version. No more emailing customers asking them to redownload manually, no more stale security patches, no more "which version are you running?" support threads.
This guide walks through how it works and how to push your first update.
How it works¶
You click "Push latest version" in the portal
↓
End-user launches your protected app
↓
App silently calls PyLocket to check for updates
↓
PyLocket says: "version X.Y.Z is available" + signed manifest
↓
App verifies the signature, then shows a popup:
[ Install now ] [ Not right now ] [ Skip this version ]
↓
If Install: downloads new version, verifies it, swaps it into place,
and relaunches as the new version.
Old version kept on disk with a .old.<timestamp> suffix.
The check runs in a background thread so your app never stalls at launch waiting for PyLocket — if our servers are slow or offline, your app launches normally and picks up the update on the next launch.
Every update payload is signed with the same Ed25519 key PyLocket uses for license activation. The end-user's protected app refuses to install anything whose signature doesn't match, so a network attacker can't swap in a malicious binary.
Before you start¶
- Your app must be protected with PyLocket engine v1.0.0.0.0.76 or newer. Apps protected before this version don't have the update-check code baked in. To enable updates, just re-protect and re-ship once — future updates after that flow normally. Your customers don't need to do anything special; the first re-protected build they install is the last manual install they'll ever need.
- You need an active Pro subscription (pushing updates is a Pro feature).
- At least one READY build for the app, plus one active license holder to push to.
Push an update to everyone¶
Bulk push — the most common flow.
- Ship your new protected build through PyLocket as usual. Upload it, wait for status
READY. - Open your App in the portal: Apps → [your app].
- Scroll to the Push app update section. You'll see:
- Latest READY build — the version PyLocket thinks you want to push.
- Push latest version button.
- Click Push latest version. A modal appears:
- Optionally type release notes (plain text, shown verbatim in the end-user popup — e.g. "Security fix for login flow").
- Click Push to all eligible users.
- The section refreshes to show an Active push card with the target version, timestamp, release notes, and an adoption breakdown that fills in as customers launch their apps over the next hours/days.
That's it. Every customer's next app launch will show the popup.
Watching adoption¶
The adoption bar under the Active push card shows:
- Installed (green) — devices that installed this update.
- Skipped (amber) — devices where the end-user clicked Skip this version. They won't be prompted again until you push a newer version than this one.
- Pending (grey) — offline, haven't launched since the push, or haven't responded yet.
Give it time — an end-user who hasn't opened your app in a week will be a Pending device until they do.
Clearing an active push¶
If you pushed by mistake, or want to stop prompting customers:
- App detail → Push app update section → Clear active push (red link on the Active push card).
End-user apps stop prompting on their next launch. Customers who already installed keep the new version. Customers who clicked Skip stay on the old version. No rollback is forced.
Push an update to one customer (targeted)¶
Useful for beta testing — push a new version to a single customer before you bulk-push to everyone else.
- Go to Licenses.
- Find the customer's license row. On the right, click the cloud-arrow icon (tooltip: "Push app update to this customer's devices").
- Optional release notes → Push update.
That one license's devices get the push on their next launch; all other licenses keep seeing whatever the app-level push says (or no push at all). When you're happy with the beta, do the bulk push — it'll override any stale per-license pushes automatically.
What the end user sees¶
A standard system dialog, drawn by your app's own process — it looks native on every platform:
┌────────────────────────────────────────────┐
│ │
│ A new version is available │
│ │
│ Version 1.2.0 │
│ │
│ Security fix for login flow │
│ │
│ [ Install now ] [ Not right now ] │
│ [ Skip this version ] │
│ │
└────────────────────────────────────────────┘
- Install now — downloads, verifies, installs, relaunches. Takes ~5–30 seconds depending on download size.
- Not right now — dismisses. Same popup appears on the next launch.
- Skip this version — dismisses permanently for this version. They won't be prompted again until you push a newer version. PyLocket shows the popup again automatically when the target changes.
If the user closes the popup via the window's X button, it's treated as Not right now — they'll see it again next launch rather than getting bombarded.
What happens on install¶
- Download — your new protected build streams to
~/.pylocket/updates/<app_id>/<build_id>/with an inline SHA256 check. If the hash doesn't match, the install aborts before touching anything. - Rename — the running binary gets renamed to
<name>.old.<timestamp>next to the original location. The rename is legal even while the binary is running (the OS tracks open files by inode/handle, not path). On Windows, PyLocket uses a 3× retry with exponential backoff to absorb transient sharing-violations from AV scanners. - Move — the new binary moves into the original path.
- Relaunch — PyLocket spawns the new binary with the same command-line args your customer used, then exits the old process. The switchover is nearly instant — the customer sees the app re-appear as the new version.
The old binary stays on disk with a timestamped .old suffix so customers can always revert if the new version breaks something — they can manually run MyApp.old.20260420-120305 and continue as before.
Platform-specific details¶
All three platforms use the same four-step primitive (download → verify → rename-then-replace → relaunch), differing only in what gets renamed:
| Platform | What's renamed | Old-binary location |
|---|---|---|
| Linux | Single ELF binary | /path/to/myapp.old.20260420-120305 |
| macOS | The whole .app directory |
/Applications/My.app.old.20260420-120305/ |
| Windows (one-file build) | Single .exe |
C:\...\myapp.exe.old.20260420-120305 |
| Windows (one-folder build / CxFreeze / Briefcase) | The whole install directory | C:\Apps\MyApp.old.20260420-120305\ |
Troubleshooting¶
The update popup isn't showing up on my test device.
Most common cause: your customer's app was protected with an older engine version (pre-v1.0.0.0.0.76) that didn't include the update-check code. Re-protect and re-ship the app once; all updates after that will flow normally.
Second most common cause: the device's license is expired or revoked. PyLocket doesn't push updates to inactive licenses. Check the license's Status column on the Licenses page.
The customer installed the update but their version in the Licenses table didn't change.
The installed version is recorded on the device's next app launch after install. If they just installed and haven't yet re-launched, the table is stale — refresh after 30 seconds.
The customer says the install failed.
- On Windows, aggressive antivirus software can hold locks on the running binary that defeat even PyLocket's retry loop. The customer can usually fix it by exiting the app fully (including tray/menubar icons) and re-launching — the next launch will try the install again. If the problem persists, they should allowlist your app in their AV.
- On any platform, a corrupted download (sha256 mismatch) is caught before any file is touched — the old binary is left intact and the customer can retry later.
- The old binary is preserved with a
.old.<ts>suffix, so nothing is ever destroyed. If the install left things in a weird state, the customer can rename the.oldfile back and resume.
How do I roll back a push?
Clear the active push via App detail → Push app update → Clear active push. That stops prompting new devices. Customers who already installed the new version stay on it — there's no server-side rollback of installs. If you need a forced downgrade, push a fresh build that happens to be v(old) instead of v(new) — the end-user popup treats it as a new update and customers opt in to install it.
Do customers who've never launched since a push eventually see the prompt?
Yes. The push target sits on your app's record indefinitely. Whenever a customer's license holder finally launches, their app checks with PyLocket, sees there's a target, and shows the popup. No time limit.
Privacy and security¶
- Nothing about your end-user's identity is sent to PyLocket beyond the license key and a device fingerprint hash. The fingerprint is a SHA-256 of a few stable system identifiers (MAC address, hostname, OS user) — not reversible to PII.
- The update check is the only regular outbound call the protected app makes after initial activation. It's a plain HTTPS POST with a short body; failures are silently ignored so the app always launches even if our servers are down.
- Signed manifests prevent network-level tampering. Every update-available response carries an Ed25519 signature over the canonical-JSON manifest. The end-user's app refuses to install any download whose manifest signature or SHA-256 fails verification.
See also¶
- Sell Your App with Stripe — collect payments before customers activate.
- Distribute Your App — distribution channels overview.
- Configure Licensing — license expiry, device limits, free trials.