Build and release
Artifacts and channels
Section titled “Artifacts and channels”The signing boundary is defined by ADR-0006: the Store applies the final Store-channel signature, while GitHub/WinGet use publicly trusted cloud-HSM signing. Production private keys are never exported into GitHub secrets. PFX handling has been removed from Release and a protected DigiCert Binary Signing simple-signing path is wired; the 1.0 signing gate remains pending until real-certificate evidence exists.
The public Code signing policy records the SignPath Foundation application status, release roles, provenance boundary, privacy statement, and the separate Partner Center MSIX identity. SignPath Foundation is not connected to the production workflow until the application is accepted and the MSIX identity decision is reviewed.
Releases contain x64 and ARM64 desktop/CLI programs, MSIX packages, standalone executables, SHA-256 files, per-package audit JSON, CycloneDX JSON SBOMs, GitHub build provenance, release notes, and a matching documentation snapshot. GitHub Release is the first public channel. WinGet uses planned ID ZiCode.ZiFile; Microsoft Store uses MSIX.
A v* tag builds both architectures. A stable tag such as v1.0.0 then signs the desktop/CLI/Worker EXEs, Explorer DLL, and MSIX in the protected production-signing Environment. The post-signing gate requires one Publisher, a valid operating-system trust chain, and a timestamp for every file, then regenerates package audits, SHA-256 files, and provenance. Publishing consumes only signed-windows-* artifacts before generating the WinGet 1.12 candidate and stable GitHub Release. Hyphenated stage tags such as v0.1.0-alpha.1, v0.1.0-beta.1, and v1.0.0-rc.1 still build both architectures and publish unsigned development EXEs, MSIX packages, audits, checksums, SBOMs, and build provenance to a GitHub pre-release; they must never be submitted to WinGet or Store. The candidate uses the community repository path and is blocked before upload unless its versioned URLs, architectures, and SHA-256 values exactly match both signed local MSIX files. Official winget validate and repository acceptance remain separate post-release evidence. A manual run may select signing_provider=none for private unsigned validation when official credentials are unavailable.
A manual run may select digicert-stm for a complete signing rehearsal. Build-time repository Variables are ZIFILE_MSIX_IDENTITY, ZIFILE_MSIX_PUBLISHER, and ZIFILE_MSIX_PUBLISHER_DISPLAY_NAME; the protected Environment provides Variables SM_HOST and SM_KEYPAIR_ALIAS plus Secrets SM_API_KEY, SM_CLIENT_CERT_FILE_B64, and SM_CLIENT_CERT_PASSWORD. The client-authentication certificate only accesses the signing service, is written to the runner temporary directory, and is removed after use; the code-signing private key remains in the cloud HSM.
A tag or digicert-stm rehearsal runs Test-PartnerCenterIdentity.ps1 -RequireConfigured before compilation. Name, Publisher, and Publisher Display Name must appear together; Name must fit the MSIX 3–50 alphanumeric/period/dash boundary, Publisher must be a valid X.500 distinguished name, and .Dev or the unsigned OID is rejected. Copy all three values exactly from Partner Center. Preflight proves structure and source constraints, not account ownership or name reservation.
See Production signing operations for provisioning, approval, rotation, emergency stop, revocation, and the minimum evidence set. Signing jobs serialize releases per architecture, have a hard 30-minute timeout, and use job-level least privilege. A timeout is a failure and cannot bypass post-signing verification.
Each MSIX is unpacked and audited for identity, publisher, publisher display name, version, minimum Windows build, PE architecture of desktop/CLI/Worker/Explorer DLL, associations, zifile.exe alias, absence of sensitive files and ZIP artifacts, and signature state. The audit also opens the desktop EXE as resource data without executing the target program, validates all 16/24/32/48/256 GROUP_ICON/ICON frames, and records embedded_desktop_icon. Audit output does not replace real install, upgrade, uninstall, or WACK testing.
tests/smoke/msix-lifecycle.ps1 provides an explicit machine gate for trusted signed baseline and upgrade packages. It audits both packages, refuses any pre-existing installation with the same identity, then checks installation, the packaged CLI, version upgrade, Reset-AppxPackage, and guaranteed cleanup with JSON evidence. Microsoft defines Reset as restoring initial configuration, so the script does not label it as a data-preserving Repair operation; formal Repair remains a separate gate. See the Appx module and Reset-AppxPackage.
The manual Trusted MSIX lifecycle workflow accepts two signed Release run IDs, reads the default package and audit JSON from each signed-windows-x64 artifact, and executes the same gate on a clean Windows Runner with 30-day evidence retention. It deliberately rejects the pre-signing windows-x64 artifacts. Real ARM64 installation remains a physical ARM64 Windows gate.
Windows Release uses pinned Rust 1.93.0, Cargo.lock, a single Cargo job, and MSVC /Brepro. Each x64/ARM64 test-and-package job has a 90-minute hard timeout; a timeout fails the job and cannot bypass package audit or artifact upload. The separate double-build gate compares five raw PE files; see Reproducible Windows builds.
Before tagging, the Release workflow can be run manually. It accepts no second version input and uses the workspace version from Cargo.toml. none saves unsigned dual-architecture artifacts and SBOMs; digicert-stm additionally enters the protected environment and saves signed artifacts plus signature audits. Both skip public publishing. Use precise incrementing stage tags such as v0.1.0-alpha.1, v0.1.0-beta.1, and v1.0.0-rc.1 for milestone pre-releases; use an unhyphenated v1.0.0 only after all formal gates pass. Normal CI and Release enforce version consistency, and a tag must exactly match v<workspace-version>. See Public contracts and version policy for the CLI, core-provider, and IPC compatibility boundaries. Manual validation and hyphenated stage tags also package the -accessible candidate; stable tags continue to publish only the default UI until accessibility and physical-architecture gates pass.
Normal CI also requires exactly one [Unreleased] section in CHANGELOG.md. Before a tag can build, its changes must be cut into ## [<workspace-version>] - YYYY-MM-DD with at least one Keep a Changelog category and one non-placeholder entry. A missing version heading, invalid date, empty section, or remaining TODO/TBD fails before compilation. Manual Release validates only the [Unreleased] structure so it remains useful as a pre-tag rehearsal.
release/readiness.json tracks the 11 release-blocking gates for 1.0. Normal CI and prerelease rehearsals validate its structure and evidence format; a stable tag without a hyphen must pass Test-ReleaseReadiness.ps1 -RequireReleaseReady, so any pending gate rejects publication before build. The current status is candidate; see 1.0 release readiness.
Stable releases require unit, interoperability, security, performance, install/upgrade, and documentation checks plus synchronized Stage logs. Test-WackReadiness.ps1 first checks the current interactive administrator session, WACK installation, host/package architecture, schema-v2 audit and package hash, the exact Partner Center Identity/Publisher/Publisher Display Name tuple, build 19041, forbidden files, and matching Valid signatures without installing or executing the package. It can persist failure evidence but does not run WACK. Certification still requires WACK in the current user’s interactive administrator session.