Context
The winapp CLI's sparse packaging support is currently limited to the create-debug-identity command, which is designed for developer-time debugging only (requires Developer Mode, registers a raw manifest). There is no support for the production sparse packaging workflow described in the MS docs.
This issue proposes adding sparse packaging support to the winapp CLI, covering the first 3 steps of the MS docs workflow — creating the manifest, building/signing the .msix, and embedding identity into the exe. Steps 4–5 (registration/unregistration) are the installer's responsibility, not the CLI's.
Gap Analysis
| # |
Gap |
Current State |
Impact |
| 1 |
No production .msix identity package creation |
Only debug-time raw manifest registration |
High |
| 2 |
No signing integration in sparse flow |
Signing exists but isn't wired in |
High |
| 3 |
Template discrepancies from MS docs |
Existing template uses packagedClassicApp (should be win32App), MinVersion too low, no ProcessorArchitecture="neutral" |
Medium |
| 4 |
No way to embed <msix> identity into exe outside debug flow |
EmbedMsixIdentityToExeAsync exists but is internal to debug flow |
Medium |
Proposed Commands
winapp init --exe <exe> --sparse # Step 1: Create sparse manifest + assets
winapp pack # Step 2: Build and sign .msix identity package (infers sparse from manifest)
winapp embed-identity <exe|xml> # Step 3: Embed <msix> element into exe's fusion manifest or an external XML
These map directly to the first 3 steps of the MS docs workflow.
1. Fix Existing Sparse Template & Code
Update the existing sparse template and related code to match the MS docs:
RuntimeBehavior="packagedClassicApp" → "win32App" (per schema docs: packagedClassicApp = Desktop Bridge/Centennial with virtualization; win32App = plain Win32, correct for sparse packages)
MinVersion="10.0.18362.0" → "10.0.19041.0" (AllowExternalContent and uap10 require 19041+)
- Add
ProcessorArchitecture="neutral" to <Identity> (identity-only packages have no binaries)
2. winapp init --exe <exe> --sparse
MS docs step 1: Create a package manifest for the identity package.
The existing winapp init command is extended with --exe and --sparse flags. When both are provided, the command generates a sparse-specific manifest. The --exe argument provides the path to the application executable, and --sparse signals the sparse packaging flow.
Infers defaults from the exe using FileVersionInfo. Interactive prompts allow the user to override inferred values, with --use-defaults/--no-prompt for non-interactive/CI use.
Validation: --exe is only valid when combined with --sparse. If --exe is provided without --sparse, the command should exit with an error: "--exe requires --sparse. Use 'winapp init' without --exe for full package initialization."
SDK installation: The sparse init flow skips SDK/package installation entirely. Since sparse packages are identity-only, there are no SDK dependencies to download — only the manifest and assets are generated.
Inference from exe (using FileVersionInfo.GetVersionInfo):
| Field |
Inferred From |
Fallback |
| Package name |
FileDescription, then filename without extension |
Directory name |
| Publisher CN |
CompanyName |
CN={username} |
| Description |
Comments, then FileDescription |
"My Application" |
| Version |
FileVersion |
1.0.0.0 |
Interactive prompts (unless --use-defaults):
Shows package name, publisher CN, version, and description with inferred defaults — user can accept or override. No executable prompt (already provided as argument).
Options:
--exe <path> — Path to the application executable (required for sparse)
--sparse — Generate a sparse identity manifest instead of a full package manifest
--use-defaults / --no-prompt — Skip all prompts, use inferred/default values
--name <name> — Override package name
--publisher <CN> — Override publisher CN
--output-dir <path> — Where to write manifest + assets (default: exe's directory)
Workflow:
- Read exe version info to infer defaults
- Prompt for overrides of package name, publisher, version, description (unless
--use-defaults)
- Generate
appxmanifest.xml from the sparse template with values filled in, placed in the exe's directory (or --output-dir)
- Generate placeholder asset images in
Assets/ subdirectory relative to the exe
- Print summary: generated files and next steps ("Run
winapp pack to create the .msix")
Important: The final output should make it clear that any generated assets (icons, images in Assets/) are referenced from the external location (i.e., the app's install directory), not bundled inside the .msix package. The sparse .msix is an identity-only package — visual assets are resolved from the external content path at runtime via Add-AppxPackage -ExternalLocation. Developers should ensure these assets are deployed alongside their application, not packed into the MSIX.
3. winapp pack
MS docs step 2: Build and sign the identity package.
The existing winapp pack command is reused. It detects whether the manifest describes a sparse package by checking for AllowExternalContent="true" in the manifest. When a sparse manifest is detected, the command automatically adjusts its behavior (e.g., identity-only packaging, no binaries bundled).
Takes the manifest generated by winapp init --sparse and produces a signed .msix identity package.
winapp pack normally requires an input folder. For sparse packaging, if the input argument is an appxmanifest.xml (rather than a folder) and it contains AllowExternalContent="true", the command packages just the manifest itself — no folder required. This simplifies the sparse flow since the identity package typically only needs the manifest.
If a folder is provided instead, the existing behavior applies: everything in the folder is included in the .msix.
Options (existing, reused as-is):
<input> — Path to an appxmanifest.xml or a folder (for sparse, passing the manifest directly is preferred)
--output <path> — Output .msix path (default: <PackageName>.identity.msix in cwd for sparse)
--cert <path> — Path to signing certificate (.pfx)
--cert-password <password> — Certificate password (default: "password")
Signing only happens when --cert is provided. No cert = no signing.
Workflow (manifest input — sparse):
- Detect that the input is an
appxmanifest.xml file
- Parse it and confirm
AllowExternalContent="true" (sparse mode)
- Create a temporary staging directory containing only the manifest
- Build
.msix using MakeAppx.exe pack /o /d "<staging-dir>" /nv /p "<output>.msix"
- Sign the
.msix (if --cert or --generate-cert provided)
- Print output path, identity info, and next steps:
- "Run
winapp embed-identity <exe> to connect your exe to this identity package"
- "Include the
.msix in your installer and register with: Add-AppxPackage -Path <msix> -ExternalLocation <install-dir>"
Workflow (folder input — existing behavior):
- Parse the manifest in the folder to extract identity info
- Package the entire folder contents into the
.msix
- If
AllowExternalContent="true" is detected, emit warnings for any non-manifest content:
- Assets detected: If image files (
.png, .jpg, .ico) are found, warn: "Warning: Assets found in package folder. For sparse packages, assets should be deployed at the external location alongside your application, not inside the .msix."
- Binaries detected: If executable/library files (
.exe, .dll, .so) are found, warn: "Warning: Binaries found in package folder. Sparse packages are identity-only — application binaries should not be included in the .msix."
- These are warnings only, not errors — the pack operation still proceeds
4. winapp embed-identity <exe|xml>
MS docs step 3: Add identity metadata to desktop application manifests.
Embeds the <msix> element so Windows connects the application to the identity package at runtime.
Supports two modes based on the argument type:
- EXE mode (
winapp embed-identity myapp.exe): Embeds the <msix> element directly into the exe's side-by-side (fusion) manifest using mt.exe.
- XML mode (
winapp embed-identity myapp.exe.manifest): Updates an external SxS manifest XML file by inserting or replacing the <msix> element. Useful when the manifest is maintained as a separate file (e.g., checked into source control) rather than embedded in the binary.
The command auto-detects the mode from the file extension (.exe → EXE mode, .xml/.manifest → XML mode).
Arguments:
target (required) — Path to the .exe or .xml/.manifest file to update
Options:
--manifest <path> — Path to appxmanifest.xml to read identity from (default: ./appxmanifest.xml)
Workflow (EXE mode):
- Parse the appxmanifest to extract identity info (packageName, publisher, applicationId)
- Embed the
<msix> element into the exe's SxS manifest using mt.exe
- Print confirmation with the embedded identity values
Workflow (XML mode):
- Parse the appxmanifest to extract identity info (packageName, publisher, applicationId)
- Read the target XML/manifest file
- Insert or replace the
<msix> element in the XML
- Write the updated file back
- Print confirmation with the embedded identity values and a reminder to rebuild with the updated manifest
What the CLI Does NOT Do
- Register/unregister — that's the installer's job (
Add-AppxPackage -Path <msix> -ExternalLocation <dir>)
- Replace
create-debug-identity — stays as-is for the debug workflow
- Handle per-machine vs per-user — installer's concern
Documentation
The following documentation should be created or updated:
New Guide: docs/guides/sparse.md
A step-by-step guide for sparse packaging, covering:
- Overview — What sparse packages are and when to use them (identity for unpackaged apps)
- Prerequisites — Windows 10 2004+ (build 19041), developer certificate or trusted cert
- Walkthrough — End-to-end steps using the CLI:
winapp init --exe <path> --sparse — generate the manifest
winapp pack — build the identity .msix
winapp embed-identity <exe> — embed identity into the app
- Register with
Add-AppxPackage -ExternalLocation
- Asset handling — Clarify that assets are resolved from the external location, not the MSIX
- Installer integration — How to register/unregister the sparse package in an installer (NSIS, WiX, Inno Setup examples)
- Troubleshooting — Common issues (identity not found, assets not loading, signing errors)
Updates to Existing Docs
docs/usage.md — Add a "Sparse Packaging" section with a brief description and link to the full guide
Sample: samples/sparse-app/
A minimal WPF sample demonstrating the full sparse packaging workflow, including an Inno Setup installer for end-to-end distribution. Structure:
samples/sparse-app/
├── README.md # Step-by-step instructions to build, package, and install
├── appxmanifest.xml # Pre-generated sparse manifest (AllowExternalContent, win32App)
├── sparse-app.csproj # WPF project file
├── App.xaml # WPF application definition
├── App.xaml.cs
├── MainWindow.xaml # Main window — displays package identity status
├── MainWindow.xaml.cs
├── app.manifest # SxS manifest with <msix> element (for XML-mode example)
├── installer/
│ └── setup.iss # Inno Setup script — installs app + registers sparse MSIX
└── Assets/
├── StoreLogo.png
├── Square150x150Logo.png
├── Square44x44Logo.png
└── Wide310x150Logo.png
README.md should walk through:
- Build the app:
dotnet build
- Initialize sparse manifest:
winapp init --exe ./bin/Debug/net8.0-windows/sparse-app.exe --sparse --use-defaults
- Generate a dev cert:
winapp cert generate
- Pack the identity MSIX:
winapp pack --cert dev.pfx
- Embed identity:
winapp embed-identity ./bin/Debug/net8.0-windows/sparse-app.exe
- Register (dev):
Add-AppxPackage -Path sparse-app.identity.msix -ExternalLocation (Resolve-Path ./bin/Debug/net8.0-windows)
- Run the app and verify identity is displayed in the window
- Cleanup:
Remove-AppxPackage <full-package-name>
- (Optional) Build installer: Compile the Inno Setup script to produce a setup.exe that installs the app, deploys the
.msix, and registers the sparse package automatically
MainWindow.xaml.cs should:
- Call
Windows.ApplicationModel.Package.Current to query identity
- Display the package family name in the window on success
- Display "No package identity" if running without registration
installer/setup.iss should:
- Install the WPF app binaries and assets to
{app} directory
- Copy the
.msix identity package alongside the app
- Run
Add-AppxPackage -Path <msix> -ExternalLocation <install-dir> as a post-install step
- Run
Remove-AppxPackage on uninstall
- This demonstrates the full end-to-end production flow: build → package → install → identity available
This gives developers a copy-paste-ready starting point and validates the complete workflow from development through distribution.
Context
The winapp CLI's sparse packaging support is currently limited to the
create-debug-identitycommand, which is designed for developer-time debugging only (requires Developer Mode, registers a raw manifest). There is no support for the production sparse packaging workflow described in the MS docs.This issue proposes adding sparse packaging support to the winapp CLI, covering the first 3 steps of the MS docs workflow — creating the manifest, building/signing the
.msix, and embedding identity into the exe. Steps 4–5 (registration/unregistration) are the installer's responsibility, not the CLI's.Gap Analysis
.msixidentity package creationpackagedClassicApp(should bewin32App),MinVersiontoo low, noProcessorArchitecture="neutral"<msix>identity into exe outside debug flowEmbedMsixIdentityToExeAsyncexists but is internal to debug flowProposed Commands
These map directly to the first 3 steps of the MS docs workflow.
1. Fix Existing Sparse Template & Code
Update the existing sparse template and related code to match the MS docs:
RuntimeBehavior="packagedClassicApp"→"win32App"(per schema docs:packagedClassicApp= Desktop Bridge/Centennial with virtualization;win32App= plain Win32, correct for sparse packages)MinVersion="10.0.18362.0"→"10.0.19041.0"(AllowExternalContentanduap10require 19041+)ProcessorArchitecture="neutral"to<Identity>(identity-only packages have no binaries)2.
winapp init --exe <exe> --sparseMS docs step 1: Create a package manifest for the identity package.
The existing
winapp initcommand is extended with--exeand--sparseflags. When both are provided, the command generates a sparse-specific manifest. The--exeargument provides the path to the application executable, and--sparsesignals the sparse packaging flow.Infers defaults from the exe using
FileVersionInfo. Interactive prompts allow the user to override inferred values, with--use-defaults/--no-promptfor non-interactive/CI use.Validation:
--exeis only valid when combined with--sparse. If--exeis provided without--sparse, the command should exit with an error:"--exe requires --sparse. Use 'winapp init' without --exe for full package initialization."SDK installation: The sparse init flow skips SDK/package installation entirely. Since sparse packages are identity-only, there are no SDK dependencies to download — only the manifest and assets are generated.
Inference from exe (using
FileVersionInfo.GetVersionInfo):FileDescription, then filename without extensionCompanyNameCN={username}Comments, thenFileDescriptionFileVersion1.0.0.0Interactive prompts (unless
--use-defaults):Shows package name, publisher CN, version, and description with inferred defaults — user can accept or override. No executable prompt (already provided as argument).
Options:
--exe <path>— Path to the application executable (required for sparse)--sparse— Generate a sparse identity manifest instead of a full package manifest--use-defaults/--no-prompt— Skip all prompts, use inferred/default values--name <name>— Override package name--publisher <CN>— Override publisher CN--output-dir <path>— Where to write manifest + assets (default: exe's directory)Workflow:
--use-defaults)appxmanifest.xmlfrom the sparse template with values filled in, placed in the exe's directory (or--output-dir)Assets/subdirectory relative to the exewinapp packto create the .msix")3.
winapp packMS docs step 2: Build and sign the identity package.
The existing
winapp packcommand is reused. It detects whether the manifest describes a sparse package by checking forAllowExternalContent="true"in the manifest. When a sparse manifest is detected, the command automatically adjusts its behavior (e.g., identity-only packaging, no binaries bundled).Takes the manifest generated by
winapp init --sparseand produces a signed.msixidentity package.winapp packnormally requires an input folder. For sparse packaging, if the input argument is anappxmanifest.xml(rather than a folder) and it containsAllowExternalContent="true", the command packages just the manifest itself — no folder required. This simplifies the sparse flow since the identity package typically only needs the manifest.If a folder is provided instead, the existing behavior applies: everything in the folder is included in the
.msix.Options (existing, reused as-is):
<input>— Path to anappxmanifest.xmlor a folder (for sparse, passing the manifest directly is preferred)--output <path>— Output.msixpath (default:<PackageName>.identity.msixin cwd for sparse)--cert <path>— Path to signing certificate (.pfx)--cert-password <password>— Certificate password (default:"password")Signing only happens when
--certis provided. No cert = no signing.Workflow (manifest input — sparse):
appxmanifest.xmlfileAllowExternalContent="true"(sparse mode).msixusingMakeAppx.exe pack /o /d "<staging-dir>" /nv /p "<output>.msix".msix(if--certor--generate-certprovided)winapp embed-identity <exe>to connect your exe to this identity package".msixin your installer and register with:Add-AppxPackage -Path <msix> -ExternalLocation <install-dir>"Workflow (folder input — existing behavior):
.msixAllowExternalContent="true"is detected, emit warnings for any non-manifest content:.png,.jpg,.ico) are found, warn:"Warning: Assets found in package folder. For sparse packages, assets should be deployed at the external location alongside your application, not inside the .msix.".exe,.dll,.so) are found, warn:"Warning: Binaries found in package folder. Sparse packages are identity-only — application binaries should not be included in the .msix."4.
winapp embed-identity <exe|xml>MS docs step 3: Add identity metadata to desktop application manifests.
Embeds the
<msix>element so Windows connects the application to the identity package at runtime.Supports two modes based on the argument type:
winapp embed-identity myapp.exe): Embeds the<msix>element directly into the exe's side-by-side (fusion) manifest usingmt.exe.winapp embed-identity myapp.exe.manifest): Updates an external SxS manifest XML file by inserting or replacing the<msix>element. Useful when the manifest is maintained as a separate file (e.g., checked into source control) rather than embedded in the binary.The command auto-detects the mode from the file extension (
.exe→ EXE mode,.xml/.manifest→ XML mode).Arguments:
target(required) — Path to the.exeor.xml/.manifestfile to updateOptions:
--manifest <path>— Path to appxmanifest.xml to read identity from (default:./appxmanifest.xml)Workflow (EXE mode):
<msix>element into the exe's SxS manifest usingmt.exeWorkflow (XML mode):
<msix>element in the XMLWhat the CLI Does NOT Do
Add-AppxPackage -Path <msix> -ExternalLocation <dir>)create-debug-identity— stays as-is for the debug workflowDocumentation
The following documentation should be created or updated:
New Guide:
docs/guides/sparse.mdA step-by-step guide for sparse packaging, covering:
winapp init --exe <path> --sparse— generate the manifestwinapp pack— build the identity.msixwinapp embed-identity <exe>— embed identity into the appAdd-AppxPackage -ExternalLocationUpdates to Existing Docs
docs/usage.md— Add a "Sparse Packaging" section with a brief description and link to the full guideSample:
samples/sparse-app/A minimal WPF sample demonstrating the full sparse packaging workflow, including an Inno Setup installer for end-to-end distribution. Structure:
README.mdshould walk through:dotnet buildwinapp init --exe ./bin/Debug/net8.0-windows/sparse-app.exe --sparse --use-defaultswinapp cert generatewinapp pack --cert dev.pfxwinapp embed-identity ./bin/Debug/net8.0-windows/sparse-app.exeAdd-AppxPackage -Path sparse-app.identity.msix -ExternalLocation (Resolve-Path ./bin/Debug/net8.0-windows)Remove-AppxPackage <full-package-name>.msix, and registers the sparse package automaticallyMainWindow.xaml.csshould:Windows.ApplicationModel.Package.Currentto query identityinstaller/setup.issshould:{app}directory.msixidentity package alongside the appAdd-AppxPackage -Path <msix> -ExternalLocation <install-dir>as a post-install stepRemove-AppxPackageon uninstallThis gives developers a copy-paste-ready starting point and validates the complete workflow from development through distribution.