The Windows signing docs still told users to import a certificate we no longer publish #238

Merged
enricobuehler merged 1 commits from worktree-docs-azure-signing-cert-import into main 2026-08-14 22:05:55 +00:00
7 changed files with 85 additions and 48 deletions
+11 -5
View File
@@ -70,12 +70,18 @@
# latest stable tag via scripts/ci/pf-version.ps1, run number climbs monotonically).
# Both arches share the version; artifacts are arch-suffixed (..._x64.msix / ..._arm64.msix).
#
# Signing (clients/windows/packaging/pack-msix.ps1): if the MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD
# Actions secrets are set (a real or shared code-signing .pfx whose subject DN == Publisher), the
# package is signed with them. Otherwise an ephemeral self-signed cert is generated and its public
# .cer is published next to the .msix (users import it to Trusted People before install).
# Signing (clients/windows/packaging/pack-msix.ps1), first match wins:
# 1. Azure Artifact Signing — what this workflow always takes, since the AZURE_CODESIGNING_*
# endpoint/account/profile are literals below and only the AZURE_TENANT_ID / AZURE_CLIENT_ID /
# AZURE_CLIENT_SECRET secrets are needed. Publicly trusted, so NO .cer is emitted or published
# and users import nothing. NOTE the Publisher DN is the Azure profile's verified subject, and
# MSIX identity is name + publisher: moving to it changed the package identity, so installs
# predating it need an uninstall, not an upgrade.
# 2. MSIX_CERT_PFX_B64 / MSIX_CERT_PASSWORD — the older self-signed .pfx, kept as a fallback.
# 3. an ephemeral self-signed cert. Modes 2 and 3 DO emit a .cer next to the .msix, which users
# would have to import into Trusted People before Windows will install the package.
#
# That fallback is for canary/CI ONLY. On a v* tag the pack script FAILS CLOSED — a missing secret
# Modes 2 and 3 are for canary/CI ONLY. On a v* tag the pack script FAILS CLOSED — a missing secret
# aborts the build instead of quietly shipping a release signed by a per-build throwaway cert that
# no one can pin. Nothing to opt into here: the script reads GITHUB_REF itself.
name: windows-client
+4 -2
View File
@@ -12,7 +12,8 @@ x64 Windows runner — `x86_64-pc-windows-msvc` builds natively, `aarch64-pc-win
cross-compiled (the x64 MSVC toolset ships the ARM64 cross compiler; since M10 nothing in the
package links FFmpeg, so neither arch needs a per-arch `FFMPEG_DIR` tree staged on the runner —
one less thing the ARM64 leg can be missing). Artifacts are arch-suffixed
(`..._x64.msix` / `..._arm64.msix`, each with its matching `.cer`); `pack-msix.ps1 -Arch x64|arm64`
(`..._x64.msix` / `..._arm64.msix`, plus a matching `.cer` only in the fallback signing modes 2 and 3
— Azure signing emits none); `pack-msix.ps1 -Arch x64|arm64`
stamps the manifest `ProcessorArchitecture` and names the output. See
[`windows-client.yml`](../../../.gitea/workflows/windows-client.yml) for the cross-build rationale.
@@ -52,7 +53,8 @@ low-level input hooks, WASAPI and SDL3.
MSIX requires a strictly 4-part numeric version. The workflow computes:
- `vX.Y.Z` tag → `X.Y.Z.0` (THE release; any `-rc`/`+meta` suffix is dropped for MSIX). Published to
the stable `latest/` alias and attached to the unified Gitea Release.
- `main` push / `workflow_dispatch``0.3.<run_number>.0` (canary, climbs by run number; `canary/` alias).
- `main` push / `workflow_dispatch``X.<Y+1>.<run_number>.0` (canary — the minor *after* the latest
`v*` tag, per `scripts/ci/pf-version.ps1`, climbing by run number; `canary/` alias).
## Signing & install
+26 -13
View File
@@ -103,29 +103,24 @@ See [packaging/flatpak](https://git.unom.io/unom/punktfunk/src/branch/main/packa
## Windows
The Windows client ships as a **signed MSIX** in the package registry. Builds use a self-signed
certificate, so you import that certificate once before Windows will install the package.
The Windows client ships as a **signed MSIX** in the package registry, signed with a publicly
trusted certificate so there is nothing to import and nothing to trust by hand. Download, install.
1. Download the package and its certificate. Each channel keeps one fixed URL, so these two lines
always fetch the current build — in PowerShell:
1. Download the package. Each channel keeps one fixed URL, so this line always fetches the current
build — in PowerShell:
```powershell
curl.exe -LO https://git.unom.io/api/packages/unom/generic/punktfunk-client-windows/latest/punktfunk-client-windows_x64.msix
curl.exe -LO https://git.unom.io/api/packages/unom/generic/punktfunk-client-windows/latest/punktfunk-client-windows_x64.cer
```
Swap `_x64` for `_arm64` on an Arm device, and `latest` for `canary` to track `main`. The same
two files are attached to every [release](https://git.unom.io/unom/punktfunk/releases), and every
file is attached to every [release](https://git.unom.io/unom/punktfunk/releases), and every
build is also kept under its own version on the
[packages page](https://git.unom.io/unom/-/packages) (generic group, `punktfunk-client-windows`).
2. **Trust the publisher certificate**, then install. The MSIX won't install until the certificate is
trusted — but it's the **same certificate for every release**, so this is genuinely one-time and
later updates need nothing. In an **admin** PowerShell:
2. Install it:
```powershell
# use the _arm64 files instead on an Arm device
Import-Certificate -FilePath .\punktfunk-client-windows_x64.cer `
-CertStoreLocation Cert:\LocalMachine\TrustedPeople
# use the _arm64 file instead on an Arm device
Add-AppxPackage .\punktfunk-client-windows_x64.msix
```
@@ -133,6 +128,10 @@ certificate, so you import that certificate once before Windows will install the
[Windows App Runtime 2.x](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads)
(the MSIX depends on `Microsoft.WindowsAppRuntime.2`), then re-run `Add-AppxPackage`.
Install from a signed-in desktop session. Over a remote, non-interactive session (SSH, an RMM
tool) `Add-AppxPackage` can fail with `0x80070005` when the Windows App Runtime it depends on is
in use and Windows can't restart the apps holding it.
3. Launch **Punktfunk** from the Start menu and pick your host. The package also adds a second
entry, **Punktfunk Console** — the same client as a controller-driven fullscreen interface for a
TV or HTPC — and the headless `punktfunk` command on your PATH.
@@ -223,7 +222,7 @@ but keeping them close is the least surprising. (Updating the **host** is its ow
| **Linux Flatpak** | `flatpak update --user io.unom.Punktfunk` — **without `sudo`** (see the [Flatpak section](#linux-desktop-flatpak)) |
| **Linux apt / dnf / pacman** | your normal `sudo apt upgrade` / `sudo dnf upgrade` / `sudo pacman -Syu`, or the app's own updater below |
| **Fedora Atomic (layered)** | `rpm-ostree upgrade` on its own is not enough — see the note below the table |
| **Windows MSIX** | no self-update — download the newer `.msix` as in [Windows](#windows) and re-run `Add-AppxPackage`. The certificate is the same every release, so you don't import it again |
| **Windows MSIX** | no self-update — download the newer `.msix` as in [Windows](#windows) and re-run `Add-AppxPackage`. Coming from **0.28.1 or earlier**, see the note below the table |
| **macOS `.dmg`** | download the newer `Punktfunk-<version>.dmg` and drag it over the copy in Applications |
| **iOS / iPadOS / tvOS** | TestFlight updates it |
| **Android** | Google Play updates it; if you sideloaded, download the APK again and install over it |
@@ -244,6 +243,20 @@ systemctl reboot
The client's own updater below runs exactly that dance for you, if you'd rather not remember it. (A
layered **host** has the same trap — [Updating](/docs/updating) covers it.)
**Windows, coming from 0.28.1 or earlier — uninstall first.** Those builds were signed with our own
self-signed certificate. The move to a publicly trusted one changes the package's *publisher*, and an
MSIX's identity is its name **plus** its publisher — so Windows treats the new package as a different
app rather than an update, and installing it leaves you with two **Punktfunk** entries. Remove the
old one first, then install the new `.msix` as above:
```powershell
Get-AppxPackage *Punktfunk* | Remove-AppxPackage
```
This is one-time; releases after that upgrade in place. Note that a packaged app's settings live
*inside* its package, so removing the old one also removes this client's identity and its paired
hosts — expect to [pair](/docs/pairing) again once. Nothing on the host side is affected.
### The Linux client can update itself
The native Linux client checks its own channel and can apply the update in place, so you don't have
+14 -14
View File
@@ -70,22 +70,22 @@ everything the installer puts on the machine, its optional tasks, the console pa
This is also the path for **canary** builds, which winget doesn't carry — see
[Release Channels](/docs/channels) for that download.
> **About the Unknown Publisher prompt.** The installer is signed with a self-signed certificate, so
> Windows warns before it runs — accepting the prompt is enough, nothing else is required. The winget
> route is no different: it downloads and runs that same installer. If you'd
> rather silence it, the matching **`punktfunk-host-windows_<ver>.cer`** is published next to the
> installer, and it's the **same certificate for every release**, so this is one-time. A self-signed
> certificate is its own root, so it has to go in both stores. In an **admin** PowerShell:
> **About signing.** The installer is signed with a publicly trusted certificate, so Windows shows
> the publisher by name at the UAC prompt — there is no Unknown Publisher warning and nothing to
> import. The winget route is no different: it downloads and runs that same installer.
>
> ```powershell
> Import-Certificate -FilePath .\punktfunk-host-windows_<ver>.cer `
> -CertStoreLocation Cert:\LocalMachine\Root
> Import-Certificate -FilePath .\punktfunk-host-windows_<ver>.cer `
> -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
> ```
> SmartScreen is a separate mechanism that builds reputation per publisher, so shortly after a new
> signing certificate starts being used it can still show *"Windows protected your PC"* on the first
> downloads — **More info → Run anyway**. It settles as installs accumulate.
>
> This is a different certificate from the one the bundled **drivers** are signed with — the
> installer imports that one for you.
> The bundled **drivers** are a separate matter — they carry their own certificate, and the installer
> imports that one for you. [Windows Host](/docs/windows-host#about-the-signatures) has the detail.
>
> Releases **0.28.1 and earlier** were signed with our own self-signed certificate, and older docs
> told you to import it. Nothing needs it any more: if you imported
> `punktfunk-host-windows_<ver>.cer` back then, you can remove it from `Cert:\LocalMachine\Root` and
> `Cert:\LocalMachine\TrustedPublisher` (look for the certificate issued to **unom**, thumbprint
> `CD1EFDEEEC9743AFC38F56C5AF30C5A3009BE941`).
## NixOS
+6 -3
View File
@@ -227,9 +227,12 @@ Three things are left on purpose:
component other apps may be using, so the Punktfunk uninstaller never touches it. Remove it
with its own uninstaller — `VBCABLE_Setup_x64.exe -u -h` — or the **VB-Audio Virtual Cable**
entry in Installed apps.
- **The publisher certificate**, if you imported it by hand to silence the Unknown Publisher prompt.
Remove it in `certlm.msc` under **Trusted Publishers** and **Trusted Root Certification
Authorities**. (This is *not* the driver certificate above, which the uninstaller does remove.)
- **The old publisher certificate**, if you imported it by hand to silence the Unknown Publisher
prompt on 0.28.1 or earlier. Releases since then are signed by a publicly trusted CA and never
needed it, so it is safe to drop: remove the certificate issued to **unom** (thumbprint
`CD1EFDEEEC9743AFC38F56C5AF30C5A3009BE941`) in `certlm.msc` under **Trusted Publishers** and
**Trusted Root Certification Authorities**. (This is *not* the driver certificate above, which the
uninstaller does remove.)
If you registered the winget source, drop it too — in an **admin** PowerShell, the same as
registering it:
+22 -10
View File
@@ -94,12 +94,14 @@ are `winget upgrade unom.PunktfunkHost` and removal is `winget uninstall unom.Pu
### About the signatures
Punktfunk signs with its own certificates rather than a publicly trusted one, so Windows warns about
an unknown publisher before setup runs. Accepting the prompt is enough. To silence it for good, the
matching **`punktfunk-host-windows_<ver>.cer`** is published next to the installer and it is the
same certificate for every release — the one-time import is in
[Install → Windows](/docs/install#windows). This applies to the winget path too: winget downloads
and runs that same installer.
Setup, and the Punktfunk executables it installs, are signed with a **publicly trusted** certificate,
so UAC names the publisher rather than warning about an unknown one, and there is nothing for you to
import. This applies to the winget path too: winget downloads and runs that same installer. (The
bundled third-party pieces keep whatever their own vendors shipped.)
Releases **0.28.1 and earlier** were signed with our own self-signed certificate, and the docs then
asked you to import `punktfunk-host-windows_<ver>.cer`. Current builds no longer produce or need it —
[Install → Windows](/docs/install#windows) shows how to remove it if you imported one.
The bundled drivers carry a **second, separate** self-signed certificate (`CN=punktfunk-driver`,
SHA-1 thumbprint `4B8493E7CD565758D335F8F4F05C5A7261A13E02`). The installer adds it to the machine's
@@ -114,12 +116,22 @@ punktfunk-host-setup-<ver>.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-
Keep `/SUPPRESSMSGBOXES`: without it an unattended run can stop on a message box nobody can see.
A silent run takes the same defaults as the wizard, so add or drop individual options with Inno's
`/MERGETASKS` — a bare name adds a task, a `!` prefix removes one. `/MERGETASKS="gamestream"` turns
on Moonlight compatibility; `/MERGETASKS="!trayicon"` skips the status icon. The task names are
`installdriver`, `installgamepad`, `installaudiocable`, `installhdrlayer`, `gamestream`,
A silent **first** install takes the same defaults as the wizard, so add or drop individual options
with Inno's `/MERGETASKS` — a bare name adds a task, a `!` prefix removes one.
`/MERGETASKS="gamestream"` turns on Moonlight compatibility; `/MERGETASKS="!trayicon"` skips the
status icon. The task names are `installdriver`, `installgamepad`, `installhdrlayer`, `gamestream`,
`allowpublicfw`, `startservice` and `trayicon`.
An **upgrade** is different: it reuses the choices the previous install recorded, not the defaults
above. That is usually what you want, but it means anything you once declined stays declined — and
if that includes `installgamepad`, the gamepad drivers are never updated alongside the host, which
surfaces later as a virtual controller games stop seeing. To force the full set on an upgrade, name
them (`/MERGETASKS` merges with the remembered set rather than replacing it):
```powershell
punktfunk-host-setup-<ver>.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /MERGETASKS="installdriver,installgamepad,installhdrlayer,startservice"
```
Two things to know before you script it:
- Setup **aborts with a non-zero exit code** if another Moonlight-compatible host — Sunshine, Apollo,
+2 -1
View File
@@ -303,7 +303,8 @@ pwsh -File packaging\windows\pack-host-installer.ps1 -Version 0.0.0-dev -TargetD
Push a `vX.Y.Z` tag — one tag releases every platform (see
[Release Channels](https://punktfunk.unom.io/docs/channels)). The workflow builds, signs, and
publishes `punktfunk-host-setup-X.Y.Z.exe` + the public `.cer`, refreshes the stable `latest/`
publishes `punktfunk-host-setup-X.Y.Z.exe` (no `.cer` — Azure signing is publicly trusted, and mode 2
or 3 would be needed to emit one), refreshes the stable `latest/`
alias, and attaches the installer to the unified Gitea Release. Main pushes publish rolling
`<next-minor>.<run>` **canary** builds (base derived from the latest stable tag by
`scripts/ci/pf-version.ps1`) to the `canary/` alias.