Gate 0.1.0: eigen image, het command-blok is een script, de app-map is leeg

Plan Eigenimage, fase 1 tot en met 3. De vier templates verhuizen naar
tools/electrum-gate/ zonder extensie; daarnaast Dockerfile (nginx:1.30-alpine
plus python3), entrypoint.sh (het command-blok van de compose, zonder $$) en
build.sh naar het voorbeeld van Evolu Relay. Een image voor beide containers,
gebouwd op de Umbrel; open punt 2 en 3 daarmee beslist.

Inhoudelijk anders dan alleen verplaatst: het log_format staat in stream.conf
zelf, het backend-adres komt via twee plaatshouders zonder dollarteken uit de
omgeving (ook in de server-service), en de pagina haalt versie en adres uit
status.json via GATE_APP_VERSION.

Tests mee verhuisd en uitgebreid: entrypoint.sh en Dockerfile in plaats van het
command-blok, en de tag in de compose gelijk aan VERSION in build.sh voor elke
eigen image. Mutatie-getest met drie ingrepen.

Nog niet gebouwd: er is hier geen Docker. De tag staat ongepind tot de eerste
push; dat is fase 4 en die is van de gebruiker.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Harmen
2026-09-07 20:20:42 +02:00
co-authored by Claude Fable 5.1
parent 7b3804df89
commit 973b24a23f
25 changed files with 981 additions and 410 deletions
+35
View File
@@ -8,6 +8,41 @@ Wat er nog moet gebeuren staat **niet** hier maar in de plannen; zie [CONTINUE_H
Een lijst met geplande features op twee plekken loopt uit elkaar, en dan is geen van beide meer te
vertrouwen.
## [0.1.0] - 2026-09-07
**Nog niet gebouwd.** De compose verwijst naar `sc.kamenier-hamer.nl/sysop/electrum-gate:0.1.0` zonder
digest; die komt erbij zodra de gebruiker de image op de Umbrel gebouwd en geduwd heeft. Tot die tijd is
dit een versie in de repo en niet op het apparaat.
### Changed
- **De app heeft een eigen image**, gebouwd uit `tools/electrum-gate/` met `build.sh`, op basis van
`nginx:1.30-alpine` plus `python3`. Eén image voor beide containers; de compose start hem als `server`
met het standaardcommando en als `agent` met `python3 /app/agent.py`. Tot nu draaide de app op
`python:3-alpine` en `nginx:alpine` met zijn eigen bestanden als `*.template` uit de app-map gemount.
Waarom, staat in het plan **Eigenimage**, `PLAN.md` §3; het korte antwoord is dat de programmacode nu
niet meer in de app-data staat, en dat het startscript weer leesbaar is.
- **Het `command`-blok van de compose is `entrypoint.sh` in de image.** Ruim honderd regels shell in een
YAML-string, met elk dollarteken verdubbeld omdat umbreld anders elke `${...}` leegmaakte, is nu een
gewoon script. Zelfde gedrag: TLS aan of uit naar de toestand van `cert.conf`, de verbindingsteller uit
`/proc/net/tcp`, de herlaadlus met vangnettak, en `exec nginx` als laatste regel.
- **Het `log_format` staat in `stream.conf` zelf.** Het stond in een los bestand dat het `command`-blok
wegschreef, omdat een dollarteken in een template niet mocht. Die omweg is weg.
- **Het backend-adres komt uit de omgeving, in beide containers.** `stream.conf` heeft twee plaatshouders
zonder dollarteken die `entrypoint.sh` bij het starten invult; de server-service krijgt daarvoor dezelfde
`GATE_ELECTRUM_HOST` en `GATE_ELECTRUM_PORT` als de agent.
- **De pagina haalt de versie en het backend-adres uit `status.json`.** umbreld vulde die tot nu
rechtstreeks in de pagina in; nu de pagina in de image zit, geeft de compose `APP_VERSION` aan de agent
door als `GATE_APP_VERSION`, en zet de agent `version` in de status. Ontbreekt hij, dan toont de kop
geen versie in plaats van een verzonnen.
- **`icon.png` is updatebaar**, want de image kopieert hem uit de app-map. Het bestand blijft ook daar
staan, want het manifest wijst ernaar voor de tegel in de winkel.
### Removed
- De vier `*.template`-bestanden en hun mounts uit de app-map. Wat er overblijft: `docker-compose.yml`,
`umbrel-app.yml`, `icon.png` en `data/`.
## [0.0.29] - 2026-09-04
### Changed
+1 -1
View File
@@ -23,7 +23,7 @@
| Plan | App | Volgende stap | Status |
|-|-|-|-|
| [Eigenimage](Plannen/Actief/004-Eigenimage/TAKEN.md) | **beide** | **Gepromoveerd op 28-08-2026, want waar het op wachtte is klaar:** de gebruiker wilde eerst Evolu Relay afmaken en dat doel is gehaald. Het register is beslist (Gitea, bij inlevering opnieuw kijken), dus fase 1 kan zo starten: het bouwrecept in `tools/electrum-gate/`, multi-arch, anoniem te halen. Er ligt nu ook een voorbeeld naast: `tools/evolu-relay/` doet precies dit, inclusief de valkuil dat een lokaal gebouwde tag voor umbreld onbereikbaar is. **Sinds 30-08-2026 wegen er twee dingen zwaarder voor dit plan.** De kolom App staat op "beide": §8 is herschreven, want ook Evolu Relay heeft zijn agent en pagina nog op vreemde images staan; alleen de relay zelf is klaar. En dit plan blijkt de poort te zijn voor het pinnen dat **Publicatie-Gate** en **Publicatie-Relay** eisen: die twee images verdwijnen hier, dus eerder pinnen is weggegooid werk. Let op §1: het lost níet het updateprobleem op, want dat bestaat niet meer | |
| [Eigenimage](Plannen/Actief/004-Eigenimage/TAKEN.md) | **beide** | **Fase 1 tot en met 3 gedaan op 07-09-2026, als Electrum Gate 0.1.0, ongebouwd.** Het recept staat in `tools/electrum-gate/` (Dockerfile, build.sh, entrypoint.sh en de vier voormalige templates), de app-map bevat alleen nog compose, manifest, icoon en `data/`, en de compose is voor het eerst zonder shellblok. Eén image voor beide containers, gebouwd op de Umbrel; open punt 2 en 3 zijn daarmee beslist. **Volgende stap is van de gebruiker en kan alleen op de Umbrel:** `sh tools/electrum-gate/build.sh`, duwen, de digest twee keer in de compose, committen, updaten, en dan de controles van fase 4. Reken op minstens één ronde, want de image is nog nooit gebouwd; de tests toetsen de tekst van het recept en niet het resultaat. **Daarna:** hetzelfde voor de agent en de pagina van Evolu Relay (§8), pas als de weg bij Gate bewezen is. Multi-arch en de anonieme pull horen bij **Publicatie-Gate** | 🔶 |
## B - Los oppakbaar (geen blokkade, geen vaste volgorde)
+4
View File
@@ -23,6 +23,10 @@ De keten die eronder ligt, van achter naar voren:
**De regel: pin alleen een image die er na Eigenimage nog staat.** In de praktijk is dat de image die je
zelf uitgeeft, en die is er precies één per app.
Stand op 07-09-2026: Electrum Gate heeft die eigen image sinds 0.1.0 en zijn twee vreemde images zijn weg;
de pin komt erbij zodra de gebruiker hem gebouwd en geduwd heeft. Bij Evolu Relay staan `python:3-alpine`
en `nginx:alpine` er nog voor de agent en de pagina, en daarvoor geldt de regel hierboven onverkort.
Twee dingen die dit níet zegt, want anders slaat het de andere kant op door:
- **de eigen image pinnen doe je wél meteen**, bij elke verhoging. Dat is geen toekomstig werk maar de
+14 -8
View File
@@ -12,17 +12,23 @@
## Nog te beslissen
2. **Eén image of twee?** [PLAN.md](PLAN.md) §6 stelt één voor: de agent en nginx blijven twee containers,
maar ze delen één image. Twee images is netter gescheiden en verdubbelt het bouwwerk en het pinwerk.
**Moment:** bij de start van het werk · **Eigenaar:** sessie, met de gebruiker mee
3. **Waar wordt gebouwd?** Op de Umbrel staat Docker, maar dat is een productiemachine en hij is amd64,
terwijl arm64 ook een eis is. Bouwen op de werkplek van de gebruiker vraagt daar Docker. Dit hangt samen
met de multi-arch-stap uit §4 van het plan, die nog nooit geprobeerd is.
**Moment:** bij de start van het werk · **Eigenaar:** gebruiker
Niets.
## Beslist
2. **Eén image of twee?** - **Eén** (07-09-2026, sessie, volgens het voorstel in [PLAN.md](PLAN.md) §6).
De agent en nginx blijven twee containers met dezelfde image; de compose geeft de agent een eigen
`command`. Basis is `nginx:1.30-alpine` met `python3` uit apk, en niet andersom: de officiële
nginx-image heeft de stream-module ingebouwd, bij alpine's eigen nginx is dat een los pakket.
**Moment:** afgerond · **Eigenaar:**
3. **Waar wordt gebouwd?** - **Op de Umbrel** (07-09-2026), om een eenvoudige reden: het is de enige
machine met Docker; de werkplek heeft het niet. Dat het een productiemachine is weegt minder dan dat
het de plek is waar Evolu Relay ook al gebouwd wordt. Gevolg: één architectuur, amd64, en dat is voor
eigen gebruik genoeg. Multi-arch zit in `build.sh` als schakelaar (`PLATFORMS=...`) en is werk voor
**Publicatie-Gate**; het is nog nooit geprobeerd.
**Moment:** afgerond · **Eigenaar:**
1. **Blijft het Gitea-register ook bij publicatie de bron?** - **Voor nu Gitea, bij inlevering opnieuw
kijken** (27-08-2026, gebruiker).
@@ -3,6 +3,25 @@
> Chronologisch sessielog, nieuwste bovenaan. Kort: 3 tot 6 regels per entry. Wat er is gebeurd en
> waarom, niet wat er nog moet: dat staat in [TAKEN.md](TAKEN.md).
## 07-09-2026 - fase 1 tot en met 3 in één sessie: het recept staat, de app-map is leeg
Op verzoek van de gebruiker opgepakt, direct na Relay 0.5.6. De vier templates zijn met `git mv` naar
`tools/electrum-gate/` verhuisd en heten daar gewoon `agent.py`, `nginx.conf`, `stream.conf` en
`index.html`. Daarnaast nieuw: `Dockerfile` (nginx:1.30-alpine plus python3), `entrypoint.sh` (het
`command`-blok van de compose, zonder `$$`) en `build.sh` naar het voorbeeld van Evolu Relay. Open punt 2
en 3 zijn beslist: één image, gebouwd op de Umbrel.
Wat er inhoudelijk veranderde en niet alleen verhuisde: het `log_format` staat nu in `stream.conf` zelf,
het backend-adres komt via twee plaatshouders zonder dollarteken uit de omgeving (ook de server-service
krijgt ze nu), en de pagina haalt versie en adres uit `status.json`, waarvoor de agent `GATE_APP_VERSION`
doorgeeft. De compose is voor het eerst sinds 0.0.3 zonder shellblok.
Tests: alle zes groen, niets overgeslagen. Drie mutaties (het `exec` voor nginx weg, `ENTRYPOINT []` weg,
één tag in de compose verhoogd) lieten precies de bedoelde toetsen omvallen, ook de nieuwe in
`test_appstore_vorm.py`. **Niet geverifieerd: de image zelf.** Er is hier geen Docker, dus of `apk add
python3` het gedraagt zoals gedacht en of de container start, blijkt pas bij fase 4 op de Umbrel.
Electrum Gate staat op 0.1.0 in het manifest, met de tag ongepind tot de eerste push.
## 27-08-2026 - het register is beslist, en daarna ging de aandacht naar Evolu Relay
Open punt 1 beantwoord: **voor nu Gitea, bij inlevering opnieuw kijken.** De twee bezwaren die dat punt
+67 -41
View File
@@ -18,65 +18,91 @@
## Volgende stap
> **Dit plan ligt stil op verzoek van de gebruiker (27-08-2026): hij gaat eerst verder met Evolu Relay,
> zie het plan Umbrelapp.** Dat is een volgordekeuze en geen blokkade; er is aan dit plan nog niets
> gebouwd, dus er ligt ook niets half af. Beginnen kan met fase 1, en het antwoord dat daarvoor nodig was
> staat er.
> **Opgepakt op 07-09-2026 op verzoek van de gebruiker.** Fase 1 tot en met 3 zijn in één sessie gedaan
> en gecommit als Electrum Gate 0.1.0, ongebouwd. **Wat nu volgt is van de gebruiker en kan alleen op de
> Umbrel**, want daar staat Docker:
>
> 1. `sh tools/electrum-gate/build.sh`, dan `docker login` en `docker push` zoals het script afdrukt;
> 2. de digest uit de push-uitvoer achter de tag in de compose, **twee keer** (agent en server);
> 3. committen en pushen, dan de app in umbrelOS updaten;
> 4. de vijf controles van fase 4 hieronder.
>
> Dit is de eerste keer dat deze image gebouwd wordt, dus reken op minstens één ronde: een `apk add` die
> anders uitpakt dan gedacht, of een pad dat de Dockerfile wél kent en de container niet. De tests
> hierboven toetsen de tekst van het recept, niet het resultaat.
- [x] **Fase 1 tot en met 3 gedaan (07-09-2026), als 0.1.0.** De vier `*.template`-bestanden staan nu in
`tools/electrum-gate/` zonder de extensie, plus `Dockerfile`, `build.sh` en `entrypoint.sh`. De
compose gebruikt één image voor beide containers, ongepind tot de eerste push. Tests aangepast en
mutatie-getest; details in [PROGRESS.md](PROGRESS.md)
- [ ] **Fase 4: bouwen, duwen, pinnen, installeren, nakijken.** Van de gebruiker; zie hierboven
- [x] **Open punt 1 beantwoord (27-08-2026): voor nu Gitea, bij inlevering opnieuw kijken.** Daarmee is de
enige vraag weg die vóór het bouwen beantwoord moest zijn. Zie [OPEN.md](OPEN.md)
- [ ] **Fase 1 beginnen: het bouwrecept in `tools/electrum-gate/`.** Open punt 2 en 3 horen bij die start
en niet ervoor: één image of twee, en op welke machine er gebouwd wordt. Dat laatste is de enige die
de gebruiker moet beantwoorden, en pas op het moment dat het recept er is en gedraaid moet worden
## Fase 1 - Het bouwrecept
- [ ] Beslissen: één image of twee (open punt 2). [PLAN.md](PLAN.md) §6 stelt één voor
- [ ] Beslissen: waar wordt gebouwd (open punt 3). De Umbrel is amd64 en een productiemachine
- [ ] `tools/electrum-gate/Dockerfile` schrijven: basisimage, `agent.py`, de nginx-configuratie, de pagina,
en een `entrypoint.sh`. **Niet in de app-map**: een `Dockerfile` staat niet in de update-whitelist,
dus bouwen-in-de-app kost bij elke versie een deïnstallatie
- [ ] `tools/electrum-gate/build.sh` naar het voorbeeld van `tools/evolu-relay/build.sh`: pin op de
basisimage, bouwen, naar het register duwen, en de digest afdrukken die in de compose moet
- [ ] **Multi-arch bouwen, en dat is nieuw werk.** `linux/amd64` én `linux/arm64` in de manifest-lijst.
Vraagt `buildx` met QEMU en is hier nog nooit geprobeerd; zie [PLAN.md](PLAN.md) §4
- [x] Beslissen: één image of twee (open punt 2). **Eén**, zoals [PLAN.md](PLAN.md) §6 voorstelt; beslist
07-09-2026, zie [OPEN.md](OPEN.md)
- [x] Beslissen: waar wordt gebouwd (open punt 3). **Op de Umbrel**, want dat is de enige machine met
Docker; de werkplek heeft het niet. Zie [OPEN.md](OPEN.md)
- [x] `tools/electrum-gate/Dockerfile`: `nginx:1.30-alpine` plus `python3` uit apk, de vijf bestanden
erin, `ENTRYPOINT []` zodat het envsubst-script van de nginx-image niet meedraait. De bouwcontext is
de repo-root, want `icon.png` blijft in de app-map (het manifest wijst ernaar) en wordt van daar
gekopieerd; `.dockerignore` in de root houdt de context klein
- [x] `tools/electrum-gate/build.sh` naar het voorbeeld van Evolu Relay: `VERSION`, controles vooraf
(ontbrekende bestanden, een `${` die niemand meer invult), bouwen, en de vervolgstappen afdrukken
- [ ] **Multi-arch bouwen, en dat is nieuw werk.** `build.sh` heeft er een schakelaar voor:
`PLATFORMS="linux/amd64,linux/arm64"` bouwt met `buildx` en duwt meteen. Nog nooit geprobeerd, en
niet nodig voor eigen gebruik op een amd64-Umbrel. Hoort bij **Publicatie-Gate**; zie
[PLAN.md](PLAN.md) §4
- [ ] **Controleren dat de image anoniem te halen is.** umbreld krijgt geen inloggegevens mee. Uitloggen in
dezelfde context waarin je inlogde, anders meet je je eigen sessie. Het commando staat onderaan
`tools/evolu-relay/build.sh`
dezelfde context waarin je inlogde, anders meet je je eigen sessie. Het commando drukt `build.sh` af.
**Eigenaar: gebruiker**, bij fase 4
## Fase 2 - Het `command`-blok wordt een script
- [ ] Het inline `command`-blok uit `docker-compose.yml` omzetten naar `entrypoint.sh` in de image. Dat is
ruim honderd regels shell in een YAML-string met een eigen ontsnappingsregel voor het dollarteken;
zie [PLAN.md](PLAN.md) §2. **Dit is de grootste inhoudelijke winst van het hele plan**
- [ ] Let op wat daarbij niet mag sneuvelen: het `log_format` met `$$`, de sessieteller uit
`/proc/net/tcp`, de herlaadlus met zijn vangnettak, en het feit dat nginx met `exec` het hoofdproces
wordt. Die laatste is geen detail: andersom blijft de shell PID 1, geeft signalen niet door, en ziet
Docker een geslaagde afsluiting als nginx omvalt
- [x] `tools/electrum-gate/entrypoint.sh`, uit het `command`-blok van de compose. Zelfde gedrag, geen
`$$` meer, en het `log_format` staat nu gewoon in `stream.conf` in plaats van in een los bestand dat
het script wegschreef. Nieuw erin: het invullen van het backend-adres in het stream-blok (zie fase 3)
- [x] Wat niet mocht sneuvelen en er staat: de sessieteller uit `/proc/net/tcp`, de herlaadlus met zijn
vangnettak, en `exec nginx` als laatste regel. Alle drie met een toets in
`test_server_start_zonder_certificaat.py`, en die zijn gezien te falen
## Fase 3 - De app-map opschonen
- [ ] `agent.py.template`, `nginx.conf.template`, `stream.conf.template` en `index.html.template` uit de
app-map halen zodra ze in de image zitten
- [ ] **`index.html` heeft dan geen template-invulling meer.** Vandaag staat er
`${APP_ELECTRS_NODE_IP}:${APP_ELECTRS_NODE_PORT}` in, ingevuld door umbreld bij het starten. Zit de
pagina in de image, dan moet dat adres langs de omgeving naar binnen, bijvoorbeeld doordat de agent
het in `status.json` zet en de pagina het daaruit leest. Dat is werk dat nu nog gratis is
- [ ] De compose houdt zijn `environment`-blok; dat is hoe `${APP_ELECTRS_NODE_IP}` binnenkomt, en die weg
is op 27-08-2026 bewezen bij de omschakeling naar Fulcrum
- [ ] Wat er in de app-map overblijft: `docker-compose.yml`, `umbrel-app.yml`, `icon.png` en `data/` met
zijn twee `.gitkeep`-bestanden
- [x] De vier templates uit de app-map, met `git mv` naar `tools/electrum-gate/` zonder `.template`
- [x] **`index.html` heeft geen template-invulling meer.** `${APP_VERSION}` en het backend-adres komen uit
`status.json`: de agent krijgt `GATE_APP_VERSION: ${APP_VERSION}` uit de compose en zet `version` in
de status; het adres stond er al als `backend.address`
- [x] **`stream.conf` heeft geen template-invulling meer.** Twee plaatshouders zonder dollarteken,
`__GATE_ELECTRUM_HOST__` en `__GATE_ELECTRUM_PORT__`, die `entrypoint.sh` uit de omgeving invult;
de server-service krijgt daarvoor dezelfde twee variabelen als de agent
- [x] De compose houdt zijn `environment`-blok; de mounts van de vijf losse bestanden zijn eruit
- [x] Wat er in de app-map overblijft: `docker-compose.yml`, `umbrel-app.yml`, `icon.png` en `data/` met
zijn twee `.gitkeep`-bestanden. Precies wat [PLAN.md](PLAN.md) §6 zei
- [x] De tests mee verhuisd: `test_agent_certificates.py` laadt `tools/electrum-gate/agent.py`;
`test_server_start_zonder_certificaat.py` toetst `entrypoint.sh` en de Dockerfile in plaats van het
`command`-blok, plus dat de tag in de compose `VERSION` uit `build.sh` is; `test_appstore_vorm.py`
leest ook de nginx-config onder `tools/` en toetst tag gelijk aan `VERSION` voor élke eigen image;
`test_paginas_parsen.mjs` en `tools/voorbeeldpagina.mjs` vinden een pagina op beide plekken
## Fase 4 - Uitrollen en verifiëren
- [ ] Image gepind als `repo:tag@sha256:<digest>` in de compose, en `version` verhoogd in het manifest.
Zonder die verhoging rolt umbrelOS niets uit
- [ ] Image gepind als `repo:tag@sha256:<digest>` in de compose (twee keer), en `version` verhoogd in het
manifest. Dat laatste staat al op 0.1.0
- [ ] Op de Umbrel installeren en controleren dat alles het nog doet: de pagina, de certificaatkeuze, de
TLS-poort, de zelfcontrole en het activiteitenlog
- [ ] **`icon.png` wordt hiermee updatebaar**, want die zit dan in de image. Dat was de enige echte
uitzondering op "alles komt al aan bij een update"; zie [PLAN.md](PLAN.md) §1
- [ ] **De versie in de kop van de pagina**, want die loopt nu via `status.json` en niet meer via umbreld
- [ ] **`icon.png` is hiermee updatebaar**, want die zit in de image. Dat was de enige echte uitzondering
op "alles komt al aan bij een update"; zie [PLAN.md](PLAN.md) §1
## Daarna - Evolu Relay
- [ ] **Hetzelfde voor de agent en de pagina van Evolu Relay**, die nog als `*.template` op
`python:3-alpine` en `nginx:alpine` draaien; zie [PLAN.md](PLAN.md) §8. Pas na fase 4 hierboven:
eerst zien dat de weg werkt voordat de tweede app hem gaat
## Geblokkeerd / wacht op
Niets, behalve open punt 1 hierboven, en dat is een vraag van één regel aan de gebruiker.
Fase 4 wacht op de gebruiker: bouwen kan alleen op de Umbrel.
+1 -1
View File
@@ -353,7 +353,7 @@ Wat daar staat en wat deze app ervan doet:
| Eis | Deze app |
|-|-|
| Elke image gepind als `repo:versie@sha256:<digest>`, met `linux/amd64` én `linux/arm64` in de manifest-lijst. Verboden: `latest`, meebewegende branch-tags, een digest zonder tag | **Nog niet.** `python:3-alpine` en `nginx:alpine` staan er kaal in. Dit is de grootste openstaande eis en hij kan alleen op de Umbrel zelf, met `docker buildx imagetools inspect` |
| Elke image gepind als `repo:versie@sha256:<digest>`, met `linux/amd64` én `linux/arm64` in de manifest-lijst. Verboden: `latest`, meebewegende branch-tags, een digest zonder tag | **Half, sinds 0.1.0 (07-09-2026).** De app heeft een eigen image; de digest komt erbij bij de eerste push en dat kan alleen op de Umbrel. Wat nog ontbreekt is `linux/arm64`: `build.sh` bouwt standaard één architectuur, multi-arch zit erin als schakelaar en is nooit geprobeerd |
| Mapnaam gelijk aan het app-id, lowercase kebab-case | Klopt, maar het id heeft nu het store-voorvoegsel `whatsnext-`. Dat is een eis van een **community** store; officiële apps hebben een kaal id, dus dit wordt `electrum-gate` |
| Manifestvelden in een vaste volgorde: `manifestVersion`, `id`, `category`, `name`, `version`, `tagline`, `description`, `releaseNotes`, `developer`, `website`, `dependencies`, `repo`, `support`, `port`, `gallery`, `path`, en daarna de optionele | Klopt sinds 20-08-2026, met een toets erop. Wat de spec niet noemt (`icon`, `backupIgnore`) staat áchter die reeks, dus de kop is letterlijk goed |
| `gallery: []` bij een nieuw pakket; het store-team doet de plaatjes | Klopt al. Zie de paragraaf hieronder: de inhoud van dit veld moet bij inlevering leeg zijn, het veld zelf blijft staan |