CleanUpdate.Core 0.1.3

dotnet add package CleanUpdate.Core --version 0.1.3
                    
NuGet\Install-Package CleanUpdate.Core -Version 0.1.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CleanUpdate.Core" Version="0.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CleanUpdate.Core" Version="0.1.3" />
                    
Directory.Packages.props
<PackageReference Include="CleanUpdate.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CleanUpdate.Core --version 0.1.3
                    
#r "nuget: CleanUpdate.Core, 0.1.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CleanUpdate.Core@0.1.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CleanUpdate.Core&version=0.1.3
                    
Install as a Cake Addin
#tool nuget:?package=CleanUpdate.Core&version=0.1.3
                    
Install as a Cake Tool

<div align="center">

⬆️ CleanUpdate

A small, modern, secure self-update library for .NET desktop apps.

CI NuGet NuGet License: MIT Targets

</div>


What is it?

Desktop apps need to update themselves β€” but you can't overwrite a running .exe, XML-feed updaters feel a decade old, and rolling your own means re-solving hashing, signatures, and safe file swaps.

CleanUpdate is an instance-based, async self-update library (a cleaner alternative to AutoUpdater.NET). A headless core checks a source, downloads and verifies a package, and applies it; an optional WPF or WinForms dialog gives you the whole experience in one call. It reads a JSON feed or GitHub Releases, verifies every package (SHA-256, optionally RSA-signed), and applies zip updates through an atomic swap helper that backs up, replaces, and rolls back on failure.

Reach for it when you ship a portable .NET desktop app and want secure, reliable in-app updates without standing up an update server.

  • 🧩 UI is optional. CleanUpdate.Core is fully headless; the WPF/WinForms dialogs are one-call wrappers.
  • πŸ”’ Secure by default. Every package needs a SHA-256 hash; missing hashes are rejected. Add a public key to require RSA signatures.
  • πŸ” Atomic zip swaps. An external helper replaces the app folder after exit, with automatic rollback.
  • 🌐 Bring your own source. A hosted JSON feed or GitHub Releases β€” both via one IReleaseProvider seam.

How it works

flowchart LR
    A[App starts] --> B{CheckAsync}
    B -->|up to date| Z[Do nothing]
    B -->|update available| C[Show prompt / your UI]
    C --> D[DownloadAndVerifyAsync]
    D -->|SHA-256 + signature OK| E{Package type}
    E -->|installer| F[Launch installer]
    E -->|zip| G[Stage files β†’ launch swap helper]
    G --> H[App exits]
    H --> I[Helper: backup β†’ swap β†’ rollback on error]
    I --> J[Relaunch app]

Quick start

  1. Install the package for your UI (or the headless core).
  2. Add a few lines to your app to check and apply updates (see Minimal examples).
  3. Publish a release β€” the cleanupdate CLI turns your dotnet publish folder into the zip, hashes, feed.json and signatures for you.
  4. Upload those artifacts and point the app's Feed at your feed.json.

Install

dotnet add package CleanUpdate.Wpf        # WPF app with the ready-made dialog
dotnet add package CleanUpdate.WinForms   # WinForms app with the ready-made dialog
dotnet add package CleanUpdate.Core       # headless / custom UI (pulled in by the UI packages)

All packages target net8.0 and .NET Framework 4.8 (UI packages use net8.0-windows). The swap helper (CleanUpdate.Tool.exe) is bundled in Core and copied into your output automatically β€” no manual step for the zip flow.


Minimal examples

WPF

using CleanUpdate;
using CleanUpdate.Wpf;

using var updater = new Updater(new UpdaterOptions
{
    Feed            = new Uri("https://example.com/update.json"),
    ExitApplication = () => Application.Current.Shutdown()   // let files be swapped cleanly
});

// Check the feed and, if an update is available (and not skipped/postponed), show the dialog.
await WpfUpdater.CheckAndPromptAsync(updater, owner: this);

WinForms

using CleanUpdate;
using CleanUpdate.WinForms;

using var updater = new Updater(new UpdaterOptions
{
    Feed            = new Uri("https://example.com/update.json"),
    ExitApplication = () => Application.Exit()
});

await WinFormsUpdater.CheckAndPromptAsync(updater, owner: this);

Headless / custom UI

using CleanUpdate;
using CleanUpdate.Download;

using var updater = new Updater(new UpdaterOptions { Feed = new Uri(feedUrl) });

UpdateCheckResult check = await updater.CheckAsync(ct);
if (check.ShouldPrompt)                                   // available and not skipped/postponed
{
    var progress = new Progress<DownloadProgress>(p =>
        Console.Write($"\rDownloading… {p.Fraction * 100:0}%"));

    string file = await updater.DownloadAndVerifyAsync(check.Info!, progress, ct);
    await updater.ApplyAndRestartAsync(check.Info!, file, ct);
}

For production, also set CurrentVersion, a Channel, and β€” strongly recommended β€” sign your releases and set PublicKeyXml. See the Security model.


Feed format

The feed is one release object, or an object with a releases array (multiple versions/channels). The updater picks the highest version on the requested channel.

Minimal:

{
  "version": "2.1.0",
  "package": {
    "type": "zip",
    "url": "https://downloads.example.com/myapp/myapp-2.1.0.zip",
    "sha256": "<lowercase hex sha-256 of the file>"
  }
}

Full:

{
  "releases": [
    {
      "version": "2.1.0",
      "channel": "stable",
      "mandatory": { "enabled": false, "minVersion": "2.0.0" },
      "changelog": "β€’ New dashboard\nβ€’ Faster startup\nβ€’ Bug fixes",
      "package": {
        "type": "zip",
        "url": "https://downloads.example.com/myapp/myapp-2.1.0.zip",
        "size": 10485760,
        "sha256": "<lowercase hex sha-256 of the file>",
        "signature": "<base64 RSA-SHA256 signature, required when a public key is set>"
      }
    }
  ]
}

The complete field reference is in docs/api.md.


Publishing updates

You don't hand-write the feed or compute hashes. The cleanupdate .NET tool turns a publish folder into everything the app consumes β€” and signs it with the same algorithm the app verifies with:

dotnet tool install --global CleanUpdate.Cli

dotnet publish -c Release -o publish
cleanupdate pack --input publish --version 2.1.0 --name myapp \
    --base-url https://downloads.example.com/myapp \
    --key private.xml --delta --feed feed.json

That emits the release zip + .sha256, a feed.json (release upserted by version), the package/feed RSA signatures, and a delta manifest.json. Generate a key pair once with cleanupdate keygen. Full workflow, options, and CI usage: docs/publishing.md.


Security model

CleanUpdate verifies every update package before it is applied.

By default:

  • every package needs a SHA-256 hash β€” a missing hash is rejected, not installed unverified;
  • serve the feed and packages over HTTPS;
  • set a public key and RSA signatures become required β€” unsigned or tampered packages are refused.

What each layer proves:

  • SHA-256 β€” the package bytes were not corrupted or swapped (but not who produced them).
  • RSA package signature β€” the package was signed with your private key.
  • Feed signature β€” the whole feed document (version, url, installer args, …). Without it, unsigned metadata is only as trustworthy as its transport.

Full setup β€” generating keys, signing packages, signing the feed, and zip-extraction hardening β€” is in docs/security.md.


Delivery modes

Chosen per release via package.type:

  • zip β€” atomic swap (recommended for portable apps). Extracted to a staging folder, verified, and handed to CleanUpdate.Tool.exe, which relocates itself, waits for your app to exit, backs up the current install, swaps in the new files, rolls back on any error, and relaunches.
  • installer β€” run a setup. The verified .exe/.msi is launched with package.args (optionally elevated via RunAsAdmin). No helper required.

Advanced

  • GitHub Releases as a source β€” point at a repo's Releases, nothing to host.
  • Delta updates β€” ship a manifest so only changed files download.
  • Skip / Remind me later β€” attach a StateStore (JsonFileStateStore by default) and choices persist across runs; CheckAsync then reports Suppressed/ShouldPrompt. Mandatory updates are never suppressed. The ready-made dialogs wire this automatically.
  • Changelogs β€” await updater.GetChangelogAsync(info, ct) returns the inline changelog or fetches changelogUrl. The dialogs display it for you.
  • Custom sources β€” implement IReleaseProvider to read from a CDN, an internal service, or a file share.

API reference

The full type, options, and method reference lives in docs/api.md. Most apps only touch a handful:

Member Purpose
Updater.CheckAsync(ct) Fetch releases and decide if a newer one applies.
Updater.DownloadAndVerifyAsync(info, progress, ct) Resumable download + SHA-256/signature verification.
Updater.ApplyAndRestartAsync(info, file, ct) Apply (installer or zip-swap) and restart.
Updater.UpdateAsync(progress, ct) Convenience: check β†’ download β†’ verify β†’ apply. Honors Skip/Later.

Contributing & releasing

Building, testing, packing, the repository layout, and the CI/release workflows are documented in docs/releasing.md.


License

MIT Β© CleanUpdate contributors

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on CleanUpdate.Core:

Package Downloads
CleanUpdate.WinForms

Ready-made, dark-themed WinForms update dialog for CleanUpdate. Depends on CleanUpdate.Core.

CleanUpdate.Wpf

Ready-made, themable WPF update dialog for CleanUpdate. Depends on CleanUpdate.Core.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.3 139 8/4/2026
0.1.2 116 8/4/2026