feat(forms): Uitnodiging openen… — een formulier ophalen en toetsen (fase 4, stap 3) #2256

Merged
brenno merged 4 commits from feat/intake-client into main 2026-10-03 21:32:12 +00:00
Owner

Wat

Fase 4, stap 3, en de eerste die een invuller ziet: Uitnodiging openen… op het startscherm. De eigenaar koos de client eerst (de server wacht op D3), tegen een inzendserver in het geheugen.

  • De HTTP-laag (PinnedIntakeHttp): alleen https, alleen de poorten 80/443/8080/8443, de host door NetGuard.safeResolve — niet safeResolveTrusted: het adres staat in een uitnodiging, dus in invoer van buiten, en mag nooit een intern adres bereiken —, de socket gepind aan dat adres, geen redirects, geen User-Agent, een body met zijn Content-Length. Elke mislukking is een waarde (IntakeHttpFailure) zonder URL, body of kop. Op het web weigert de stub (if (dart.library.js_interop), niet html: onder wasm kiest die stil de stub).
  • IntakeClient.openInvitation: GET /v1/info (te oud, te nieuw of geen inzendserver), het formulier, en dan elke bundel tegen de vingerafdruk uit de link, het adres uit de link en de pins. Een variant die niet slaagt valt af; slaagt er geen, dan noemt de weigering de reden van de eerste. Eerlijk bevonden onderweg: toetst men de bundels van verschillende talen achter elkaar tegen oplopende pins, dan weigert een lagere reeks zichzelf — daarom toetst elke bundel tegen de pins van vóór het antwoord, en worden de pins daarna het hoogste van wat slaagde. En: zou een publicatie varianten met verschillende bundle_seq dragen, dan weigert wie de hoogste zag de lagere bij een volgend bezoek. INTAKE_PROTOCOL.md §5.3 zegt nu dat één publicatie één bundle_seq draagt (de server weigert anders met bundle-invalid; de organisator tekent bij publiceren alle talen opnieuw).
  • Het venster (FormInvitationDialog): de link plakken (van het klembord als hij op een uitnodiging lijkt), het adres in beeld vóór het verzoek — de tik op Formulier ophalen is het "ja" —, en erna de naam van de organisator uit de ondertekende bundel, met de vingerafdruk onder Details. Een taalkeuze bij meer dan één taal (voorgekozen op de taal van het programma); geen weg verder voor een formulier dat de server gesloten noemt of waarvan de bundel een verstreken laatste dag noemt. Een link zonder vingerafdruk gaat niet verder, met zijn eigen zin. Voor iedereen op een computer, niet achter de uitbreiding voor formulieren: een invuller heeft die niet.
  • De handeling (openFormInvitation): bewaart de pins, zet het geheugen van de invulpagina klaar (het gepubliceerde formulier, de bundel en de vingerafdruk) en opent het sjabloon als nieuw document — het opslaan van de inzending, ook verzegeld, vraagt dan om geen bestand en geen vingerafdruk.
  • Het geheugen van de invulpagina is nu per formulier, versie én taal. Daarvoor deelde het Nederlandse sjabloon een sleutel met het Engelse en duwde het eruit (ook op de bestandsroute).
  • De zin bij een geweigerde bundel staat op één plek (form_bundle_issue_text.dart) voor de bestandsroute én de uitnodiging; hostMismatch heeft een eigen zin.
  • 28 nieuwe teksten in 30 talen. Eén bestond al (Server: {host}, overal gelijk gelaten, ook waar de taal een ander woord heeft): die staat nu in alle 30 talen goed en in de Engelse uitzonderingenlijst.

Getoetst

  • IntakeClient tegen FakeIntakeServer (een IntakeHttp in het geheugen die de routes uit de kern gebruikt, niet een tweede lezing van het document): wat er gevraagd wordt en in welke volgorde, elke reden waarom een bundel niet wordt geloofd (andere vingerafdruk, ander sjabloon, ander adres, verlopen, terugval, ander formulier), elke weigering van de server (te oud/nieuw, geen inzendserver, een code, een proxy-pagina, Retry-After tot de grens van een dag, geen UTF-8), elke IntakeHttpFailure, het plafond voor het formulier, de tijd die de HTTP-laag krijgt, een poort in het adres.
  • De echte HTTP-laag via een lokale server waarheen de socket wordt omgeleid (NetGuard draait op een publiek adres): alles wat vóór een socket wordt geweigerd (schema, poort, begrenzing, intern adres, naam die nergens op uitkomt), en onderweg status/koppen/bytes, geen User-Agent, een body met zijn lengte en niet in stukken, een redirect die niet wordt gevolgd, te groot, een antwoord precies op de grens, te traag, een afgebroken verbinding.
  • Het venster: elke zin, het klembord (wel/niet overnemen), annuleren tijdens het ophalen met een late uitkomst, de taalkeuze, een gesloten formulier, de laatste dag, pins, en in het Engels. De handeling: het tabblad, de pins, het geheugen per taal, en dat annuleren niets achterlaat. Het startscherm: de knop is er zonder de uitbreiding en opent het venster.
  • Mutatietoets over de client, de HTTP-laag en het venster: 0 overlevers in de eerste twee; in het venster alleen opmaakgetallen (breedtes, marges, regelaantallen). De eerste ronde liet echte overlevers zien: de begrenzingen van de HTTP-laag (de plafondwaarde, de standaardtijd, maxResponseBytes <= 0), het geforceerd sluiten van een verbinding (een test die op elke uitkomst slaagde), een klembord dat laat komt, het venster dat niet door ernaast tikken sluit, de voorgekozen taal — en twee die dood waren (een annuleervlag naast mounted, een beginwaarde die direct werd overschreven): die zijn geschrapt.
  • make check (ongepiped, exit 0) liep over de boom vóór de hernoeming van één testconstante (kTestToken → kTestInvite: gitleaks las token plus een waarde met entropie als lek, ook in de historie, dus de commits zijn herschreven); daarna draaiden de betrokken tests, check-static, check-registrations, check-secrets (gitleaks + trufflehog, alle refs), sast en check-packages opnieuw: groen. De tak loopt niet achter op main.

Wat er nog niet is

Het versturen (acceptatiedialoog, upload, aankomstbewijs, outbox), de kant van de organisator (publiceren naar een server, binnenhalen, token), de server zelf en de webinvuller.

## Wat Fase 4, stap 3, en de eerste die een invuller **ziet**: **Uitnodiging openen…** op het startscherm. De eigenaar koos de client eerst (de server wacht op D3), tegen een inzendserver in het geheugen. - **De HTTP-laag** (`PinnedIntakeHttp`): alleen `https`, alleen de poorten 80/443/8080/8443, de host door `NetGuard.safeResolve` — **niet** `safeResolveTrusted`: het adres staat in een uitnodiging, dus in invoer van buiten, en mag nooit een intern adres bereiken —, de socket gepind aan dat adres, geen redirects, **geen `User-Agent`**, een body met zijn `Content-Length`. Elke mislukking is een waarde (`IntakeHttpFailure`) zonder URL, body of kop. Op het web weigert de stub (`if (dart.library.js_interop)`, niet `html`: onder wasm kiest die stil de stub). - **`IntakeClient.openInvitation`**: `GET /v1/info` (te oud, te nieuw of geen inzendserver), het formulier, en dan **elke bundel tegen de vingerafdruk uit de link**, het adres uit de link en de pins. Een variant die niet slaagt valt af; slaagt er geen, dan noemt de weigering de reden van de eerste. **Eerlijk bevonden onderweg:** toetst men de bundels van verschillende talen achter elkaar tegen oplopende pins, dan weigert een lagere reeks zichzelf — daarom toetst elke bundel tegen de pins van **vóór** het antwoord, en worden de pins daarna het hoogste van wat slaagde. En: zou een publicatie varianten met verschillende `bundle_seq` dragen, dan weigert wie de hoogste zag de lagere bij een volgend bezoek. `INTAKE_PROTOCOL.md` §5.3 zegt nu dat **één publicatie één `bundle_seq`** draagt (de server weigert anders met `bundle-invalid`; de organisator tekent bij publiceren alle talen opnieuw). - **Het venster** (`FormInvitationDialog`): de link plakken (van het klembord als hij op een uitnodiging lijkt), **het adres in beeld vóór het verzoek** — de tik op *Formulier ophalen* is het "ja" —, en erna **de naam van de organisator uit de ondertekende bundel**, met de vingerafdruk onder *Details*. Een taalkeuze bij meer dan één taal (voorgekozen op de taal van het programma); geen weg verder voor een formulier dat de server gesloten noemt of waarvan de bundel een verstreken laatste dag noemt. Een **link zonder vingerafdruk gaat niet verder**, met zijn eigen zin. Voor iedereen op een computer, niet achter de uitbreiding voor formulieren: een invuller heeft die niet. - **De handeling** (`openFormInvitation`): bewaart de pins, **zet het geheugen van de invulpagina klaar** (het gepubliceerde formulier, de bundel en de vingerafdruk) en opent het sjabloon als nieuw document — het opslaan van de inzending, ook verzegeld, vraagt dan om geen bestand en geen vingerafdruk. - **Het geheugen van de invulpagina is nu per formulier, versie én taal.** Daarvoor deelde het Nederlandse sjabloon een sleutel met het Engelse en duwde het eruit (ook op de bestandsroute). - De zin bij een geweigerde bundel staat op één plek (`form_bundle_issue_text.dart`) voor de bestandsroute én de uitnodiging; `hostMismatch` heeft een eigen zin. - 28 nieuwe teksten in 30 talen. Eén bestond al (`Server: {host}`, overal gelijk gelaten, ook waar de taal een ander woord heeft): die staat nu in alle 30 talen goed en in de Engelse uitzonderingenlijst. ## Getoetst - `IntakeClient` tegen `FakeIntakeServer` (een `IntakeHttp` in het geheugen die de routes uit de kern gebruikt, niet een tweede lezing van het document): wat er gevraagd wordt en in welke volgorde, elke reden waarom een bundel niet wordt geloofd (andere vingerafdruk, ander sjabloon, ander adres, verlopen, terugval, ander formulier), elke weigering van de server (te oud/nieuw, geen inzendserver, een code, een proxy-pagina, `Retry-After` tot de grens van een dag, geen UTF-8), elke `IntakeHttpFailure`, het plafond voor het formulier, de tijd die de HTTP-laag krijgt, een poort in het adres. - De echte HTTP-laag via een lokale server waarheen de socket wordt omgeleid (`NetGuard` draait op een publiek adres): alles wat vóór een socket wordt geweigerd (schema, poort, begrenzing, intern adres, naam die nergens op uitkomt), en onderweg status/koppen/bytes, **geen `User-Agent`**, een body met zijn lengte en niet in stukken, **een redirect die niet wordt gevolgd**, te groot, een antwoord precies op de grens, te traag, een afgebroken verbinding. - Het venster: elke zin, het klembord (wel/niet overnemen), annuleren tijdens het ophalen met een late uitkomst, de taalkeuze, een gesloten formulier, de laatste dag, pins, en in het Engels. De handeling: het tabblad, de pins, het geheugen per taal, en dat annuleren niets achterlaat. Het startscherm: de knop is er zonder de uitbreiding en opent het venster. - Mutatietoets over de client, de HTTP-laag en het venster: 0 overlevers in de eerste twee; in het venster alleen opmaakgetallen (breedtes, marges, regelaantallen). De eerste ronde liet echte overlevers zien: de begrenzingen van de HTTP-laag (de plafondwaarde, de standaardtijd, `maxResponseBytes <= 0`), het **geforceerd sluiten** van een verbinding (een test die op elke uitkomst slaagde), een klembord dat laat komt, het venster dat niet door ernaast tikken sluit, de voorgekozen taal — en twee die dood waren (een annuleervlag naast `mounted`, een beginwaarde die direct werd overschreven): die zijn geschrapt. - `make check` (ongepiped, exit 0) liep over de boom vóór de hernoeming van één testconstante (`kTestToken` → `kTestInvite`: gitleaks las `token` plus een waarde met entropie als lek, ook in de historie, dus de commits zijn herschreven); daarna draaiden de betrokken tests, `check-static`, `check-registrations`, `check-secrets` (gitleaks + trufflehog, alle refs), `sast` en `check-packages` opnieuw: groen. De tak loopt niet achter op main. ## Wat er nog niet is Het **versturen** (acceptatiedialoog, upload, aankomstbewijs, outbox), de kant van de organisator (publiceren naar een server, binnenhalen, token), de server zelf en de webinvuller.
IntakeHttp (gepind, alleen https, nooit een intern adres, geen redirects, geen User-Agent) en
IntakeClient.openInvitation: GET /v1/info, het formulier, dan elke bundel tegen de vingerafdruk uit de
link, het adres en de pins zoals ze voor het antwoord waren. Getoetst tegen een inzendserver in het geheugen
die de routes uit de kern gebruikt.
Het venster toont het adres vóór het verzoek en de naam van de organisator uit de ondertekende bundel erna;
een link zonder vingerafdruk gaat niet verder. Het formulier opent als nieuw document en de invulpagina
onthoudt het gepubliceerde formulier, de bundel en de vingerafdruk, per formulier, versie en taal.
De zin bij een geweigerde bundel staat nu op één plek (form_bundle_issue_text.dart).
brenno merged commit b79ea1302b into main 2026-10-03 21:32:12 +00:00
brenno deleted branch feat/intake-client 2026-10-03 21:32:13 +00:00
Sign in to join this conversation.
No description provided.