`winget install unom.PunktfunkHost` after adding the source, and `winget upgrade`
from then on. Windows had no update path at all before this — no self-update, no
package manager — so keeping a host current meant noticing a release and
re-running an installer by hand.
Ships the manifest trio in winget-pkgs' own format (so submitting upstream later
is a copy, not a rewrite) plus a release-time generator that substitutes only
version, URL, hash and release-notes link. Everything reviewable — the silent
switches, the agreements, the installation notes — stays in the checked-in
templates rather than buried in a generator.
Silent installs deliberately take the SAME task defaults the wizard shows: a
per-channel default is a support trap. The disclosures the wizard puts on screen
travel as manifest Agreements instead, shown before install and requiring
acceptance — VB-Audio's bundling grant wants the user to see VB-CABLE's origin
and donationware status, and a silent install shows them nothing otherwise. The
console password can only be pointed at, never printed: it is generated per
install and the notes are fixed at publish time.
Self-hosted rather than the community repo, which gates on Defender/SmartScreen
validation the self-signed installer cert would not clear today. The source is
three endpoints — /information, /manifestSearch, /packageManifests/{id}. The
reference implementation's other twenty are its admin API for mutating a
CosmosDB; a catalogue generated at release time has nothing to mutate.
It runs on unom-1 as a stock bun image with the two .mjs files bind-mounted, the
same shape as the flatpak server, so there is no image to build or pull. The
catalogue is derived from the RELEASES rather than local files: winget resolves
--version and upgrade against the version list, so a source that only knew the
newest release could neither pin an older one nor show an upgrade path from it.
That also leaves no state to drift — re-running the build reproduces it exactly.
NormalizedPackageNameAndPublisher is declared unsupported on purpose. winget
derives it client-side with its own normalization, and a near-miss silently
mis-correlates an installed host; ProductCode is exact and Inno gives us one.
28 checks drive the handler directly, and CI gates on them before anything
reaches the box — a wrong response shape does not fail loudly, it just makes
winget report "no package found".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
winget manifests — Windows host
The reviewed source of truth for the unom.PunktfunkHost winget package. Everything except
PackageVersion / InstallerUrl / InstallerSha256 / ReleaseNotesUrl is edited here;
scripts/ci/winget-manifest.ps1 only substitutes those four per release, so the switches,
agreements and installation notes stay under normal code review.
| File | Purpose |
|---|---|
unom.PunktfunkHost.yaml |
Version manifest — ties the other two together. |
unom.PunktfunkHost.installer.yaml |
Installer type, scope, silent switches, ProductCode, URL + hash. |
unom.PunktfunkHost.locale.en-US.yaml |
User-facing metadata, Agreements, InstallationNotes. |
Why these choices
InstallerType: inno,Scope: machine,ElevationRequirement: elevatesSelf. The host registers a SYSTEM service, installs drivers and opens firewall ports;PrivilegesRequired=adminin the.issmeans Setup raises its own UAC prompt. There is no per-user scope.ProductCode: {7C9E6A52-…}_is1— Inno's ARP key is<AppId>_is1. This is what correlates an installed host with the package forwinget list/winget upgrade. It must trackAppIdinpackaging/windows/punktfunk-host.iss— if that GUID ever changes, change it here too or upgrades silently stop being detected.interactiveis inInstallModes.winget install unom.PunktfunkHost --interactiveruns the full existing wizard: every task checkbox, the web-console password page, the VB-CABLE notice. Nothing about the installer changes to support it.- No
/MERGETASKSin the silent switches. A silent install deliberately takes the same task defaults the wizard shows, so the product does not differ by install channel — a per-channel default is a support trap ("it works when I install it by hand"). The disclosures the wizard puts on screen are carried byAgreementsinstead, which winget shows before install and requires the user to accept. UpgradeBehavior: install— Inno upgrades in place (UsePreviousAppDir=yes). Uninstalling first would run the[UninstallRun]service + driver teardown between versions.
Opting out of individual tasks
Inno's /MERGETASKS takes ! prefixes to deselect a default-checked task. Use --override
(replaces winget's switches) rather than --custom (appends — you would end up with two
/MERGETASKS on one command line):
winget install unom.PunktfunkHost --override "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /MERGETASKS=!gamestream"
Task names: installdriver, installgamepad, installaudiocable, installhdrlayer,
gamestream, allowpublicfw, startservice, trayicon.
Two installer behaviours that exist for this path
Both are in packaging/windows/punktfunk-host.iss and both also fix pre-existing bugs on the
plain double-click upgrade path:
InitializeSetupusesSuppressibleMsgBox, notMsgBox. A plainMsgBoxignores/SUPPRESSMSGBOXESand displays even under/VERYSILENT— an unattended install on a box that already runs Sunshine/Apollo would block on an invisible modal dialog. Suppressed it returnsIDNO, so that install aborts (Setup exits non-zero) rather than proceeding into the unsupported dual-host state.GamestreamParamis fresh-install-only. On an upgrade the flag is omitted entirely, whichservice installreads as "keep host.env as-is". Passing an explicit on/off would rewritePUNKTFUNK_HOST_CMDwhenever it still holds either canonical value — so a silent upgrade, where no wizard carries the old choice forward, would flip a user's GameStream setting with nothing on screen.PublicFwParamis fresh-install-only too, and--allow-public-networkis now tri-state (=on/=off/ absent → keep the recorded choice, resolved from thefw-allow-publicmarker inwindows/service.rs). This task is default-unchecked, so without the change a silent upgrade would have silently revoked a Public-network opt-in the user made once. The bare--allow-public-networkform still meansonfor existing scripts; a malformed value is a hard error rather than a fall-through, since a typo'd opt-out must never resolve to "keep Public open".
Release flow
.gitea/workflows/windows-host.yml runs on stable v* tags only, after the installer is
attached to the Gitea release — winget validates the URL and hash, so a manifest must never be
published ahead of its artifact:
scripts/ci/winget-manifest.ps1 -Version 0.19.2 `
-InstallerPath C:\t\out\punktfunk-host-setup-0.19.2.exe -OutDir C:\t\out\winget
The generated trio is attached to the same release. Canary builds are excluded: winget pins one
immutable artifact per version, so the rolling canary/ alias has nothing it could point at.
Validating a change
winget validate --manifest packaging\winget
winget install --manifest packaging\winget # local install from the manifest
For a throwaway check, winget-pkgs' Tools\SandboxTest.ps1 runs a manifest in Windows Sandbox.
Note the host needs a real GPU and installs drivers, so a Sandbox run exercises the manifest
(download, hash, switches, ARP correlation) rather than a working stream.
Publishing
Through our own REST source on unom-1 — see server/. It sits alongside the
docs (3220) and flatpak (3230) services, behind the same edge Caddy; windows-host.yml rebuilds and
ships its catalogue on every stable tag, so releasing is one pipeline with no manual step.
winget source add -n punktfunk https://winget.punktfunk.unom.io -t Microsoft.Rest # elevated, once
winget install unom.PunktfunkHost
These manifests stay in winget-pkgs' own format rather than a bespoke one, so submitting upstream
later is a copy, not a rewrite. Two things would need attention on that path: the signing note
below, and Agreements being verified-developers-only in the community repo.
Signing. The installer is currently signed with a self-signed cert (
CN=unom, subject == issuer) and ships a.cerusers import manually. winget does not sign anything; it downloads and runs the same binary, so SmartScreen behaves exactly as it does for a browser download. That is a pre-existing condition rather than something winget introduces — but the community repo (microsoft/winget-pkgs) gates on it via itsBinary-Validation-Error/Validation-Defender-Errorchecks, so a submission there needs a publicly-trusted cert (Azure Trusted Signing is the cheap path). A self-hosted source has no such gate.