You added licensing, activation works, everyone is happy — and then you ship your first auto-update and the support tickets start: "it's asking me to activate again." The cause is almost always the same, and it has nothing to do with your licensing logic. It is where you cached the activation. Modern .NET desktop updaters install each version into its own directory, and anything you wrote next to the executable goes with the old version. This post is the per-updater map of where to put the signed lease so activation survives.
The root cause: versioned install directories
A cached activation is a signed lease — a small, node-locked, RSA-signed blob the SDK writes after a successful activation so the app can validate offline on later launches without phoning home. The question is which directory it lands in.
Velopack and Squirrel do not update in place. They install version 1.0.0 to …\YourApp\app-1.0.0\, then install 1.1.0 to …\YourApp\app-1.1.0\ and point a stub launcher at the new folder. If your lease was written next to the EXE — AppDomain.CurrentDomain.BaseDirectory or Assembly.Location's folder — it was written into app-1.0.0, and the 1.1.0 process starts in a folder where that file does not exist. The lease is not gone; it is stranded in the previous version's directory, which the updater will eventually clean up.
.b{fill:none;stroke:#64748b;stroke-width:1.5}.t{fill:currentColor;font-size:13px}.s{fill:#64748b;font-size:11px}.bad{stroke:#ef4444;stroke-width:1.5}.ok{stroke:#10b981;stroke-width:2}.ar{stroke:#64748b;stroke-width:1.5;marker-end:url(#u)}
app-1.0.0\lease(lost on update)
app-1.1.0\ (empty)→ re-activate prompt
%LocalAppData%\YourApp\leasestable across every version → survives
Same machine fingerprint before and after,
so a reachable lease still validates offline.
Where to put the lease, per updater
The rule is one line: store the lease in a stable, version-independent location, and point the SDK's lease-cache path at it. The specifics differ by updater.
Velopack / Squirrel. Use a fixed per-user folder — %LocalAppData%\YourApp (the stable parent of the app-* folders) or Roaming if you want it to follow a roaming profile:
string dir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YourApp");
Directory.CreateDirectory(dir);
keyright.LeaseCachePath = Path.Combine(dir, "license.lease");
MSIX. Packaged apps are redirected, but in your favour: ApplicationData.Current.LocalFolder persists across package updates because the package identity is stable. Write the lease there (not next to the EXE inside the package, which is read-only anyway):
// MSIX-packaged app
var folder = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
keyright.LeaseCachePath = Path.Combine(folder, "license.lease");
This pairs with the packaging and machine-id details in licensing an MSIX-packaged app.
ClickOnce. Avoid the per-deployment data directory (it is isolated per version and migrated in ways you do not control). Use a fixed app-data folder you own, exactly like the Velopack case above.
In every case the SDK then reads that path on startup, verifies the lease signature against your embedded public key, and — because the hardware fingerprint is unchanged by a software update — validates offline with no server round-trip.
Why this is safe, and the idempotency backstop
Two facts make this robust. First, the lease is node-locked to hardware, and updating software does not touch the fingerprint, so a persisted lease is still valid for the machine after the update. Second, even in the edge case where a lease is genuinely lost (a user clears app-data, say), re-activating the same machine is idempotent in Keyright — the fingerprint matches the existing activation, so it does not burn a second seat (the mechanics are in node-locking and seat management). So the worst case is a silent, seat-neutral re-activation, and the normal case is no prompt at all.
Doing this with Keyright
Keyright lets you set the lease-cache path explicitly, so you control exactly where the signed activation lives — a stable %LocalAppData% folder, the MSIX LocalFolder, or your own ClickOnce data directory — and it validates offline against the hardware fingerprint after every update. Combined with idempotent same-machine re-activation, your users activate once and never see the prompt again, no matter how often you ship. It is self-hosted, so the activation data and signing stay on infrastructure you control.
See the plans, or start from storing an activated license on the client and move the path to a stable location.
Top comments (0)