Een Windows-installer die niets meer doet dan installeren #1582

Merged
brenno merged 4 commits from feat/windows-installer into main 2026-08-19 17:29:58 +00:00
Owner

Sluit #1208. Uitpakken en zelf een snelkoppeling maken is een armoedige manier om iets te installeren; er is nu een gewone Windows-installer.

Wat er komt

make build-windows-installer verpakt de bundel die make build-windows net heeft neergezet (Inno Setup 6.3+, Windows only). Snelkoppeling in het startmenu en desgewenst op het bureaublad, de bestandsassociaties, en de-installeren via Programs and Features. Standaard machinebreed, met een uitwijk naar alleen-voor-mij voor wie geen beheerdersrechten heeft — vandaar HKA en {autopf}, zodat één sleutellijst allebei bedient.

Bestand Wat
packaging/windows/ocideck.iss het installatieprogramma, declaratief
scripts/build_windows_installer.sh bouwen + optioneel ondertekenen
Makefile make build-windows-installer
test/windows_packaging_test.dart de poort (15 controles)

Plaatsing wijkt bewust af van het plan in de issue: dat noemde windows/, maar packaging/linux/ bestond intussen als precedent. packaging/windows/ spiegelt dat en houdt Flutters eigen platformmap schoon.

De grens

Geen bijwerken, geen versiecontrole, geen release-feed, en op geen enkel moment het netwerk op. SECURITY.md belooft dat OciDeck niet naar huis belt en dat een verbetering je bereikt door de standaardtak op te halen en opnieuw te bouwen; een installer die zichzelf kan bijwerken ruilt die belofte in voor gemak. Dat staat niet op goede bedoelingen: de poort laat de bouw vallen zodra er een downloader óf een [Code]-sectie in het script verschijnt — dat laatste omdat dáár een versiecontrole zich zou verstoppen.

SECURITY.md zei "geen getekende installer die zichzelf bijwerkt". Dat blijft letterlijk waar, maar wie nu een installer aantreft verdient de verzoening erbij; die alinea is toegevoegd.

Ondertekenen: een schakelaar die uit staat

#1013 blijft gelden — sinds maart 2024 koopt geen enkel certificaattype directe SmartScreen-rust. Zonder certificaat bouwt het script een prima installer en waarschuwt luid; mét certificaat is het alles-of-niets, want een half-getekende installer ziet er betrouwbaar uit op precies de plek waar mensen kijken. Het accepteert uitsluitend een vingerafdruk uit de certificaatopslag — geen sleutelbestand, geen wachtwoord — zodat ondertekenmateriaal nooit een omgevingsvariabele of runner-geheim kan worden. Dezelfde lijn die notarize_macos.sh op macOS aanhoudt.

Afweging (bewaker) — één echte botsing

Vier van de vijf vragen zijn schoon: het .md-formaat wordt niet geraakt, er komt geen nieuwe partij in het draaipad (Inno Setup is een bouwgereedschap op de machine van de beheerder), het .iss is klein en leesbaar, en een geïnstalleerde OciDeck overleeft dit project zonder server. De vijfde niet.

Een installer vraagt om verhoging. Een ongetekende installer die om verhoging vraagt leert een slechtere gewoonte aan dan een ongetekende app die je zelf uitpakt — mensen zijn getraind installatievensters weg te klikken, en juist daar staat straks "Onbekende uitgever". Publiceren is dus een andere beslissing dan bouwen. Veiligheid gaat hier voor gemak, maar niet door de installer tegen te houden: de voorwaarde is dat hij, zodra hij gepubliceerd wordt, in SHA256SUMS staat en dus onder de minisign-handtekening over dat manifest valt. Die handtekening komt in de plaats van het afgewezen certificaat. Staat het commentaar bij in docs/BUILD.md; ik verander van gedachten zodra er wél een certificaat is.

Eén claim rechtgezet

Ik schreef eerst dat geen CI-runner een installer kan bouwen. Onjuist: de forge heeft geen Windows-machine, maar .github/workflows/release.yml op de spiegel bouwt bij elke v*-tag de Windows-zip die de forge terughaalt. De installer in die lijn hangen is een open besluit, geen onmogelijkheid — het vraagt Inno Setup gepind op de runner (windows-latest levert het sinds Server 2025 niet meer mee) en de keuze over een tweede Windows-artefact. De packager is daarom bash en geen make-doel: die job merkt zelf op dat make er niet betrouwbaar staat.

Tot dat besluit valt is het handwerk en levert een release nog steeds alleen de zip. Dat staat nu ook zo in de changelog, zodat niemand naar een download zoekt die er niet is.

Toetsen

  • make check groen: 9.938 tests, dekking 87,1%, per-bestand-vloer 0.
  • make check-secrets (gitleaks + trufflehog, werkboom én historie): geen bevindingen. make sast (semgrep, lokale regels): 0 findings.
  • DAST (ZAP) niet gedraaid: deze wijziging raakt het geserveerde weboppervlak niet.
  • Waarom een poort in plaats van een gewone test: Inno Setup draait alleen op Windows, dus geen enkele testrun máákt een installer. test/windows_packaging_test.dart toetst offline wat wél kan roesten — precies de opzet van linux_packaging_test.dart voor .deb/.rpm/AppImage. Of de installer écht installeert, associeert en de-installeert blijft handwerk op Windows.
  • Mutatiegetoetst, want een groene poort die niets bewijst is erger dan geen poort: ProgID uit de pas, een [Code]-sectie erbij, een bestandslijst in plaats van de bundel, de ongetekend-waarschuwing weg, tijdstempeling laten vallen — alle vijf betrapt.
  • De packager echt gedraaid met een nagemaakte ISCC en signtool, vijf paden: geen bundel (faalt netjes), geen certificaat (bouwt + waarschuwt, exit 0), mét certificaat (tekent exe, dll's en de installer), één dll die weigert (héle bouw valt — ook binnen de lus), certificaat zonder signtool (faalt vóórdat er iets gebeurt).
  • De poort staat in REGISTRATION_TESTS en niet alleen in de volle suite: die draait pas ná de merge op linux-gate, en dan is drift geschiedenis in plaats van een bewerking. docs/CHECKS.md telde er vier; het zijn er nu vijf.

Documentatie

docs/BUILD.md (bouwroute + ondertekenen + publicatievoorwaarde), packaging/README.md (van Linux-only naar beide platformen), SECURITY.md (twee alinea's), docs/CHECKS.md (de telling), CHANGELOG.md.

Sluit #1208. Uitpakken en zelf een snelkoppeling maken is een armoedige manier om iets te installeren; er is nu een gewone Windows-installer. ## Wat er komt `make build-windows-installer` verpakt de bundel die `make build-windows` net heeft neergezet (Inno Setup 6.3+, Windows only). Snelkoppeling in het startmenu en desgewenst op het bureaublad, de bestandsassociaties, en de-installeren via *Programs and Features*. Standaard machinebreed, met een uitwijk naar alleen-voor-mij voor wie geen beheerdersrechten heeft — vandaar `HKA` en `{autopf}`, zodat één sleutellijst allebei bedient. | Bestand | Wat | | --- | --- | | `packaging/windows/ocideck.iss` | het installatieprogramma, declaratief | | `scripts/build_windows_installer.sh` | bouwen + optioneel ondertekenen | | `Makefile` | `make build-windows-installer` | | `test/windows_packaging_test.dart` | de poort (15 controles) | Plaatsing wijkt bewust af van het plan in de issue: dat noemde `windows/`, maar `packaging/linux/` bestond intussen als precedent. `packaging/windows/` spiegelt dat en houdt Flutters eigen platformmap schoon. ## De grens Geen bijwerken, geen versiecontrole, geen release-feed, en op geen enkel moment het netwerk op. `SECURITY.md` belooft dat OciDeck niet naar huis belt en dat een verbetering je bereikt door de standaardtak op te halen en opnieuw te bouwen; een installer die zichzelf kan bijwerken ruilt die belofte in voor gemak. Dat staat niet op goede bedoelingen: de poort laat de bouw vallen zodra er een downloader óf een `[Code]`-sectie in het script verschijnt — dat laatste omdat dáár een versiecontrole zich zou verstoppen. `SECURITY.md` zei "geen getekende installer die zichzelf bijwerkt". Dat blijft letterlijk waar, maar wie nu een installer aantreft verdient de verzoening erbij; die alinea is toegevoegd. ## Ondertekenen: een schakelaar die uit staat #1013 blijft gelden — sinds maart 2024 koopt geen enkel certificaattype directe SmartScreen-rust. Zonder certificaat bouwt het script een prima installer en waarschuwt luid; mét certificaat is het alles-of-niets, want een half-getekende installer ziet er betrouwbaar uit op precies de plek waar mensen kijken. Het accepteert **uitsluitend** een vingerafdruk uit de certificaatopslag — geen sleutelbestand, geen wachtwoord — zodat ondertekenmateriaal nooit een omgevingsvariabele of runner-geheim kan worden. Dezelfde lijn die `notarize_macos.sh` op macOS aanhoudt. ## Afweging (bewaker) — één echte botsing Vier van de vijf vragen zijn schoon: het `.md`-formaat wordt niet geraakt, er komt geen nieuwe partij in het draaipad (Inno Setup is een bouwgereedschap op de machine van de beheerder), het `.iss` is klein en leesbaar, en een geïnstalleerde OciDeck overleeft dit project zonder server. De vijfde niet. Een installer vraagt om verhoging. Een ongetekende installer die om verhoging vraagt leert een slechtere gewoonte aan dan een ongetekende app die je zelf uitpakt — mensen zijn getraind installatievensters weg te klikken, en juist daar staat straks "Onbekende uitgever". **Publiceren is dus een andere beslissing dan bouwen.** Veiligheid gaat hier voor gemak, maar niet door de installer tegen te houden: de voorwaarde is dat hij, zodra hij gepubliceerd wordt, in `SHA256SUMS` staat en dus onder de minisign-handtekening over dat manifest valt. Die handtekening komt in de plaats van het afgewezen certificaat. Staat het commentaar bij in `docs/BUILD.md`; ik verander van gedachten zodra er wél een certificaat is. ## Eén claim rechtgezet Ik schreef eerst dat geen CI-runner een installer kan bouwen. Onjuist: de forge heeft geen Windows-machine, maar `.github/workflows/release.yml` op de spiegel bouwt bij elke `v*`-tag de Windows-zip die de forge terughaalt. De installer in die lijn hangen is een **open besluit**, geen onmogelijkheid — het vraagt Inno Setup gepind op de runner (`windows-latest` levert het sinds Server 2025 niet meer mee) en de keuze over een tweede Windows-artefact. De packager is daarom bash en geen `make`-doel: die job merkt zelf op dat `make` er niet betrouwbaar staat. Tot dat besluit valt is het handwerk en levert een release nog steeds alleen de zip. Dat staat nu ook zo in de changelog, zodat niemand naar een download zoekt die er niet is. ## Toetsen - `make check` groen: 9.938 tests, dekking 87,1%, per-bestand-vloer 0. - `make check-secrets` (gitleaks + trufflehog, werkboom én historie): geen bevindingen. `make sast` (semgrep, lokale regels): 0 findings. - DAST (ZAP) niet gedraaid: deze wijziging raakt het geserveerde weboppervlak niet. - **Waarom een poort in plaats van een gewone test:** Inno Setup draait alleen op Windows, dus geen enkele testrun máákt een installer. `test/windows_packaging_test.dart` toetst offline wat wél kan roesten — precies de opzet van `linux_packaging_test.dart` voor .deb/.rpm/AppImage. Of de installer écht installeert, associeert en de-installeert blijft handwerk op Windows. - **Mutatiegetoetst**, want een groene poort die niets bewijst is erger dan geen poort: ProgID uit de pas, een `[Code]`-sectie erbij, een bestandslijst in plaats van de bundel, de ongetekend-waarschuwing weg, tijdstempeling laten vallen — alle vijf betrapt. - **De packager echt gedraaid** met een nagemaakte ISCC en signtool, vijf paden: geen bundel (faalt netjes), geen certificaat (bouwt + waarschuwt, exit 0), mét certificaat (tekent exe, dll's en de installer), één dll die weigert (héle bouw valt — ook binnen de lus), certificaat zonder signtool (faalt vóórdat er iets gebeurt). - De poort staat in `REGISTRATION_TESTS` en niet alleen in de volle suite: die draait pas ná de merge op `linux-gate`, en dan is drift geschiedenis in plaats van een bewerking. `docs/CHECKS.md` telde er vier; het zijn er nu vijf. ## Documentatie `docs/BUILD.md` (bouwroute + ondertekenen + publicatievoorwaarde), `packaging/README.md` (van Linux-only naar beide platformen), `SECURITY.md` (twee alinea's), `docs/CHECKS.md` (de telling), `CHANGELOG.md`.
Uitpakken en zelf een snelkoppeling maken is een armoedige manier om iets te
installeren. `make build-windows-installer` verpakt daarom de bundel die
`make build-windows` net heeft neergezet in een gewone Inno Setup-installer:
snelkoppeling, de bestandsassociaties uit windows/file-associations.reg, en
de-installeren via Programs and Features. Standaard machinebreed, met een
uitwijk naar alleen-voor-mij voor wie geen beheerdersrechten heeft — vandaar
`HKA` en `{autopf}`, zodat één sleutellijst allebei bedient.

Wat er niet in zit is de kern van het ontwerp: geen bijwerken, geen
versiecontrole, geen release-feed, en op geen enkel moment het netwerk op.
SECURITY.md belooft dat OciDeck niet naar huis belt en dat een verbetering je
bereikt door de standaardtak op te halen en opnieuw te bouwen; een installer
die zichzelf kan bijwerken ruilt die belofte in voor gemak. Het gemak is het
waard, het updatekanaal niet.

Ondertekenen is een schakelaar die uit staat, want #1013 blijft gelden: sinds
maart 2024 koopt geen enkel certificaattype directe SmartScreen-rust. Zonder
certificaat bouwt het script een prima installer en waarschuwt luid; mét
certificaat is het alles-of-niets, omdat een half-getekende installer er
betrouwbaar uitziet op precies de plek waar mensen kijken. Het accepteert
uitsluitend een vingerafdruk uit de certificaatopslag — geen sleutelbestand,
geen wachtwoord — zodat ondertekenmateriaal nooit een omgevingsvariabele of
een runner-geheim kan worden. Dezelfde lijn die notarize_macos.sh op macOS
aanhoudt.

Verwijst naar #1208.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Een installer valt buiten `flutter test` — Inno Setup draait pas op Windows,
dus geen enkele testrun maakt er een. Wat wél offline te toetsen valt is de
bedrading, precies zoals linux_packaging_test.dart dat voor de .deb/.rpm/AppImage
doet: dat het .iss-script en windows/file-associations.reg dezelfde ProgID,
dezelfde sleutels en hetzelfde ocideck.exe blijven noemen, dat de installer de
héle uitvoer van `make build-windows` verpakt in plaats van een met de hand
gekozen bestandslijst, dat er geen downloader en geen [Code]-sectie in zit, en
dat het ondertekenen optioneel-maar-luid blijft.

De poort staat in REGISTRATION_TESTS en niet alleen in de volle suite. Die
draait pas ná de merge op linux-gate; dan is drift geschiedenis in plaats van
een bewerking. De lijst is met de hand bijgehouden en zegt dat er zelf bij: een
nieuwe invariantpoort als test hoort erin, anders is het opnieuw een stil gat.
Kosten: ongeveer een derde seconde per PR.

Vijf mutaties getoetst en alle vijf betrapt: ProgID uit de pas, een
[Code]-sectie erbij, een bestandslijst in plaats van de bundel, de
ongetekend-waarschuwing weg, en tijdstempeling laten vallen.

Verwijst naar #1208.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SECURITY.md zei "geen getekende installer die zichzelf bijwerkt". Dat blijft
letterlijk waar, maar een lezer die nu een installer aantreft verdient de
verzoening erbij: hij bestaat, hij is dom en offline, en een test houdt dat zo.
De ondertekenparagraaf krijgt dezelfde behandeling — de installer valt onder
hetzelfde besluit als de zip, en de haak zit er al in zodat alleen het
certificaat nog ontbreekt.

BUILD.md krijgt de bouwroute, packaging/README.md wordt van Linux-only naar
beide platformen getrokken, en CHECKS.md telde de registratiepoorten als "vier"
— dat zijn er nu vijf.

Eén claim rechtgezet die ik eerst zelf verkeerd opschreef: er is wél een
Windows-bouwmachine. De forge heeft er geen, maar de spiegel op github.com
bouwt bij elke v*-tag de Windows-zip die de forge terughaalt. De installer in
die lijn hangen is dus een open besluit en geen onmogelijkheid — het vraagt
Inno Setup gepind op de runner (windows-latest levert het niet meer mee) en een
keuze over een tweede Windows-artefact. Tot dat besluit valt, is het handwerk,
en levert een release nog steeds alleen de zip. Dat staat er nu ook zo.

Verwijst naar #1208.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs(windows): benoem de voorwaarde waaronder de installer gepubliceerd mag worden
All checks were successful
scans / scans (pull_request) Successful in 1m50s
static-gate / static-gate (pull_request) Successful in 4m32s
6dcd309db8
Bewakerstoets op deze wijziging leverde één echte botsing op, en die hoort
opgeschreven in plaats van stilzwijgend gemaakt.

Een installer vraagt om verhoging. Een óngetekende installer die om verhoging
vraagt leert een slechtere gewoonte aan dan een ongetekende app die je zelf
uitpakt: mensen zijn getraind om installatievensters weg te klikken, en juist
daar staat straks "Onbekende uitgever". Daarmee is publiceren een andere
beslissing dan bouwen — bouwen raakt niemand, publiceren wel.

Veiligheid gaat hier voor gemak (waarde 1), maar niet door de installer tegen
te houden: de voorwaarde is dat hij, zodra hij gepubliceerd wordt, in
SHA256SUMS staat en dus onder de minisign-handtekening over dat manifest valt.
Díe handtekening is wat hier in de plaats komt van het Authenticode-certificaat
dat dit project heeft gewogen en afgewezen. Een gepubliceerde installer die
noch getekend is noch in het manifest staat, heeft helemaal geen herkomst — dat
is een stap terug ten opzichte van de zip, geen gemak.

Ik verander van gedachten als er alsnog een certificaat komt: dan draagt de
installer zijn eigen herkomst en is de manifestregel een bevestiging in plaats
van de enige anker.

Verwijst naar #1208.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
brenno merged commit 939e7868ec into main 2026-08-19 17:29:58 +00:00
Sign in to join this conversation.
No description provided.