Host your own WinGet source and keep your apps up-to-date. Based on winget-extras.
- Automated package updates, powered by Anthelion and Komac. New versions are submitted as pull requests
- Automated validation, using GitHub-hosted Windows runners (x64 and arm64) with Attack Surface Analyzer reports, screenshots, and installer logs
- Serve the source from GitHub, Azure Blob Storage, or any S3-compatible bucket (AWS S3, Cloudflare R2, MinIO...)
- Sign the source package with Azure Trusted Signing, or an Azure Key Vault certificate via AzureSignTool
- Create a repository from this template
- Update the
Identity,Properties, and display names inindex/AppxManifest.xml. ThePublishermust exactly match the subject of your signing certificate. Optionally replace the logos inindex/Assets - Configure a signing backend - WinGet requires preindexed sources to be signed by a certificate the client trusts
- Configure a storage backend (defaults to GitHub, no setup required)
- Add packages
- Add the source on your machines:
winget source add --name selfhost --type Microsoft.PreIndexed.Package --arg <cache URL for your storage backend>Both backends authenticate to Azure with OIDC:
- Create an app registration
- Add a federated credential for the repository (entity type
Branch, branchmain) - Create these repository variables:
| Variable | Value |
|---|---|
AZURE_TENANT_ID |
App registration tenant ID |
AZURE_CLIENT_ID |
App registration client ID |
AZURE_SUBSCRIPTION_ID |
Subscription ID (only needed for Azure Blob Storage) |
- Set up an Azure Trusted Signing certificate profile (currently closed to new users)
- Assign Trusted Signing Certificate Profile Signer to the app registration
- Create these repository variables:
| Variable | Value |
|---|---|
SIGNING_BACKEND |
trusted-signing |
AZURE_SIGNING_ENDPOINT |
Trusted Signing endpoint, like https://eus.codesigning.azure.net/ |
AZURE_SIGNING_ACCOUNT |
Trusted Signing account name |
AZURE_SIGNING_PROFILE |
Certificate profile name |
- Import or generate a code signing certificate in an Azure Key Vault
- Assign the app registration the
Key Vault Crypto UserandKey Vault Certificate Userroles (or equivalent access policies) on the vault - Create these repository variables:
| Variable | Value |
|---|---|
SIGNING_BACKEND |
azuresigntool |
KEY_VAULT_URL |
Key Vault URL, like https://myvault.vault.azure.net/ |
KEY_VAULT_CERT |
Certificate name in the vault |
Note
If the certificate is self-signed, it must be deployed to the Local Machine\Trusted People or Trusted Root Certification Authorities store on client machines.
No configuration needed. The generated cache directory is committed back to the repository and served from raw.githubusercontent.com:
winget source add --name selfhost --type Microsoft.PreIndexed.Package --arg https://github.com/OWNER/REPO/raw/main/cacheBest for small sources - every rebuild adds the source package to the repository's history, and GitHub raw serving isn't a CDN.
- Create a storage account and a blob container with anonymous read access for blobs (or front it with Azure CDN / Front Door)
- Assign the app registration the
Storage Blob Data Contributorrole on the container - Create these repository variables:
| Variable | Value |
|---|---|
STORAGE_BACKEND |
azure-blob |
AZURE_STORAGE_ACCOUNT |
Storage account name |
AZURE_STORAGE_CONTAINER |
Container name (defaults to cache) |
AZURE_SUBSCRIPTION_ID |
Subscription containing the storage account |
winget source add --name selfhost --type Microsoft.PreIndexed.Package --arg https://ACCOUNT.blob.core.windows.net/CONTAINERThe cache is synced with rclone, so any S3-compatible provider works. Serve the bucket over HTTPS (e.g. an R2 custom domain, CloudFront, or public bucket hosting).
- Create a bucket and an access key with write permission
- Create these repository variables and secrets:
| Variable | Value |
|---|---|
STORAGE_BACKEND |
s3 |
S3_BUCKET |
Bucket name |
S3_ENDPOINT |
Endpoint URL for non-AWS providers, like https://<ACCOUNT_ID>.r2.cloudflarestorage.com |
S3_PROVIDER |
rclone provider name, like AWS, Cloudflare, or Minio (defaults to AWS) |
| Secret | Value |
|---|---|
AWS_ACCESS_KEY_ID |
Access key ID |
AWS_SECRET_ACCESS_KEY |
Secret access key |
winget source add --name selfhost --type Microsoft.PreIndexed.Package --arg https://your-domain.example.com/cacheAdd standard WinGet manifests under manifests/<first letter>/<Publisher>/<Package>/<version>/, the same layout as winget-pkgs. Font packages can live in an optional fonts/ directory with the same layout.
The easiest way to author or update a manifest is the Anthelion fork of Komac, which can target your own repository instead of microsoft/winget-pkgs. Download a binary for your platform from unpn-org/Komac releases, then point it at your repo with these environment variables (the same ones the update workflow uses):
| Variable | Value |
|---|---|
GITHUB_TOKEN |
A token with contents and pull-requests write scope |
KOMAC_GITHUB_OWNER |
Target repository owner (your account, not microsoft) |
KOMAC_GITHUB_REPO |
Target repository name |
KOMAC_FORK_OWNER |
Where Komac pushes its branch (usually the same owner) |
# add a new package
komac new Publisher.Package
# update an existing package to a new version
komac update Publisher.Package --version 1.2.3 --urls https://example.com/setup-1.2.3.exeKomac downloads the installers, fills in the hashes and metadata, and opens a pull request against $KOMAC_GITHUB_OWNER/$KOMAC_GITHUB_REPO instead of the default microsoft/winget-pkgs. Add --dry-run --output <dir> to write the manifests locally without opening a pull request. wingetcreate also works if you prefer to author manifests by hand.
On merge to main, the publish workflow merges the manifests, resolves winget-pkgs dependencies, builds the preindexed source package with IndexCreationTool, signs it, and uploads it to your storage backend.
Add a shard at shards/json/<PackageIdentifier>.json describing how to detect new versions, and the update workflow runs Anthelion on a schedule to open a pull request via Komac when a new version is found. See Anthelion's CONTRIBUTING.md for the shard format, the available strategies, and how to test a shard locally with bun test:shard <PackageIdentifier> --dry-run. Suffix a shard filename with .disabled to skip it.
Anthelion authenticates as a GitHub App so its pull requests trigger CI:
- Create a GitHub App with
ContentsandPull requestswrite permissions, and install it on your repository - Create the
GH_CLIENT_IDrepository variable (the app's client ID) and theGH_PRIVATE_KEYrepository secret (a private key for the app)
Changed manifests in pull requests are validated automatically by the validate workflow: each installer is installed on a GitHub-hosted runner matching its architecture, with logs, an Attack Surface Analyzer SARIF report, and a desktop screenshot attached to the job summary. Limitations:
- Interactive installation is only tested if silent installation fails
- The
armarchitecture (32-bit ARM) is not tested, because arm32 GitHub runners aren't available
You can also validate manually with SandboxTest from winget-pkgs:
git clone https://github.com/microsoft/winget-pkgs
cd winget-pkgs\Tools
.\SandboxTest.ps1 -Manifest ..\..\REPO\manifests\p\Publisher\Package\1.0\The source can be deployed to managed devices via the EnableAdditionalSources policy.
The Identifier is the package family name of your source package: the Identity Name from index/AppxManifest.xml plus a hash of the publisher, e.g. Winget.Source.Selfhost_ggk937h18f62r. Get it from a machine with the source installed via Get-AppxPackage in PowerShell.
In the Settings Catalog, enable Administrative Templates > Windows Components > Desktop App Installer > Enable App Installer Additional Sources and set the value to:
{"Arg":"<cache URL>","Data":"<Identifier>","Explicit":false,"Identifier":"<Identifier>","Name":"selfhost","TrustLevel":["Trusted"],"Type":"Microsoft.PreIndexed.Package"}Enable Computer Configuration > Administrative Templates > Windows Components > Desktop App Installer > Enable App Installer Additional Sources and set the same value as above.
Inspired by ScoopInstaller/Extras. See a real deployment at pl4nty/winget-extras.