This guide demonstrates how to use winappcli with a Rust application to debug with package identity and package your application as an MSIX.
For a complete working example, check out the Rust sample in this repository.
Package identity is a core concept in the Windows app model. It allows your application to access specific Windows APIs (like Notifications, Security, AI APIs, etc), have a clean install/uninstall experience, and more.
A standard executable (like one created with cargo build) does not have package identity. This guide shows how to add it for debugging and then package it for distribution.
-
Rust Toolchain: Install Rust using rustup or winget (or update if already installed):
winget install Rustlang.Rustup --source winget -
winapp CLI: Install the
winapptool via winget (or update if already installed):winget install microsoft.winappcli --source winget
Start by creating a simple Rust application:
cargo new rust-app
cd rust-appRun it to make sure everything is working:
cargo runOutput should be "Hello, world!"
We'll update the app to check if it's running with package identity. This will help us verify that identity is working correctly in later steps. We'll use the windows crate to access Windows APIs.
First, add the windows dependency to your Cargo.toml by running:
cargo add windows --features ApplicationModelThis adds the Windows API bindings with the ApplicationModel feature, which gives us access to the Package API for checking identity.
Next, replace the entire contents of src/main.rs with the following code. This code attempts to retrieve the current package identity. If it succeeds, it prints the Package Family Name; otherwise, it prints "Not packaged".
Note: The full sample also includes code to show a Windows Notification if identity is present, but for this guide, we'll focus on the identity check.
use windows::ApplicationModel::Package;
fn main() {
match Package::Current() {
Ok(package) => {
match package.Id() {
Ok(id) => match id.FamilyName() {
Ok(name) => println!("Package Family Name: {}", name),
Err(e) => println!("Error getting family name: {}", e),
},
Err(e) => println!("Error getting package ID: {}", e),
}
}
Err(_) => println!("Not packaged"),
}
}Now, build and run the app as usual:
cargo runYou should see the output "Not packaged". This confirms that the standard executable is running without any package identity.
The winapp init command sets up everything you need in one go: app manifest and assets. The manifest defines your app's identity (name, publisher, version) which Windows uses to grant API access.
Run the following command and follow the prompts:
winapp initWhen prompted:
- Package name: Press Enter to accept the default (rust-app)
- Publisher name: Press Enter to accept the default or enter your name
- Version: Press Enter to accept 1.0.0.0
- Description: Press Enter to accept the default or enter a description
- Setup SDKs: Select "Do not setup SDKs" (Rust uses its own
windowscrate, not the C++ SDK headers)
This command will:
- Create
Package.appxmanifest— the manifest that defines your app's identity - Create
Assetsfolder — icons required for MSIX packaging and Store submission
Note: Because no SDK packages are being managed, no
winapp.yamlis created — Rust uses thewindowscrate via Cargo, so there's nothing forwinapp restore/updateto track.
You can open Package.appxmanifest to further customize properties like the display name, publisher, and capabilities.
Because cargo new creates a console app, we need to add an execution alias to the manifest. Without it, winapp run launches the app via AUMID activation, which opens a new window — and that window closes immediately when a console app finishes, swallowing any output.
The alias also lets users run your app by name from any terminal after they install the MSIX. The manifest registers an alias like rust-app.exe (defaulting to your project's exe name), which users can invoke as rust-app or rust-app.exe.
Skip this step if you're building a UI app (a Rust app that renders its own window). Those apps work fine with the default AUMID launch.
Add the alias:
winapp manifest add-aliasThis adds a uap5:ExecutionAlias entry to Package.appxmanifest.
To test features that require identity (like Notifications) without fully packaging the app, use winapp run. This registers the entire build output folder as a loose layout package — just like a real MSIX install — and launches the app. No certificate or signing is needed for debugging.
-
Build the executable:
cargo build
-
Run with identity:
winapp run .\target\debug --with-alias
The --with-alias flag launches the app via its execution alias so console output stays in the current terminal. This requires the uap5:ExecutionAlias we added in step 4.
Note:
winapp runalso registers the package on your system. This is why the MSIX may appear as "already installed" when you try to install it later in step 6. Usewinapp unregisterto clean up development packages when done.
You should now see output similar to:
Package Family Name: rust-app_12345abcde
This confirms your app is running with a valid package identity!
Tip: For advanced debugging workflows (attaching debuggers, IDE setup, startup debugging), see the Debugging Guide.
Once you're ready to distribute your app, you can package it as an MSIX using the same manifest. MSIX provides clean install/uninstall, auto-updates, and a trusted installation experience.
First, build your application in release mode for optimal performance:
cargo build --releaseThen, create a directory with just the files needed for distribution. The target\release folder contains build artifacts that aren't part of your app — we only need the executable:
mkdir dist
copy .\target\release\rust-app.exe .\dist\MSIX packages must be signed. For local testing, generate a self-signed development certificate:
winapp cert generate --if-exists skipImportant: The certificate's publisher must match the
Publisherin yourPackage.appxmanifest. Thecert generatecommand reads this automatically from your manifest.
Now you can package and sign in one step:
winapp pack .\dist --cert .\devcert.pfx Note: The
packcommand automatically uses the Package.appxmanifest from your current directory and copies it to the target folder before packaging. The generated .msix file will be in the current directory.
Before you can install the MSIX package, you need to trust the development certificate on your machine. Run this command as administrator (you only need to do this once per certificate):
winapp cert install .\devcert.pfxNote: If you used
winapp runin step 5, the package may already be registered on your system. Usewinapp unregisterfirst to remove the development registration, then install the release package.
Install the package by double-clicking the generated .msix file, or via PowerShell:
Add-AppxPackage .\rust-app.msixNow you can run your app from anywhere in the terminal by typing:
rust-appYou should see the "Package Family Name" output, confirming it's installed and running with identity.
Tip: If you need to repackage your app (e.g., after code changes), increment the
Versionin yourPackage.appxmanifestbefore runningwinapp packagain. Windows requires a higher version number to update an installed package.
- Once you are ready for distribution, you can sign your MSIX with a code signing certificate from a Certificate Authority so your users don't have to install a self-signed certificate
- The Microsoft Store will sign the MSIX for you, no need to sign before submission.
- You might need to create multiple MSIX packages, one for each architecture you support (x64, Arm64)
- Distribute via winget: Submit your MSIX to the Windows Package Manager Community Repository
- Publish to the Microsoft Store: Use
winapp storeto submit your package - Set up CI/CD: Use the
setup-WinAppCliGitHub Action to automate packaging in your pipeline - Explore Windows APIs: With package identity, you can now use Notifications, on-device AI, and other identity-dependent APIs