Eén app store, twee apps

umbrelOS leest per store één repo, dus twee apps in twee repo's kan niet. Deze
repo is de store en bevat vanaf nu Electrum Gate en het werk aan Evolu Relay.
Opgezet als verse repo op verzoek van de gebruiker: de historie van ElectrumTLS
en van EvoluRelay komt niet mee.

Dat heeft één gevolg dat verder gaat dan opruimen. In de historie van
ElectrumTLS staat het domein van de gebruiker en het certificaatpad, van vóór de
opschoning van 19-08. Die komt hier niet in. Zolang die repo op de Git-server
blijft staan verandert dat niets, dus het weghalen ervan is het laatste stuk van
open punt 3 van het plan Appstore, en geen bijzaak.

De store zelf hoefde niet te veranderen: store-id whatsnext, en dus blijft het
app-id whatsnext-electrum-gate. Dat hangt aan het store-id en niet aan de URL,
dus voor umbrelOS is dit dezelfde app in een andere store. Dat de store op 19-08
naar de maker genoemd werd in plaats van naar deze ene app, betaalt zich hier
uit.

Wat de documentatie betreft is dit één wortel voor beide apps, en dat was de
reden om samen te voegen en niet de prijs ervan: de appstore-spec, het pinnen
van images en de werkwijze golden al voor allebei en stonden in twee repo's naast
elkaar. De kruisverwijzing die daarvoor nodig was (Referenties/Umbrel-appstore.md
in de oude EvoluRelay-repo) is verdwenen; wat daarin stond over de plekken waar
de relay een ander geval is, staat nu als ontwerp in het masterplan Umbrelapp §4.

Botsende namen kregen een achtervoegsel met de app, en alleen die: Publicatie
werd Publicatie-Gate en Publicatie-Relay, CHANGELOG.md werd
CHANGELOG-electrum-gate.md. Proefopstelling kreeg 007, tussen de twee bestaande
nummers, zodat de bovenkant van de reeks op tier-orde blijft staan.
CONTINUE_HERE.md heeft een kolom App, maar de tiers lopen over beide apps heen:
er is één volgorde van werken.

Electrum Gate gaat naar 0.0.15, want website, repo, support, submission en icon
wijzen nu naar UmbrelApps en zonder versieverhoging rolt dat niet uit. De release
notes leggen aan de gebruiker uit dat hij de store opnieuw moet toevoegen. Of een
geïnstalleerde app een wisseling van store-URL overleeft is nog steeds niet
uitgezocht; dat blijkt bij het omzetten.

Twee dingen in de plannen van Electrum Gate waren door deze verhuizing niet meer
waar en zijn bijgewerkt: de taak "de repo hernoemen" in fase 7 is afgevinkt, en
de repo-vorm in PLAN.md §4a toonde nog de store-id electrumtls, die al sinds
fase 7 achterhaald was.

Tests: 39 goed 0 fout en 54 goed 0 fout, niets overgeslagen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Harmen
2026-08-25 16:27:57 +02:00
co-authored by Claude Opus 5
commit 67ed9b603b
45 changed files with 8652 additions and 0 deletions
@@ -0,0 +1,107 @@
# Open punten - Webinterface
> Beslissingen die nog een **eigenaar** of een **moment** nodig hebben. Wordt een punt een taak, dan
> verhuist het naar [TAKEN.md](TAKEN.md).
>
> **Nummers blijven staan**, ook als een punt beslist is: er kan elders naar verwezen worden. Beslissen
> betekent verplaatsen naar de kop hieronder, niet hernummeren.
## Nog te beslissen
3. **Komt het activiteitenlog er?**
Toegevoegd 19-08-2026, nadat de gebruiker vroeg of er iets over live client-aanroepen te tonen is, en
bijgesteld naar logregels onder elkaar. Het ontwerp staat in [PLAN.md](PLAN.md) §4e en de kaart staat
al op de pagina in de `no data`-toestand, dus de vorm is te beoordelen zonder dat er iets gebouwd is.
Wat er beslist moet worden is niet "kan het" maar "is dit het waard, nu het niet is wat er gevraagd
werd". Er was gevraagd om **elk verzoek** te loggen, en dat kan niet zonder het Electrum-verkeer van de
gebruiker uit te lezen. Wat overblijft is een regel per verbinding en per verkeerspiek. Het vraagt een
`log_format` die langs de template-invulling moet, plus een tweede weg naar de bytetellers van een
lopende verbinding, want nginx logt een stream-sessie pas bij het sluiten en een wallet houdt hem
twaalf uur open.
**Moment:** na fase 2, want dan draait de schrijflus toch al · **Eigenaar:** gebruiker
5. **Wordt er gecontroleerd of de TLS-poort zelf antwoordt?**
Toegevoegd 19-08-2026, nadat de badge "Serving this page" eruit ging. De gebruiker wees erop dat die
badge niets zei: de pagina wordt nooit getoond aan een wallet die op 50022 verbindt. Mijn gedachte
erachter was dat de pagina en de TLS-terminatie in dezelfde container zitten, dus dat de een de ander
bewijst, maar dat stond er niet en niemand leest het zo.
Wat daarbij opvalt en het eigenlijke punt is: **de app controleert wel of de Electrum-server antwoordt,
maar niet of hij zelf antwoordt.** Dat is precies zijn enige taak. De agent zou een TLS-verbinding naar
de eigen poort kunnen opzetten en de handdruk kunnen afmaken; dat bewijst het luisteren, het certificaat
en de doorverbinding in één keer, en het is dezelfde truc waarmee de einddatum van het actieve
certificaat te lezen valt.
Waarom het nog geen taak is: het raakt de agent, en die heeft nog nooit gedraaid. Er is weinig aan om
een tweede ongeteste controle op een eerste ongeteste controle te bouwen. Zodra de agent op de Umbrel
loopt, is dit de eerstvolgende zinvolle toevoeging aan het dashboard.
**Moment:** nadat de agent draait · **Eigenaar:** gebruiker
7. **Kan Zoraxy of Nginx Proxy Manager verplicht gesteld worden bij de installatie?** Gevraagd door de
gebruiker op 20-08-2026, die erbij opmerkte dat sommige apps bij de installatie twee dropdowns tonen.
**Technisch antwoord: nee, niet als "de een of de ander".** Nagetrokken in de bron, zie
[Umbrel-appstore-spec.md](../../../Referenties/Umbrel-appstore-spec.md) §4, met bronlinks.
Kort: dat dialoog toont één dropdown **per afhankelijkheid** en niet per keuze. In het schema van
umbreld is `dependencies` een platte lijst van app-id's, en wat er in een dropdown staat komt van de
andere kant, namelijk elke app die `implements: [<dat id>]` declareert. Het voorbeeld dat de gebruiker
zag is `mempool` met `dependencies: [bitcoin, electrs]`. Zoraxy en NPM declareren geen `implements`,
dus er is geen gedeelde rol om naar te wijzen, en dat staat in hún manifest en niet in het onze.
Wat overblijft is Zoraxy hard eisen, en dat is een dropdown met één optie. Wat er nog over te beslissen
is, is of dat wenselijk is: het sluit de bron `Own folder` uit, en Zoraxy deïnstalleren zou deze app
meesleuren.
**Moment:** vrij · **Eigenaar:** gebruiker beslist
## Beslist
8. **Welke tagline, en waar?** - **Overal die van de appstore** (20-08-2026, gebruiker).
"Your own node from anywhere, without waiting for Tor" staat nu ook op de pagina; daar stond "TLS in
front of your own Electrum server".
De afweging was dat de twee lezers op een ander moment zitten: in de winkel moet de regel iemand
overtuigen die de app niet kent, op het dashboard staat iemand die hem al draait. Dat pleit voor twee
regels. Wat de doorslag gaf is dat deze afweging al eerder gemaakt is: bij het hernoemen naar Electrum
Gate was de reden dat de oude naam het **middel** beschreef en niet de opbrengst. Met die maatstaf was
de dashboardregel de achterblijver, niet de winkelregel.
Een toets houdt de twee plekken nu gelijk. Niet omdat het kan, maar omdat ze ver uit elkaar staan:
niemand die het manifest aanpast, opent daarna de pagina.
6. **Kan een certificaat via de pagina geupload worden?** - **Ja, en gebouwd** (20-08-2026, op verzoek van
de gebruiker). Zie [CHANGELOG-electrum-gate.md](../../../CHANGELOG-electrum-gate.md) 0.0.7 voor de vier
stukken die het vroeg.
Twee besluiten die eruit voortkwamen en die het waard zijn om terug te lezen:
- **de bestandsnaam komt uit het certificaat en niet uit het verzoek.** Daarmee is de hele klasse
padtrucs weg zonder dat er iets gefilterd hoeft te worden op invoer die je niet vertrouwt. Wat er
niet in het alfabet zit gaat eruit, dus een jokerteken-certificaat voor `*.example.org` wordt
`example.org`;
- **uploaden kiest niet.** De nieuwe komt voorgeselecteerd in de lijst en de gebruiker drukt op de
bestaande knop. Zo blijft er precies één plek waar TLS van certificaat wisselt, en dat is dezelfde
reden waarom de app bij twijfel niets kiest.
Het geheim door een formulier: te verantwoorden omdat het pad achter de inlog van umbrelOS zit en de
sleutel in de app-data van de gebruiker zelf landt, met rechten 0600. De agent kapt af op 96k en nginx
ook, dus een verzoek dat te groot is komt niet eens bij de validatie.
4. **Wat gebeurt er met de blokken-per-uur-grafiek in het eerste etmaal?** - **Vervallen** (19-08-2026).
De vraag ging over een grafiek die er niet meer is: de gebruiker heeft blokken per uur diezelfde dag
geschrapt, als grafiek én als tegel. Zie [PLAN.md](PLAN.md) §3.
1. **Wat toont de pagina als `status.json` ontbreekt?** - **Een neutrale melding, geen fout**
(19-08-2026).
"No readings yet", met de uitleg dat dit normaal is in de eerste minuten na een installatie of een
herstart. Bewust neutraal opgemaakt en niet rood: het bestand ontbreekt in de normale gang van zaken
even, en een foutkleur op een normale toestand leert mensen foutkleuren negeren.
Daarnaast blijft elk afzonderlijk veld op `unknown` staan in plaats van leeg. Een leeg veld leest als
"in orde", en dit is de pagina die juist geraadpleegd wordt wanneer er iets mis lijkt.
2. **Nederlands of Engels?** - **Engels, en alles** (19-08-2026, gebruiker).
Dat betekent de pagina én de teksten in `umbrel-app.yml`, want een Engelse pagina onder een
Nederlandse winkelbeschrijving is een halve keuze. Codecommentaar blijft Nederlands: de
projectafspraak koppelt zichtbare UI-tekst aan de taal van de app, en commentaar niet.
@@ -0,0 +1,288 @@
# Webinterface - plan
> Gepromoveerd op 19-08-2026 vanuit `Plannen/Masterplannen/`, omdat het werk begonnen is: de pagina is
> herbouwd. Taken staan in [TAKEN.md](TAKEN.md), open punten in [OPEN.md](OPEN.md), de geschiedenis in
> [PROGRESS.md](PROGRESS.md).
>
> **De afhankelijkheid van Configuratie is vervallen.** Die stond er omdat de pagina de ingestelde
> waarden moet tonen. In het ontwerp dat er nu ligt haalt de pagina álles uit `status.json` en uit de
> umbrel-variabelen, en staat er geen enkele installatiespecifieke waarde meer in het bestand. Daarmee
> hoefde Configuratie er niet vóór, en is dit deel van Configuratie fase 2 meteen af.
## 1. Doel
De statuspagina toont verzonnen gegevens. De statuswaarden staan als de tekst `Online` in de HTML, het
domein staat er hardgecodeerd in en er wordt niets uitgelezen. De pagina meldt dus ook "Online" als
stunnel omgevallen is, en dat is erger dan geen pagina: hij wordt geraadpleegd juist wanneer er iets mis
lijkt, en geeft dan het verkeerde antwoord.
Als dit af is, toont de pagina alleen dingen die waar zijn, of hij zegt dat hij het niet weet.
## 2. Afbakening
Vastgesteld op 18-08-2026, na het zien van de pagina in bedrijf.
- De verzonnen status en het verzonnen logvenster eruit.
- **Blokhoogte en backend-status.** De hoogte via het Electrum-protocol, plus welke implementatie actief
is en of hij antwoordt. Dit is meteen de echte gezondheidscontrole.
- **Certificaat:** geldig tot, resterende dagen, een zichtbare waarschuwing onder de dertig dagen, en het
tijdstip van de laatste herlading.
- **Reactietijd van de backend**, en de geschiedenis daarvan als sparkline.
- Aansluiten op het design-systeem in `HomeGit/Docs/website-design-system.html`: donker-eerst met een
lichte variant, tokens voor kleur en radius, en de bestaande componenten `card`, `card-stat`, `badge`,
`alert` en `list-item`.
- **Verbindingsregels per client**, met kopieerknop. Toegevoegd 19-08-2026 op verzoek van de gebruiker.
De ene wallet wil `domein:poort:s` op één regel, de andere wil host en poort in aparte velden met SSL
aangevinkt, en juist die kleine verschillen kosten mensen tijd. De rijen komen uit
[Referenties/Clients.md](../../Referenties/Clients.md) §4; wie er een bijzet, zet hem daar ook bij.
- **Een activiteitenlog**, zie §4e. Herziening van een eerder niet-doel.
## 3. Niet-doelen
- **Een backend of API.** Zodra de pagina iets moet opvragen, is er een proces nodig dat luistert, en dan
is dit geen configuratie-app meer. De weg eromheen staat in §4.
- **Live logs, in de zin van een logvenster.** Ruwe logregels in de browser vragen precies de backend uit
het vorige punt, en bij een TLS-proxy staan er verbindingsgegevens in. `docker logs` blijft de plek
voor de echte logs. Wat er **wel** komt is een uitgedunde samenvatting per sessie; zie §4e. Dat is
op 19-08-2026 herzien op verzoek van de gebruiker, en het is een andere zaak dan een logvenster.
- **Instellingen bewerken in de pagina.** Uitdrukkelijk zo besloten in het plan **Configuratie**.
- **Koersgegevens en koersgrafieken.** Besloten op 18-08-2026, na afweging. Het vraagt een externe API,
en dan haalt de browser van iedereen die dit dashboard opent data op bij een derde partij. Bij een
zelfgehoste Bitcoin-opstelling is dat precies het soort lek dat deze app juist dichtzet, en umbrelOS
heeft er bovendien al widgets voor. **Alles op het dashboard komt uit lokale bronnen.**
- **Verbindingen per uur als grafiek.** Kan wel, maar bij één of twee wallets is die grafiek vrijwel
leeg. Het activiteitenlog uit §4e dekt dezelfde vraag beter.
- **De blokhoogte zelf als grafiek.** Die loopt met ongeveer één per tien minuten omhoog, dus het is
altijd dezelfde schuine lijn.
- **Blokken per uur, in welke vorm dan ook.** Geschrapt op 19-08-2026 door de gebruiker, nadat het als
grafiek én als tegel op de pagina had gestaan. Het antwoordt op een vraag die de gebruiker niet heeft:
of de node meekomt met het netwerk is de zorg van de Electrum-server en niet van deze proxy, en de
blokhoogte zelf zegt dat al. Gevolg voor het ontwerp: `history` in `status.json` hoeft alleen nog de
reactietijd te bewaren, want de sparkline is de enige afnemer.
- **De afweging Tor tegenover TLS op het dashboard.** Ook 19-08-2026, door de gebruiker. Die uitleg is
positionering en hoort in de winkelbeschrijving in `umbrel-app.yml`, waar iemand staat te kiezen. Wie
het dashboard opent, heeft al gekozen en komt iets nakijken. De onderbouwing blijft in
[Referenties/Clients.md](../../../Referenties/Clients.md) §1 staan; die voedt nu alleen de marketing.
## 4. Ontwerp
### 4a0. Wél een backend, en waarom die er alsnog is
Herzien op 19-08-2026, nadat de gebruiker vroeg of de WebDAV-truc uit §4f van
**Configuratie** eigenlijk de gebruikelijke manier is om een backend in een Umbrel-app te bouwen. Het
antwoord was nee, en het onderzoek eromheen veranderde deze paragraaf.
**Wat umbrelOS zelf aanbiedt is één instelling per app: de afhankelijkheidskeuze.** In
`AppSettingsSchema` staat `dependencies: z.record(z.string())` en verder niets. Daarom kostte het wisselen
tussen Electrs, Fulcrum en ElectrumX twee regels: dát mechanisme bestaat. Er is géén generiek
instellingenformulier dat een community-app kan declareren, en de andere umbrel-haken (`hooks/`,
`exports.sh`, `*.template`) zijn er voor het opstarten en voor andere apps, niet voor de gebruiker.
**Apps die wél instelbare configuratie hebben, zijn zelf een backend.** Nginx Proxy Manager, Home
Assistant en Jellyfin brengen hun eigen server en hun eigen instellingenscherm mee, in hun eigen image.
umbrelOS levert dat scherm niet; de app doet het. De juiste conclusie is dus niet "umbrelOS heeft er iets
voor" maar "die apps hebben een backend".
**Besluit van de gebruiker: een tweede container met een klein programma.** Een officiële
`python:3-alpine`, met het script uit `agent.py.template` zodat het bij een update meekomt. Geen eigen
image, dus geen bouwstap, geen registry en geen multi-arch-gedoe. Dat laatste is de reden dat een eigen
image afvalt, niet de complexiteit van het programma.
Wat die keuze onderweg oploste, en dat was de eigenlijke aanleiding:
- **de shell-lus was aan zijn plafond.** Een certificaatdatum lezen, een JSON-RPC-verzoek doen, een
geschiedenis bijhouden en een keuze valideren zijn geen dingen die je met `openssl` en `nc` aan elkaar
knoopt zonder dat het stil verkeerde antwoorden gaat geven;
- **de controles op `openssl`, `nc` en de WebDAV-module vervallen alle drie.** De agent doet dat werk zelf
met de standaardbibliotheek, dus de app hangt niet meer af van wat er toevallig in de nginx-image zit;
- **het hardgecodeerde domein is weg uit `docker-compose.yml` en `nginx.conf.template`.** De agent
schrijft `cert.conf` met de paden die hij gevonden heeft, en nginx doet daar een `include` op. Daarmee
is dat deel van **Configuratie** fase 2 af.
**Wat het kost.** Een tweede container, en een echt API-oppervlak in plaats van geen. Klein, maar niet
nul, en daarom staat de verantwoording bij de `location /api/` in `nginx.conf.template` en niet alleen
hier.
**nginx herladen kan de agent niet zelf**, want dat vraagt de Docker-socket en die is er bewust uit. De
agent zet een vlagbestand neer en de nginx-container herlaadt zichzelf zodra hij dat ziet. Die lus
verplaatst wat eerst de certificaatbewaking was, en doet nu alleen nog dit ene ding.
### 4a. Statusgegevens uit status.json
De pagina vraagt nooit iets aan een dienst die kan omvallen: de agent schrijft elke ronde een
`status.json`, en de pagina haalt dat op met `fetch` en vult zichzelf. Dat blijft de kern, ook nu er een
agent is; het alternatief, een pagina die live naar de backend vraagt, zou de pagina zelf laten hangen
als de backend hangt.
Wat er in staat: het domein en de poort waar de wallet heen moet, het adres en de naam van de backend, de
blokhoogte en de reactietijd, de einddatum van het actieve certificaat, de lijst met gevonden
certificaten, een geschiedenis van reactietijden over 24 uur, en de logregels. Plus het tijdstip van
schrijven, en dat is het belangrijkste veld van allemaal.
Wat de vorm oplevert:
- **de pagina kan niet meer liegen over "draait het".** Elk veld heeft een zichtbare onbekend-toestand en
nooit een lege waarde, want een leeg veld leest als "in orde". Dit is de pagina die juist geraadpleegd
wordt wanneer er iets mis lijkt;
- **oude gegevens zijn herkenbaar, en wel bij de waarde zelf.** Onder de blokhoogte staat "as of ... ago",
de badge bij de backend gaat van groen naar grijs met "last answered", en de voettekst noemt het
tijdstip. Dat is de enige manier waarop de pagina een gestopte agent kan opmerken.
**Bewust geen melding bovenaan daarvoor.** Die heeft er even gestaan en is er op 19-08-2026 op verzoek
van de gebruiker uit. De afweging is het opschrijven waard, want hij geldt voor elke volgende melding
die iemand wil toevoegen: een banner die hetzelfde zegt als wat er drie regels lager bij het getal
staat, voegt geen informatie toe maar wel gewicht, en dat gewicht gaat ten koste van de meldingen die
wél iets nieuws zeggen. Een melding bovenaan is er voor iets wat je nergens anders ziet, zoals een
ontbrekend certificaat;
- **de lus valt nooit stil.** Een mislukte ronde wordt gelogd en overgeslagen; stoppen zou de pagina op
oude gegevens bevriezen zonder dat iemand het ziet.
### 4a1. De indeling van de pagina
Vastgesteld door de gebruiker op 19-08-2026, na het bekijken van de eerste versie.
**De volgorde, van boven naar beneden:** Electrum-server, de drie statustegels, de certificaatkeuze, het
activiteitenlog, en onderaan het instellen van je wallet. De regel erachter: **eerst waar je naar kijkt
als je iets nakomt, onderaan waar je naar kijkt als je iets instelt.** Dat laatste doe je één keer.
De laatste twee zijn op verzoek van de gebruiker omgedraaid ten opzichte van de eerste opzet. Dat pakte
beter uit dan alleen als voorkeur: het certificaatkader is nu het smalle kader links en het log het brede
rechts, waardoor de kolomgrens samenvalt met die van de rij erboven in plaats van ertegenin te lopen. En
logregels zijn monospace, dus die hebben de breedte beter nodig dan een lijst met keuzerondjes.
**Twee kaders zijn er één geworden.** "Point your wallet here" en "Setting up your wallet" stelden dezelfde
vraag, dus het verbindingsadres met de kopieerknop staat nu bovenaan het instelkader in plaats van in een
eigen kader erboven.
**Breed, met kaders naast elkaar.** Twaalf kolommen, en de rijen wisselen 5/7 en 7/5 af: de
Electrum-server naast de statustegels, het log naast de certificaatkeuze, en het instelkader vol breed.
Afgekapt op 1760px, want op een ultrawide monitor levert een kader van 2500px regels op die niemand leest.
**Elk kader is hetzelfde opgebouwd.** Een `card-head` met een titel in `t-h3`, dan de inhoud. Bijgesteld op
19-08-2026 door de gebruiker: de drie statustegels hadden een eigen kleine grijze kop in kapitalen, en dat
maakte ze een apart soort ding zonder dat daar een reden voor was. De titel gebruikt de token
`--text-primary` en niet letterlijk wit, want in de lichte variant is wit onzichtbaar.
Wat daarbij vastligt en niet per ongeluk mag verschuiven:
- **de leesvolgorde in de HTML ís de bedoelde volgorde.** Er staat nergens een `order` of een `row-start`
die de opmaak laat afwijken van de bron, dus bij een smal scherm stapelt alles precies zoals het gelezen
hoort te worden. Wie hier een kader bijzet, zet het op de goede plek in de HTML en niet op de goede plek
in het raster;
- **de statustegels rekken mee met hun buur**, met de titel boven en het getal onder, want van zichzelf
zijn ze lager dan het kader ernaast en dan staat er een gat rechtsboven. De onderregel van een tegel
heeft daarvoor dezelfde hoogte als de sparkline, anders staat het getal van "Backend response" hoger dan
de andere twee;
- **het logblok scrollt zelf horizontaal in plaats van af te kappen.** Met puntjes zou een smal scherm de
bytes stil verbergen, en dat is dezelfde soort onwaarheid als een verzonnen statuswaarde;
- **twee kaders naast elkaar zijn even hoog**, en het langste bepaalt de rij. Wat mag meegroeien zegt dat
zelf: het logblok en de certificaatlijst. Daardoor is de certificaatlijst een scrollend vak in plaats van
een lijst die de pagina oprekt, en dat was nodig: een gedeelde certificatenmap kan er tientallen bevatten.
Het aantal staat in de kop, want zodra er een scrollbalk is, is niet meer te zien hoeveel er onder de rand
staan.
**Bewust een lijst en geen dropdown.** De gebruiker vroeg op 19-08-2026 of een dropdown niet netter was,
en dat is het visueel ook, maar het botst met wat dit besturingselement moet doen: je kiest hier op de
hostnaam waarop je wallet verbindt, en dan wil je bron, naam en resterende dagen naast elkaar kunnen
vergelijken. In een dropdown zie je er één per keer, en een verlopen certificaat kan er niet rood in
omdat `option`-opmaak per browser verschilt. De hoogtewinst die de dropdown zou opleveren komt er met een
scrollend vak ook, dus er hoefde niets ingeleverd te worden;
- **er is geen voetregel.** Er heeft er een gestaan met "Last reading ... ago" en het versienummer; die is
er op verzoek van de gebruiker uit. De leeftijd stond al bij de waarden zelf, dus dat was een derde keer
hetzelfde.
Het **versienummer** is wel gebleven, klein achter de tagline als `v0.0.3`. Dat is een aparte afweging en
hij viel andersom uit dan de rest van de voetregel: dit project verloor een keer een dag aan een wijziging
die niet uitrolde door een niet-verhoogde `version`, en dan is "welke versie zie ik nu eigenlijk" precies
de vraag die je stelt. Het staat in `--text-sec` en niet in `--text-ter`, want die laatste is in de lichte
variant `#bbbbbb` op wit en op deze grootte niet te lezen.
### 4b. Waarschuwen op een aflopend certificaat
Met de einddatum in `status.json` is dit een vergelijking in de pagina zelf: onder de dertig dagen een
opvallende melding, verlopen een duidelijke fout. Dat is de enige echte toevoeging ten opzichte van nu,
en hij is goedkoop omdat de datum er toch al is.
### 4c. Welke backend er gekozen is
Die volgt uit `${APP_ELECTRS_NODE_IP}` en `${APP_ELECTRS_NODE_PORT}`, maar dat is een IP-adres en geen
naam. De adressen liggen per app vast (Electrs op `10.21.21.10`, Fulcrum op `10.21.21.200`, ElectrumX op
`10.21.21.199`), dus een vertaaltabel kan er een naam van maken. Dat is aardig, maar het is een tabel die
stilzwijgend veroudert als Umbrel de adressen wijzigt. Voorstel: het adres tonen, en de naam alleen als
extra wanneer hij in de tabel staat. Dan is de pagina bij veroudering minder informatief in plaats van
onwaar.
### 4e. Het activiteitenlog
Toegevoegd 19-08-2026, nadat de gebruiker vroeg of er iets over live client-aanroepen te tonen is, en
diezelfde dag bijgesteld naar logregels onder elkaar in plaats van een lijst met sessiekaarten.
**Eerst de grens, want die bepaalt de rest. Een regel per protocolaanroep kan niet.** De wallet doet zijn
verzoeken *binnen* één TLS-verbinding die hier getermineerd wordt en daarna als bytestroom naar de backend
gaat. nginx `stream` kent geen verzoeken, alleen verbindingen. Ze wél tellen zou betekenen dat de app de
Electrum-berichten van de gebruiker uitleest, en dat is precies het verkeer waarvoor deze app bestaat. Dat
is geen implementatiedrempel maar een ontwerpgrens, en hij hoort ook op de pagina te staan zodat niemand
denkt dat het log iets verzwijgt.
Wat er wél per regel in kan:
| Gebeurtenis | Waar het vandaan komt |
|-|-|
| `connect` | de verbindingsteller is opgelopen sinds de vorige ronde |
| ~~`traffic`~~ | vervallen op 20-08-2026, zie punt 2 onderaan deze paragraaf |
| `disconnect` | de `access_log`-regel van nginx, met duur en bytes |
| `probe` | een sessie die eindigde zonder één byte in beide richtingen. Toegevoegd 20-08-2026: een doorgestuurde poort wordt gescand, en zonder dit onderscheid gaf elke scan een rode `refused`-regel terwijl er niets geweigerd is. Bewust op de bytes en niet op de duur of de status: een scan die tien seconden open blijft is nog steeds een scan, en een echte sessie die na een halve seconde omvalt is nog steeds een storing |
| `refused` | de verbinding gaf wél verkeer door en liep daarna stuk. Dit is het geval dat rood mag zijn |
| `reload` | het certificaat is gewijzigd en nginx is herladen |
| `start` | de container is gestart en luistert |
Die regels komen als `log` in `status.json`, afgekapt op 24 uur. De pagina toont ze nieuwste bovenaan, in
een monospace-blok dat scrollt.
**Bewust niet gelogd: het IP-adres van de client en de bronpoort.** Dat is precies het soort gegeven dat
deze app van het netwerk af houdt, en om te zien dát het werkt is het niet nodig.
Twee dingen die eerst uitgezocht moeten worden, en die het bouwen kunnen blokkeren:
1. **Een `log_format` bevat nginx-variabelen met een dollarteken, en dit bestand is een template.** De
invulling bij het starten vervangt die en zou ze leegmaken; dat is dezelfde valkuil die nu bovenaan
`nginx.conf.template` beschreven staat. De uitweg is de `log_format` niet in het template te zetten
maar door het `command`-blok in `docker-compose.yml` te laten wegschrijven als een `include`-bestand,
want daar wordt een dollarteken al als `$$` ontsnapt. Te verifiëren.
2. **nginx `stream` schrijft zijn regel pas bij het sluiten van de sessie.** Een verbinding die twaalf uur
openstaat, verschijnt dus pas na afloop, en dat is de normale toestand van een wallet. Daarom komen
`connect` en `traffic` niet uit de log maar uit de lopende verbinding; anders lijkt een actieve wallet
afwezig. **Uitgezocht en gebouwd op 20-08-2026, nadat de gebruiker meldde dat hij van zijn verbonden
wallet niets terugzag:**
- **`connect` komt uit `/proc/net/tcp`.** Kolom 2 is het lokale adres met de poort in hex, kolom 4 de
toestand; tellen wat op `:C366` staat met toestand `01` geeft het aantal open wallet-verbindingen.
Dat bestand geldt **per netwerk-namespace**, en daar zit de bevinding: de agent kan het niet lezen,
want hij zit in een andere container. De teller staat daarom in de achtergrondlus van de
nginx-container, die het aantal naar een bestand schrijft dat de agent oppikt. Zelfde patroon als de
herlaadvlag, en om dezelfde reden: geen Docker-socket. Geteld wordt alleen het aantal;
- **`traffic` vervalt.** Bytes van een lopende sessie zijn er niet af te lezen: `/proc/net/tcp` heeft
geen tellers, en de interfacetellers van de container bevatten ook het dashboard- en backendverkeer.
Wat er dan overblijft is een schatting die als meting leest, en dat is precies wat §4c verbiedt. De
sessieregel bij het sluiten geeft de echte aantallen;
- **de pagina toont de teller in de badge van het activiteitenlog** en niet als logregel alleen. De
vraag is "is mijn wallet nu verbonden", en die hoort niet uit een lijst afgeleid te worden.
## 5. Het werk
Staat in [TAKEN.md](TAKEN.md), met de fase-indeling en wat er af is.
## 6. Open punten
Staan in [OPEN.md](OPEN.md).
## 7. Verificatie
Handmatig, want het gaat om wat er in een browser staat:
- de pagina toont het werkelijk ingestelde domein en de werkelijke poorten, niet die uit de HTML;
- na een certificaatvernieuwing verandert de einddatum op de pagina;
- **stunnel of de proxy stoppen laat de pagina niet "Online" tonen.** Dit is de controle die de aanleiding
van dit plan afdekt en de enige die per se gedaan moet worden;
- een certificaat dat binnen dertig dagen verloopt, geeft een zichtbare waarschuwing.
@@ -0,0 +1,339 @@
# Voortgang - Webinterface
## 20-08-2026 - scans zijn geen weigeringen, en meer lucht tussen de kaders
De gebruiker vroeg wat een logregel `refused ... status 500` met nul bytes betekende. Antwoord: een
TLS-handdruk die niet is afgemaakt, en op een doorgestuurde poort vrijwel altijd een scanner. Dat is geen
storing, maar het log liep er wel vol met rode regels die suggereerden dat de app iets geweigerd had.
**Nu een eigen soort `probe`, en de grens ligt bij de bytes.** Niet bij de duur en niet bij de status: een
scan die tien seconden open blijft is nog steeds een scan, en een sessie die na een halve seconde omvalt
maar wél verkeer had is nog steeds een storing. Dat de bytetellers niets over de handdruk zeggen, bleek uit
de sessies van de gebruiker zelf: een mislukte verbinding kwam op 0 en 0 uit terwijl er wél een handdruk
geprobeerd is. Rood is nu voorbehouden aan het geval waar je iets aan moet doen.
Verder de ruimte tussen de kaders van 1 naar 1,5rem. De kaders hebben 1,8rem binnenin, dus de ruimte ertussen
was kleiner dan die erbinnen; dat is de reden dat het krap aanvoelde.
**Geraakt:** `agent.py.template`, `index.html.template`, `umbrel-app.yml` (0.0.14), `PLAN.md` §4e-tabel,
`tests/test_agent_certificates.py`. **Tests:** 54 goed 0 fout en 39 goed 0 fout, niets overgeslagen; de
indeling van de sessieregels is mutatie-getest met een grens op de duur in plaats van de bytes, en dat viel
om zoals het moest.
## 20-08-2026 - de mobiele opmaak, en een tegel minder
De pagina liep op een telefoon buiten beeld. **De oorzaak was één regel CSS:** de clientlijst stond op
`minmax(420px, 1fr)`, en dat eist een track van minstens 420 pixels ook op een smaller scherm. Nu met
`min(420px, 100%)` eromheen. Daarbij vouwen de lijstregels en de logregels onder de 700 pixels om, met de
waarde onder de titel in plaats van ernaast.
Voor de logregels is dat een **herziening** van een eerdere keuze: die scrollen liever dan dat ze afkappen,
omdat de kolomvorm dan blijft staan. Dat argument geldt op een breed scherm en daar blijft het zo, maar op
een telefoon betekende het dat je de bytes nooit zag.
Verder is de tegel met het aantal dagen tot het certificaat verloopt eruit, op verzoek van de gebruiker.
Dezelfde redenering als bij de badges: de kaart eronder zegt het al, en met een datum erbij. Backend
response is nu twee kolommen breed, want de sparkline is het enige op die rij dat met breedte iets doet.
**Diezelfde dag nagekeken op een telefoon en het ziet er goed uit.** Eén regressie kwam eruit: door het
omvouwen volgde de kopieerknop de tekst, en het ene adres is langer dan het andere, dus stonden de knoppen
niet op één lijn. Verholpen in **0.0.11** met `margin-left: auto` op de knop, en niet met `space-between` op
de rij: bij de gesplitste weergave staat er ook een notitie naast de waarde die daar tegenaan hoort te
blijven staan.
Bijvangst die geen opmaak is: **`0.0.10` na `0.0.9` leverde gewoon een update op.** Daarmee is de aanname
uit die release bevestigd en is een tekstvergelijking met groter-dan uitgesloten. Staat in de naslag, want
het scheelt de volgende keer een omweg via `0.1.0`.
**Geraakt:** `index.html.template`, `umbrel-app.yml` (0.0.10 en 0.0.11), `CHANGELOG.md`,
`Referenties/Umbrel-appstore-spec.md`. **Tests:** 39 goed 0 fout op de manifest- en configuratietoetsen; aan
de agent is niets geraakt.
## 20-08-2026 - nagekeken in de browser, en dit plan is nu tier A
Na de verse installatie van 0.0.9 heeft de gebruiker alles nagekeken wat alleen met de hand kan.
**Uploaden werkt, kiezen werkt, ook met de certificaten uit Zoraxy**, de keuzelijst bij het installeren zag
er goed uit en het versienummer op de pagina klopt. Daarmee is het uploadpad uit 0.0.7 bewezen op het
gelukkige pad; alleen de weigering bij een sleutel die niet bij het certificaat hoort is nog niet in een
browser gezien.
**Dit plan is naar tier A gegaan** en van nummer 020 naar 005. Niet omdat er meer werk bij kwam, maar omdat
het het enige plan is met werk dat nu te doen is: **Appstore** is in de kern af en wacht op een herstart die
niet te plannen is, en **Publicatie** wacht op een publieke repo.
Eén nieuwe bevinding van de gebruiker om mee te beginnen: in mobiele weergave lopen de verbindingsregels
met hun kopieerknoppen en de logregels buiten beeld. Dat laatste is een bewuste keuze geweest (de logregels
scrollen liever dan dat ze afgekapt worden), maar op een telefoon is dat het verkeerde antwoord.
**Geraakt:** alleen documentatie. **Tests:** niet van toepassing.
## 20-08-2026 - uploaden gebouwd, en de pagina verder uitgekleed
**Een certificaat uploaden kan nu via de pagina**, open punt 6. Het echte werk zat niet in het formulier
maar in de validatie: het paar gaat onder een `.tmp`-naam naar de doelmap en wordt daar door
`ssl.SSLContext.load_cert_chain` geopend, dezelfde OpenSSL die nginx straks gebruikt. Zonder die controle
levert een verkeerde sleutel een nginx die niet meer herlaadt, en dat is dezelfde klasse storing als
waardoor 0.0.3 niet startte. **De bestandsnaam komt uit het certificaat zelf**, dus de hele klasse padtrucs
is weg zonder invoerfiltering. Uploaden kiest niet: de nieuwe komt voorgeselecteerd in de lijst.
Twee dingen die bij het bouwen bijna fout gingen en het opschrijven waard zijn: `os.replace` uit `/tmp`
naar `/certs/own` faalt met EXDEV omdat dat een bind-mount is, en `-subj "/CN=../.."` in openssl leest de
schuine streep als scheidingsteken, waardoor de padtruc-toets zichzelf stil oversloeg.
Verder op verzoek van de gebruiker: het app-icoon in de kop in plaats van het schildje, de badges uit de
hoek van álle kaders, "your choice" weg achter het actieve certificaat, en Trezor Suite is niet
desktop-only. Die laatste is de tweede keer dat leveranciersdocumentatie het aflegt tegen één keer kijken.
Bijgevangen: de teller zweeg over een wallet die al verbonden was voordat de agent begon, en dat is precies
het geval waarin iemand komt kijken.
Aan het eind van de dag nog één tekstbesluit: de pagina had een andere tagline dan de appstore, en die van
de appstore wint (0.0.8). Zie [OPEN.md](OPEN.md) punt 8; de reden is dezelfde maatstaf als bij het
hernoemen van de app, namelijk de opbrengst en niet het middel.
**Geraakt:** `agent.py.template`, `index.html.template`, `nginx.conf.template`, `docker-compose.yml`,
`umbrel-app.yml` (0.0.7 en 0.0.8), `OPEN.md` punt 6 en 8, `Referenties/Clients.md`, beide testbestanden.
**Tests:** 48 goed 0 fout en 24 goed 0 fout, niets overgeslagen; de sleutelcontrole en de beschrijfbare
mount zijn mutatie-getest. **Niet geverifieerd:** het uploaden in een echte browser.
## 20-08-2026 - de pagina naast een werkende app, en §4e afgemaakt
Eerste sessie met een app die echt draait, en dat leverde meteen de bevinding op waar §4e om vroeg: de
gebruiker zag van zijn verbonden wallet niets terug. Oorzaak is bekend en staat in het plan, namelijk dat
nginx `stream` pas bij het sluiten van een sessie logt. Het open punt was hoe je de lopende verbinding dan
wél afleest. **Antwoord: `/proc/net/tcp`, geteld in de nginx-container**, want dat bestand geldt per
netwerk-namespace en de agent zit in een andere. Het aantal gaat via een bestand naar de agent, net als de
herlaadvlag. **`traffic` is daarbij vervallen:** bytes van een lopende sessie zijn er niet af te lezen, en
een schatting die als meting leest is precies wat §4c verbiedt.
Daarnaast een reeks verzoeken van de gebruiker, alle om dezelfde reden: de pagina was te druk. De
certificaatkeuze is een dropdown geworden (met veertien certificaten was die lijst het drukste onderdeel
van de pagina, voor iets wat je één keer doet), `disconnected` heet `session ended` met leesbare details
erachter, en er zijn vijf stukken tekst geschrapt: de voetnoot onder het log, de regel bij Certificate, de
stip in de badges, de zin over apps op de Umbrel zelf en de uitleg over één regel versus twee velden.
Bijgevangen bij de dropdown: de oude keuzelijst zette zichzelf elke ronde terug, dus een keuze verdween
binnen tien seconden weer. Verder heet Blockstream Green nu Blockstream; die app is omgedoopt.
**Geraakt:** `index.html.template`, `agent.py.template`, `docker-compose.yml`, `umbrel-app.yml` (0.0.6),
`PLAN.md` §4e, `OPEN.md` punt 6 en 7, `Referenties/Clients.md`, `tests/`. **Tests:** 29 goed 0 fout en 19
goed 0 fout, niets overgeslagen; de teller en de logregel zijn mutatie-getest. **Niet geverifieerd:** alles
in de browser, dus de dropdown, de hoogtes en de badge. Dat is handwerk van de gebruiker.
## 19-08-2026 - sessie afgesloten; plan zakt naar tier C
Laatste twee wijzigingen van de dag: certificaat en activiteitenlog omgedraaid, en het losse
verbindingsadres met kopieerknop eruit samen met het label "tested clients". Het omdraaien pakte beter uit
dan als voorkeur alleen, want de kolomgrens valt nu samen met die van de rij erboven (x=738 in beide rijen)
en logregels hebben de breedte beter nodig dan keuzerondjes. Het adres verdween zonder verlies: het staat
bij elke clientregel in de vorm die díe client wil, met kopieerknop.
**Het plan zakt van A naar C**, wachtend op de installatie. De pagina is af voor zover dat zonder de Umbrel
kan; al het resterende werk hier begint met kijken of de agent doet wat hij zou moeten doen.
**Geraakt:** `whatsnext-electrum-gate/index.html.template`, de plannen, `CONTINUE_HERE.md`.
**Tests:** 21 goed, 0 fout. Die raken de pagina niet.
**Niet geverifieerd:** de agent heeft nooit gedraaid, en ik heb de pagina zelf nooit gezien; het
browserpaneel bleef dicht, dus alles is gemeten in plaats van bekeken.
## 19-08-2026 - voetregel weg, certificaatlijst scrollt, kaders gelijk
De voetregel met "Last reading ... ago" en het versienummer is eruit; de gebruiker houdt van een rustige
interface en de leeftijd stond al bij de waarden zelf. Het **versienummer** is op verzoek dezelfde dag
teruggezet, klein achter de tagline als `v0.0.3`: dit project verloor een keer een dag aan een wijziging
die niet uitrolde door een niet-verhoogde `version`, en dan is dat precies de vraag die je stelt. Het
staat in `--text-sec`, want `--text-ter` is in de lichte variant `#bbbbbb` op wit en op die grootte niet
te lezen.
De gebruiker vroeg of de certificaatlijst een scrollbalk moest krijgen of beter een dropdown werd, en of
het logblok en het certificaatkader dan even hoog konden. Het is een scrollende lijst geworden en geen
dropdown, want je kiest hier op de hostnaam waarop je wallet verbindt en dan wil je bron, naam en
resterende dagen kunnen vergelijken; in een dropdown zie je er één per keer en kan een verlopen
certificaat er niet rood in. De hoogtewinst kwam er met een scrollend vak toch, dus er hoefde niets
ingeleverd te worden. Het aantal staat nu in de kop, want met een scrollbalk is niet meer te zien hoeveel
er onder de rand staan.
Gemeten met negentien certificaten: 1062px inhoud in een vak van 360px, en de knop blijft eronder staan.
Met vier: geen scrollbalk, dus geen leeg vak. In beide gevallen zijn de twee kaders exact even hoog, 773px
respectievelijk 636px. Eerst stond het vak op 460px, maar dan werd de rij 873px en vulde die op een
1080p-scherm het hele beeld.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` en de plannen.
**Tests:** 21 goed, 0 fout; die raken de pagina niet. Nagemeten op 1760px en op mobiel, in beide gevallen
zonder horizontale schuifbalk en zonder console-fouten.
## 19-08-2026 - kaders gelijkgetrokken, en een badge die niets zei
De drie statustegels hadden een eigen kleine grijze kop in kapitalen en de andere kaders een witte titel.
Op verzoek is dat nu overal hetzelfde: een `card-head` met een titel in `t-h3`. Wel met de token
`--text-primary` en niet met letterlijk wit, want in de lichte variant is wit onzichtbaar. Bij het
nameten bleek het getal van "Backend response" 21px hoger te staan dan de andere twee, omdat daar een
sparkline onder hangt in plaats van een tekstregel; de onderregel van een tegel heeft nu dezelfde hoogte
als die sparkline.
De badge "Serving this page" is eruit. De gebruiker wees erop dat die niets zegt: de pagina wordt nooit
getoond aan een wallet die op 50022 verbindt. Mijn gedachte erachter was dat de pagina en de
TLS-terminatie in dezelfde container zitten en dat de een de ander dus bewijst, maar dat stond er niet en
zo leest niemand het. Een badge waarvan de betekenis niet in één zin op te schrijven is, hoort er niet te
staan.
Wat dat wel opleverde, en dat is als open punt 5 vastgelegd: **de app controleert of de Electrum-server
antwoordt, maar niet of hij zelf antwoordt**, en dat is zijn enige taak. Een TLS-verbinding naar de eigen
poort zou het luisteren, het certificaat en de doorverbinding in één keer bewijzen. Nog geen taak, want
het raakt de agent en die heeft nog nooit gedraaid.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` en de plannen.
**Tests:** 21 goed, 0 fout; die raken de pagina niet. Gemeten in beide thema's en op 1760px: zeven
identieke koppen, drie getallen op één lijn, geen horizontale schuifbalk, geen console-fouten.
## 19-08-2026 - de vervallen-melding eruit
De gebruiker vond de melding "Readings are out of date" niet nuttig, en dat klopt: dezelfde mededeling
staat al onder de blokhoogte ("as of ... ago"), in de badge bij de Electrum-server die grijs wordt met
"last answered", en in de voettekst met het tijdstip. De banner was een vierde keer hetzelfde en wel de
hardste.
De berekening blijft staan, want die stuurt de badge; alleen de melding is weg. Gecontroleerd met een
`status.json` van een uur oud: geen enkele melding zichtbaar, badge grijs met "last answered 2 hours ago",
"as of 2 hours ago" onder de blokhoogte, en de voettekst met datum en tijd. Er gaat dus geen informatie
verloren.
De afweging staat nu in [PLAN.md](PLAN.md) §4a, omdat hij geldt voor elke volgende melding die iemand wil
toevoegen: een banner is er voor iets wat je nergens anders ziet.
Wat de gebruiker zag was trouwens de preview en niet de app: die `status.json` is een vast bestand dat
niets ververst, dus die verloopt altijd als je het tabblad open laat staan.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` en de plannen.
**Tests:** 21 goed, 0 fout; die raken de pagina niet.
## 19-08-2026 - de pagina opnieuw ingedeeld, breed en met kaders naast elkaar
De gebruiker zag dat "Point your wallet here" en "Setting up your wallet" dezelfde vraag stelden. Die zijn
er één geworden, en dat kader is naar onderen verhuisd. De volgorde is nu: Electrum-server, de drie
statustegels, het activiteitenlog, de certificaatkeuze, en onderaan het instellen. De regel erachter is de
moeite van het opschrijven waard, want hij beslist waar een volgend kader komt: eerst waar je naar kijkt
als je iets nakomt, onderaan waar je naar kijkt als je iets instelt.
Op verzoek ook breed, met kaders naast elkaar: twaalf kolommen, rijen die 5/7 en 7/5 afwisselen, afgekapt
op 1760px omdat een kader van 2500px regels oplevert die niemand leest. Belangrijk detail dat bewust zo is
gebouwd: er staat geen `order` in de CSS, dus de leesvolgorde in de HTML is de bedoelde volgorde en
stapelen op een smal scherm geeft exact dezelfde reeks.
Twee dingen die het meten opleverde en die anders waren blijven staan. De statustegels waren 146px naast een
kader van 293px, dus stond er een gat rechtsboven; ze rekken nu mee en zetten hun inhoud onderaan. En het
logblok kapte op een smal scherm de bytes af met puntjes; het scrollt nu zelf horizontaal, want stil
verbergen is dezelfde soort onwaarheid als een verzonnen statuswaarde.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` en de plannen.
**Tests:** de suite raakt de pagina niet; wel gedraaid en groen (21). Gecontroleerd via de afmetingen van
de kaders op 1760px en op mobiel: volgorde gelijk, geen horizontale schuifbalk op de pagina, logblok
scrollt zelf, geen console-fouten.
**Niet geverifieerd:** ik heb de pagina niet met eigen ogen gezien; het browserpaneel stond dicht, dus dit
is gemeten en niet bekeken.
## 19-08-2026 - een tweede container, en de eerste tests in dit project
De gebruiker vroeg of de WebDAV-truc de gebruikelijke manier is om een backend in een Umbrel-app te
bouwen. Dat was hij niet, en het uitzoeken veranderde het ontwerp. umbrelOS biedt precies één instelling
per app, de afhankelijkheidskeuze in `AppSettingsSchema`; apps met instelbare configuratie zijn zelf een
backend, in hun eigen image. De gebruiker koos daarop een tweede container met een klein python-programma,
uit een `*.template` zodat het bij een update meekomt, zonder eigen image.
Dat loste meer op dan de keuzelijst. De controles op `openssl`, `nc` en de WebDAV-module zijn alle drie
vervallen, want de agent doet dat werk met de standaardbibliotheek. En het hardgecodeerde domein is weg uit
`docker-compose.yml` en `nginx.conf.template`: de agent schrijft `cert.conf` en nginx doet daar een
`include` op. Daarmee is dat deel van **Configuratie** fase 2 af.
Twee dingen die het bouwen opleverde. De standaardbibliotheek heeft geen X.509-parser, dus die is er nu:
een DER-lezer voor de einddatum en de domeinnamen. Dat is precies het soort code dat niet faalt met een
fout maar met een verkeerd antwoord, en een certificaatdatum die er een jaar naast zit valt nooit op.
Daarom is hij getoetst tegen `ssl` op 74 echte CA-certificaten, op een levend servercertificaat voor de
subjectAltName, en op beide tijdvormen met zelfgebouwde certificaten, want het CA-materiaal gebruikt
uitsluitend UTCTime na 2000. Dit project had nog geen suite; die staat nu in `tests/` en `CLAUDE.md`
vertelt hoe je hem draait.
Het tweede: bij het schrijven van de compose bleek mijn eigen `choose` het plan **Configuratie** §4c tegen
te spreken. Die koos bij meerdere certificaten "de langst geldige", terwijl daar uitdrukkelijk staat dat er
niet gegokt mag worden. Nu weigert de app en noemt de kandidaten, en de pagina zet er een foutmelding
boven, want geen certificaat betekent geen TLS.
**Geraakt:** nieuw `whatsnext-electrum-gate/agent.py.template`, nieuw `tests/`, nieuw `CLAUDE.md`,
gewijzigd `docker-compose.yml`, `nginx.conf.template`, `index.html.template`, en de plannen.
**Tests:** 21 goed, 0 fout. Mutatietest gedaan op de twee guards: de keuze-validatie en de verloopfilter,
allebei met de juiste enkele test die omvalt.
**Niet geverifieerd:** de agent heeft nog nooit op de Umbrel gedraaid. Alles over de echte werking, dus de
certificaatscan op de Zoraxy-map, de Electrum-vraag, het herladen via de vlag en de keuze via de API, staat
nog open.
## 19-08-2026 - certificaatkeuze op het dashboard
De gebruiker vroeg hoe je aanwijst welk certificaat van welke app je wilt gebruiken, en koos daarbij voor
een keuzelijst op het dashboard boven een sleutel in een configuratiebestand. Dat draait het niet-doel
"geen instellingenscherm in de web-UI" terug, en dat is opgeschreven als uitzondering met de reden erbij:
dit is de enige instelling waarvan de app de mogelijke waarden zelf al kent, want hij kijkt in de
gemounte mappen.
Terugschrijven kan zonder backend met de WebDAV-module van nginx: één `location` die een `PUT` van
hooguit een kilobyte aanneemt, naar `${APP_DATA_DIR}/config/` zodat de keuze een herstart overleeft.
Of die module in de image zit, is de eerste van drie controles die nu op de Umbrel moeten gebeuren.
Twee dingen bewust níet gedaan. De mount voor Nginx Proxy Manager staat uitgecommentarieerd, want het pad
is een gok en Docker maakt een ontbrekend bind-mountpad aan; dat zou een lege maphierarchie neerzetten in
de app-data van een app die er misschien niet is. En de pagina meldt geen succes na het opslaan: nginx
moet het certificaat nog herladen, en dat blijkt pas uit de volgende `status.json`.
**Geraakt:** `index.html.template`, `nginx.conf.template`, `docker-compose.yml`, en de plannen
Webinterface en Configuratie.
**Tests:** geen suite. In de lokale render gecontroleerd dat de lijst vier certificaten toont met het
verlopen exemplaar als zodanig, dat het actieve aangevinkt staat, en dat de knop pas aangaat bij een
andere keuze. **Het wegschrijven zelf is niet geprobeerd**, want daar is de Umbrel voor nodig.
## 19-08-2026 - dashboard uitgedund na de eerste blik
De gebruiker heeft de pagina bekeken en er drie dingen af gehaald: de grafiek blokken per uur, de tegel
blokken laatste uur, en het kader over Tor tegenover TLS. De eerste twee beantwoorden een vraag over de
Electrum-server en niet over deze proxy; de derde is positionering en hoort in de winkelbeschrijving,
waar iemand nog staat te kiezen. Er blijven drie tegels over. `history` in `status.json` hoeft daardoor
alleen nog de reactietijd te bewaren.
De badge bij de Electrum-server zei `answering`, en op de vraag wat dat betekende was er geen goed
antwoord: het liet in het midden of dat nú gold of ooit. Hij noemt nu de meting en het moment, dus
`answered 2 minutes ago`, `no answer 2 minutes ago` of `not checked yet`.
De activiteitenkaart is omgebouwd tot logregels onder elkaar in monospace, nieuwste bovenaan. Daarbij
hoort een grens die op de pagina zelf staat: een regel per protocolaanroep kan niet, want die verzoeken
zitten in de versleutelde verbinding en ze tellen zou betekenen dat de app het verkeer van de gebruiker
uitleest.
Kopieerknoppen in een lijstregel verschijnen nu bij hover. Met `opacity` en niet met `display`, zodat ze
met de tab-toets bereikbaar blijven; op aanraakschermen staan ze altijd aan.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` en de plannen.
**Tests:** geen suite. In de lokale render gecontroleerd: drie tegels, zestien logregels, geen
console-fouten, blokkengrafiek en Tor-kader weg.
## 19-08-2026 - plan werd actief, en de pagina is herbouwd
Gepromoveerd vanuit `Plannen/Masterplannen/` omdat het werk begon. De oude pagina beweerde `Online` als
platte tekst in de HTML, had een verzonnen logvenster en noemde een hardgecodeerd domein. Die is
vervangen door een pagina die alles uit `status.json` haalt en elk veld dat hij niet kent zichtbaar op
`unknown` laat staan.
Twee dingen die het ontwerp veranderd hebben ten opzichte van het masterplan. De pagina is verhuisd van
`web/index.html` naar `index.html.template` in de app-root: umbreld ververst bij een update alleen een
whitelist, dus onder `web/` zou elke latere wijziging stilzwijgend niet aankomen en een herinstallatie
kosten. En doordat de pagina niets meer hardgecodeerd heeft, is de afhankelijkheid van **Configuratie**
vervallen; dat deel van Configuratie fase 2 is hiermee meteen af.
Op verzoek van de gebruiker zijn er drie dingen bijgekomen: verbindingsregels per client met een
kopieerknop (uit `Referenties/Clients.md` §4), een korte eerlijke uitleg over Tor tegenover TLS, en een
kaart voor wallet-activiteit die nu op `no data` staat zodat de vorm beoordeeld kan worden voordat er
iets voor gebouwd wordt. Dat laatste is een herziening van het niet-doel "live logs", en de grens ligt
bij een samenvatting per sessie zonder client-IP.
**Geraakt:** `whatsnext-electrum-gate/index.html.template` (was `web/index.html`),
`nginx.conf.template`, `docker-compose.yml`, `umbrel-app.yml`.
**Tests:** dit project heeft geen suite. Handmatig gecontroleerd in een lokale render met een
voorbeeld-`status.json`: beide thema's, alle kaarten, geen console-fouten.
**Nog niet geverifieerd:** alles wat een echte `status.json` vraagt, want die wordt nog niet geschreven
(fase 2). De kopieerknop is niet achter de app-proxy van umbrelOS geprobeerd, en dat is juist het pad
waar de terugval voor http gebruikt wordt.
@@ -0,0 +1,210 @@
# Taken - Webinterface
> Prioriteit: **A** | Wacht op:
>
> **Van C naar B naar A op 20-08-2026, op één dag.** Eerst verviel de blokkade "de app geïnstalleerd en de
> agent draaiend". Aan het eind van die dag is dit het enige plan met werk dat nú te doen is: **Appstore**
> heeft alleen nog een herstart nodig die niet te plannen is, en **Publicatie** wacht op een publieke repo.
> Het nummer is daarom van 020 naar 005 gegaan; alleen dit plan kreeg een nieuw nummer, Appstore houdt 010.
>
> Afgezakt van A naar C op 19-08-2026 bij het afsluiten van de sessie. De pagina is af voor zover dat
> zonder de Umbrel kan; al het resterende werk in dit plan begint met kijken of de agent doet wat hij zou
> moeten doen, en dat kan niet vóór de installatie.
## Volgende stap
- [x] **0.0.10 op een telefoon nagekeken. In orde (20-08-2026).** De gebruiker meldt dat het er goed uitziet
op mobiel. Eén ding kwam eruit: de kopieerknoppen stonden niet op één lijn, omdat de knop door het
omvouwen de lengte van het adres volgde. Verholpen in 0.0.11 met `margin-left: auto` op de knop.
Bijvangst: `0.0.10` na `0.0.9` levert wél een update op, dus tweecijferige versiedelen zijn veilig
- [x] **De agent zien draaien. Gelukt op 20-08-2026, over drie versies.** De agent start en luistert, de
pagina laadt, hij vond veertien certificaten in `/certs/zoraxy` en weigerde daarom te kiezen, een
keuze op de pagina werd aangenomen, en daarna verbindt een wallet over 50022. De keten waar dit plan
op wachtte is dus rond.
- [ ] **Het uploaden op het niet-gelukkige pad proberen**, het enige stuk van 0.0.7 dat nog niet in een
browser gezien is: een sleutel die niet bij het certificaat hoort. Verwacht: een weigering met een
leesbare reden, en niets dat achterblijft in `data/certs`. De guard is met echte sleutelparen getoetst
en mutatie-getest, dus dit gaat over de weg van de melding naar de pagina en niet over de controle
zelf. **Eigenaar: gebruiker**
- [x] **0.0.7 en 0.0.9 in de browser nagekeken. Gelukt (20-08-2026):** uploaden werkt, kiezen werkt, ook
met de certificaten uit Zoraxy, de keuzelijst bij het installeren zag er goed uit en het
versienummer op de pagina klopt. Daarmee is het uploadpad uit 0.0.7 bewezen op het gelukkige pad
- [x] **0.0.6 in de browser nagekeken (20-08-2026).** Ziet er goed uit volgens de gebruiker. Wat eruit
kwam: de badges konden weg, "your choice" zei niets, Trezor Suite is niet desktop-only, en
Blockstream Green heet Blockstream
- [x] **De pagina naast een werkende app gelegd (20-08-2026).** Blokhoogte en backend-reactietijd komen
binnen. Wat eruit kwam: van een verbonden wallet was niets te zien, de keuzelijst was te druk, en
een reeks uitleg-teksten kon weg. Alles verwerkt in 0.0.6
- [ ] **Het activiteitenlog een etmaal laten lopen** en dan kijken of het klopt: komt er een
`connected`-regel bij een nieuwe wallet, en wat staat er na een nacht in. De teller is nieuw en is
alleen tegen tijdelijke bestanden getoetst, niet tegen een echte wallet
Daarna, en niet eerder:
- [ ] Uitzoeken waar Nginx Proxy Manager op umbrelOS zijn certificaten neerzet. De mount staat
uitgecommentarieerd in `docker-compose.yml`: een gok invullen zou Docker een lege maphierarchie
laten aanmaken in de app-data van een app die er misschien niet eens is
- [ ] Open punt 5: laten controleren of de TLS-poort zélf antwoordt. Dat is nu het enige wat de app niet
over zichzelf weet, en het is zijn enige taak
- [x] **Open punt 6: een certificaat uploaden via de pagina. Gebouwd in 0.0.7** (20-08-2026). Beschrijfbare
mount voor de agent, een eigen nginx-locatie met een grotere limiet, en validatie met
`load_cert_chain` voordat er iets geplaatst wordt. De bestandsnaam komt uit het certificaat zelf,
dus padtrucs kunnen niet. **Nog niet in een browser geprobeerd**
- [ ] Open punt 7: beslissen of Zoraxy een harde afhankelijkheid wordt. "Zoraxy of NPM" kan niet, en dat
is op 20-08-2026 in de bron nagetrokken; alleen de wens staat nog open. Zie [OPEN.md](OPEN.md)
punt 7
- [ ] Fase 5, het activiteitenlog, als de gebruiker het wil
De drie controles op `openssl`, `nc` en de WebDAV-module zijn **vervallen** met de komst van de agent: die
doet dat werk zelf met de standaardbibliotheek. Zie [PLAN.md](PLAN.md) §4a0.
Daarna, in deze volgorde:
- [ ] Fase 2 bouwen op de uitkomst van die controle
- [ ] Fase 5, als de gebruiker de wallet-activiteit wil
## Fase 1 - Het liegen eruit
- [x] De verzonnen statusblokken (`Online`, `Online`) en het verzonnen logvenster weg
- [x] Het hardgecodeerde domein weg. De pagina bevat geen enkele installatiespecifieke waarde meer
- [x] Taal naar Engels, zie [OPEN.md](OPEN.md) punt 2
- [x] Opnieuw opgebouwd op het design-systeem: tokens, `card`, `badge`, `alert`, `list-item`, `btn`,
donker met een lichte variant en een schakelaar
- [x] **De pagina verhuisd naar `index.html.template` in de app-root.** Onder `web/` zou elke latere
wijziging een herinstallatie kosten: umbreld ververst bij een update alleen een whitelist en die
kijkt niet in submappen. Als template zit hij in de whitelist én wordt hij bij elke start ingevuld
## Fase 2 - De agent schrijft `status.json`
Herzien 19-08-2026: dit is een tweede container met een python-programma geworden in plaats van een
shell-lus in de compose. Onderbouwing in [PLAN.md](PLAN.md) §4a0.
- [x] `agent.py.template`, met de instellingen uit de omgeving in plaats van uit template-invulling.
Daardoor staat er geen accolade-variabele in en blijft het geldige Python, dus is het te importeren
in een test. De eerste toets in `tests/` controleert precies die aanname
- [x] Schrijft `status.json` atomair via een tijdelijk bestand en `os.replace`, want de pagina leest het
elke minuut en een half geschreven bestand geeft een lege pagina
- [x] Einddatum van het certificaat met een eigen DER-lezer. Getoetst tegen `ssl` op 74 echte
CA-certificaten, op een levend servercertificaat, en op beide tijdvormen
- [x] Blokhoogte en reactietijd via `blockchain.headers.subscribe` over een verse socket
- [x] Een `history`-reeks bijhouden, afgekapt op 24 uur, met alléén de reactietijd. De sparkline is sinds
19-08-2026 de enige afnemer
- [x] Herkennen welke backend het is aan de hand van het adres, en `unknown` als hij niet in de tabel
staat. Zie [PLAN.md](PLAN.md) §4c: liever minder informatief dan onwaar
- [x] De lus valt nooit stil: een mislukte ronde wordt gelogd en overgeslagen. Stoppen zou de pagina op
oude gegevens bevriezen, en dat is precies het liegen dat dit plan moest afschaffen
- [ ] Draaien op de Umbrel. Niets hiervan is buiten de tests uitgevoerd
## Fase 3 - De pagina vult zichzelf
- [x] `fetch` op `status.json`, elke minuut
- [x] Elk veld heeft een zichtbare onbekend-toestand; nooit een leeg veld, want dat leest als "in orde"
- [x] "Laatst bijgewerkt" tonen, en oude gegevens als zodanig laten zien zodra ze ouder zijn dan twee
schrijfronden. **Bijgesteld 19-08-2026 op verzoek van de gebruiker:** dat gebeurt niet meer met een
melding bovenaan de pagina maar alleen bij de waarden zelf, want die melding zei voor de vierde keer
wat er al onder de blokhoogte, in de badge bij de Electrum-server en in de voettekst stond
- [x] Leesbare melding als het bestand ontbreekt, zonder dat het op een fout lijkt.
Zie [OPEN.md](OPEN.md) punt 1
- [ ] Verifiëren tegen een échte `status.json` in plaats van tegen de voorbeeldversie uit de preview
## Fase 4a - De indeling
Vastgesteld door de gebruiker op 19-08-2026 na het bekijken van de eerste versie. Ontwerp in
[PLAN.md](PLAN.md) §4a1.
- [x] Volgorde: Electrum-server, drie statustegels, activiteitenlog, certificaatkeuze, instellen van je
wallet
- [x] "Point your wallet here" opgegaan in "Setting up your wallet"; het waren dezelfde kaders
- [x] Breed met een raster van twaalf kolommen, rijen die 5/7 en 7/5 afwisselen, afgekapt op 1760px
- [x] De leesvolgorde in de HTML is de bedoelde volgorde, zonder `order` in de CSS, zodat stapelen op een
smal scherm dezelfde volgorde geeft. Gecontroleerd op 1760px en op mobiel
- [x] Statustegels rekken mee met de hoogte van hun buur, anders staat er een gat rechtsboven
- [x] Het logblok scrollt zelf horizontaal in plaats van de regel met puntjes af te kappen
- [x] **Alle kaders hetzelfde opgebouwd** (19-08-2026): een `card-head` met een titel in `t-h3`, dan de
inhoud. De drie statustegels hadden een eigen kleine grijze kop in kapitalen. De titel gebruikt de
token `--text-primary`, niet letterlijk wit, anders is hij onzichtbaar in de lichte variant
- [x] De onderregel van een statustegel is even hoog als de sparkline, zodat de drie getallen op één lijn
staan. Gecontroleerd: koppen op y=153, getallen op y=311
- [x] **De badge "Serving this page" verwijderd** (19-08-2026, op aanwijzing van de gebruiker). Zie
[OPEN.md](OPEN.md) punt 5 voor wat er wél op die plek zou horen
- [x] **De voetregel verwijderd** (19-08-2026). Daarin stond "Last reading ... ago" plus het versienummer;
het eerste stond al bij de waarden zelf
- [x] **Het versienummer teruggezet**, klein achter de tagline als `v0.0.3`. Dezelfde dag op verzoek van de
gebruiker. In `--text-sec` en niet in `--text-ter`, want die laatste is in de lichte variant `#bbbbbb`
op wit en op deze grootte onleesbaar
- [x] **De certificaatlijst scrollt**, met het aantal in de kop. Een gedeelde certificatenmap kan er
tientallen bevatten. Gecontroleerd met 19 certificaten: 1062px inhoud in een vak van 360px, knop
blijft eronder staan
- [x] **Het logblok en het certificaatkader zijn even hoog.** Het langste kader van de rij bepaalt de
hoogte, en wat mag meegroeien zegt dat zelf met flex. Gemeten: 636px bij vier certificaten, 773px bij
negentien, in beide gevallen allebei gelijk
- [x] **Certificaat en activiteitenlog omgedraaid** (19-08-2026, op verzoek). Certificaat links en smal,
log rechts en breed. Dat pakt twee kanten goed uit: de kolomgrens valt nu samen met die van de rij
erboven (gemeten: x=738 in beide rijen), en monospace logregels hebben de breedte beter nodig
- [x] **Het losse verbindingsadres met kopieerknop eruit**, plus het label "tested clients". Zonder
verlies: hetzelfde adres staat bij elke clientregel, in de vorm die díe client wil, met een
kopieerknop erbij. Het instelkader werd daarmee 533px in plaats van 610px
- [ ] Nog niet met eigen ogen gezien op een echt breed scherm; gecontroleerd via de afmetingen van de
kaders, niet visueel, want het browserpaneel stond dicht
## Fase 4 - Bruikbaar in plaats van alleen eerlijk
- [x] Verloopwaarschuwing onder de dertig dagen, en een foutmelding als het certificaat verlopen is
- [x] Kopieerknop op het verbindingsadres, met terugval voor http zonder de clipboard-API
- [x] Verbindingsregels per client, met kopieerknop per regel. Bron: `Referenties/Clients.md` §4
- [x] Kopieerknoppen verschijnen bij hover over de regel. Met `opacity` en niet met `display`, zodat ze
met de tab-toets bereikbaar blijven, plus `focus-within` en altijd zichtbaar op aanraakschermen
- [ ] De kopieerknop echt uitproberen achter de app-proxy van umbrelOS. Die draait over http, dus de
terugval met `execCommand` is daar het pad dat gebruikt wordt en niet de uitzondering. Wat op
19-08-2026 lokaal wél bewezen is: de **faalroute** meldt "Press Ctrl+C" in plaats van stil niets te
doen. De geslaagde route vraagt een echte muisklik, want de clipboard-API weigert een klik die uit
een script komt
## Fase 4b - De certificaatkeuze
Besloten 19-08-2026 door de gebruiker: een keuzelijst op het dashboard in plaats van een sleutel in het
configuratiebestand. Het ontwerp hoort bij het plan **Configuratie** §4e en §4f; hier staat alleen wat de
pagina en de compose ervoor doen.
- [x] Keuzelijst op de pagina, met per certificaat de bron, de bestandsnaam, het domein en het aantal
resterende dagen. Een verlopen certificaat staat er rood bij en wordt niet verborgen: het bestaat,
en het kiezen ervan moet een zichtbare vergissing zijn en geen onvindbare
- [x] `PUT` naar `api/certificate`, doorgestuurd naar de agent. De WebDAV-truc is vervallen: de agent
neemt de keuze aan en kan hem ook controleren, wat WebDAV niet kon
- [x] De pagina meldt níet zelf dat het gelukt is. Nginx moet het certificaat nog herladen, en dat blijkt
pas uit de volgende `status.json`
- [x] De Zoraxy-mount van `/certs` naar `/certs/zoraxy`, en `/certs/own` erbij, zodat er per bron een map
is. Beide containers hebben ze nodig: de agent om te kiezen, nginx om het bestand te openen
- [x] `certificates` in `status.json`, met per certificaat de bron, de naam, het domein uit het
certificaat en de einddatum
- [x] **De guard: een id die de agent niet zelf gevonden heeft, wordt geweigerd.** Zowel bij de `PUT` als
bij het kiezen. Getest, inclusief de mutatietest
- [x] **Bij meerdere kandidaten kiest de app niet.** Bijgesteld nadat bleek dat mijn eerste versie "de
langst geldige" pakte, wat het plan **Configuratie** §4c uitdrukkelijk verbiedt: een verkeerd
certificaat geeft een verbinding die het lijkt te doen en bij de wallet stukloopt op
naamverificatie. De pagina toont dan een foutmelding met de kandidaten erin
- [x] De pagina toont bovenaan een foutmelding als er geen certificaat actief is, want dan is er geen TLS
en dat is het ergste wat deze app kan overkomen
## Fase 5 - Het activiteitenlog
Voorstel, nog niet besloten. Ontwerp in [PLAN.md](PLAN.md) §4e. De kaart staat al op de pagina en toont
`no data`, zodat de vorm te beoordelen is voordat er iets voor gebouwd wordt.
- [x] Vorm: logregels onder elkaar, monospace, nieuwste bovenaan, met de grens erbij vermeld dat een
regel per protocolaanroep niet kan zonder het verkeer van de gebruiker uit te lezen
- [ ] Uitzoeken of een `log_format` met dollartekens langs de template-invulling te krijgen is, via een
`include` die het `command`-blok wegschrijft
- [ ] Uitzoeken hoe de bytetellers van een lopende verbinding in de container af te lezen zijn; nginx
logt een stream-sessie pas bij het sluiten
- [ ] `log` in `status.json`, zonder client-IP en zonder bronpoort
## Geblokkeerd / wacht op
- [ ] Niets. De afhankelijkheid van **Configuratie** is vervallen, zie [PLAN.md](PLAN.md)
@@ -0,0 +1,50 @@
# Open punten - Proefopstelling
> Beslissingen die nog een **eigenaar** of een **moment** nodig hebben. Staat een punt hier zonder
> allebei, dan is dat de eerste fout om op te lossen. Wordt een punt een taak, dan verhuist het naar
> [TAKEN.md](TAKEN.md).
>
> **Nummers blijven staan**, ook als een punt beslist is: er kan elders naar verwezen worden, ook vanuit
> codecommentaar. Beslissen betekent verplaatsen naar de kop hieronder, niet hernummeren.
## Nog te beslissen
1. **Waar draait de proefopstelling?**
Er zijn twee plekken en ze meten niet hetzelfde. Een machine met Docker onder handbereik is het
snelst en het makkelijkst opruimen. De Umbrel zelf lijkt dichter bij het doel, maar dat is
schijnnauwkeurigheid: je draait dan nog steeds niet als umbrelOS-app, je hebt wel meteen last van
poortbotsingen met wat er al draait, en een mislukte poging laat rommel achter op een
productiemachine.
Voorstel: de losse machine, tenzij er een reden is dat het daar niet kan. De echte controle op de
Umbrel hoort bij het masterplan **Umbrelapp**.
**Moment:** voor fase 2 · **Eigenaar:** gebruiker
2. **Welke Trezor Suite telt?**
Desktop, web en mobiel zijn drie verschillende programma's, en het is niet gegeven dat ze alle drie
een eigen sync-server accepteren. Het antwoord bepaalt wat "het werkt" betekent, en het bepaalt ook
het masterplan **Bereikbaarheid**: alleen desktop op het thuisnetwerk vraagt veel minder dan een
telefoon onderweg.
**Moment:** valt samen met de "Volgende stap" van fase 1 · **Eigenaar:** gebruiker
3. **Is er een tweede apparaat om mee te synchroniseren?**
Fase 3 heeft er een nodig, want dat is de enige controle die bewijst dat de relay doet waarvoor hij
bestaat. Een tweede installatie van Suite op dezelfde machine kan misschien ook, maar dat is niet
uitgezocht en het is zwakker bewijs: dezelfde machine, hetzelfde netwerk.
**Moment:** voor fase 3 · **Eigenaar:** gebruiker
4. **Wat als de quota-manager verplicht blijkt én zelf een externe dienst nodig heeft?**
Dan is dit geen pakketteerprobleem meer. Denkrichtingen, niet in volgorde: de quota-manager mee
pakketteren met een minimale configuratie die niets betaalt; uitzoeken of er een schakelaar is die de
controle uitzet; of concluderen dat zelf hosten niet bedoeld is en het project hier stoppen. Dat
laatste is een geldige uitkomst en zou de goedkoopste zijn die dit plan kan opleveren.
**Moment:** zodra fase 1 of fase 2 het antwoord geeft · **Eigenaar:** gebruiker beslist, op basis van
wat er dan bekend is
5. **Wordt de proefopstelling zelf vastgelegd in deze repo?**
Een `compose/`-map met de gebruikte compose en een `.env.sample` maakt het herhaalbaar, en dat is
veel waard als er over twee weken pas verder gewerkt wordt. Er zit een prijs aan: deze repo wordt
publiek, dus er mag geen enkel echt geheim in, en een tweede compose naast die van het pakket kan
later verwarren welke de echte is.
Voorstel: wel, in een map die duidelijk `proefopstelling/` heet, met uitsluitend een `.env.sample`
en nooit een `.env`.
**Moment:** bij de eerste geslaagde start in fase 2 · **Eigenaar:** gebruiker
@@ -0,0 +1,89 @@
# Proefopstelling - plan
> Ontwerp en afbakening. **Dit bestand lees je zelden**, alleen bij twijfel over scope of architectuur.
> Status staat in [TAKEN.md](TAKEN.md), geschiedenis in [PROGRESS.md](PROGRESS.md), onbesliste punten in
> [OPEN.md](OPEN.md).
## 1. Doel
Uitzoeken of Evolu Relay zelf gehost bruikbaar is voor Trezor Suite, en met welke **minimale** set
containers en omgevingsvariabelen. Dat gebeurt lokaal, buiten Umbrel om, want een pakket bouwen voor iets
waarvan je niet weet of het draait is de dure volgorde.
Als dit plan af is, is er één zin die het volgende plan kan aannemen: "de relay draait met deze containers
en deze variabelen, en Trezor Suite synchroniseert ermee." Zonder die zin is elk manifest een gok.
## 2. Afbakening
Alles tot en met een label dat op apparaat A gezet wordt en op apparaat B verschijnt, via een relay die op
het thuisnetwerk draait en die niets van Trezor nodig heeft.
Binnen dit plan valt ook het opschrijven van wat er nodig bleek: welke variabelen, welke poorten, welke
volumes, en waar de images vandaan komen. Dat is niet de bijvangst maar het eigenlijke product; het
volgende plan leest het.
## 3. Niet-doelen
- **Geen `umbrel-app.yml` en geen `docker-compose.yml` in Umbrel-vorm.** Dat is het masterplan
**Umbrelapp**, en het heeft de uitkomst van dit plan nodig.
- **Geen bereikbaarheid van buiten, geen TLS, geen Tailscale.** Masterplan **Bereikbaarheid**. Hier
volstaat een IP op het eigen netwerk.
- **Geen eigen image bouwen of publiceren.** Blijkt dat nodig, dan is dat een bevinding van dit plan en
werk van het volgende.
- **Niets met quota's of betalen.** De quota-manager is hier een obstakel dat je wegwerkt of moet
meenemen, geen functionaliteit die we willen.
- **Geen bijdrage aan `trezor/trezor-suite-sync`.** Ook niet als er onderweg iets stuk blijkt.
## 4. Ontwerp
### 4a. De volgorde is: eerst wat het project kan doden
Het vooronderzoek zet "clone en draai" als eerste stap. Dat is niet de goedkoopste weerlegging. Er zijn
twee aannames waarop dit project stukloopt, en de eerste kost een minuut:
1. **Kan Trezor Suite überhaupt naar een eigen relay wijzen?** Het vooronderzoek gaat uit van een
"Custom server"-veld. Bestaat dat niet in de Suite-versie van de gebruiker, of alleen op een platform
dat hij niet gebruikt, dan is er niets te pakketteren. Dit is te controleren in de interface, zonder
iets te installeren.
2. **Is de quota-manager verplicht?** Zie [Upstream-evolu-relay.md](../../../Referenties/Upstream-evolu-relay.md)
§3. Dit is te lézen in `.env.sample`, de compose en de broncode, en pas daarna te bewijzen door hem weg
te laten.
Pas als die twee goed staan, is "clone en draai" de moeite waard.
### 4b. Wat er gedraaid wordt
De compose van Trezor zelf, ongewijzigd waar het kan, met de quota-manager uitgeschakeld. Ongewijzigd is
hier een doel op zich: elke aanpassing die je maakt, is een aanpassing waarvan je later niet meer weet of
hij nodig was. Werkt het niet zonder wijziging, dan is de wijziging zelf een bevinding.
### 4c. Wat er opgeschreven wordt
Per container: image en herkomst, poorten, volumes, en de omgevingsvariabelen die **echt** nodig bleken,
niet de hele `.env.sample`. Dat onderscheid is het verschil tussen een manifest van tien regels en een van
veertig, en het is achteraf niet meer te maken.
Daarbij twee dingen die het volgende plan hard nodig heeft en die je alleen hier tegenkomt: **komt de
image uit een registry of alleen uit een Dockerfile**, en **hoe authenticeert Suite zich tegen de relay**.
Die tweede bepaalt of de app achter de inlog van umbrelOS kan staan; zie het masterplan **Umbrelapp**,
§4b.
## 5. Raakvlakken
- **Umbrelapp** is hard afhankelijk van dit plan: het aantal containers, de variabelen en de
authenticatievraag komen hier vandaan.
- **Bereikbaarheid** leunt op één bevinding hier: accepteert Suite een `http://`-adres, of eist het TLS?
- **Publicatie** leunt op de herkomst van de image. Zonder een gepinde multi-arch image uit een registry
komt dat plan niet van de grond.
## 6. Verificatie
Wat als bewijs telt, in oplopende sterkte:
1. de relay start en blijft draaien zonder quota-manager;
2. Trezor Suite accepteert het adres en meldt geen fout;
3. een label dat op apparaat A gezet wordt, verschijnt op apparaat B na een synchronisatie. **Dit is de
enige die telt.** De eerste twee kunnen slagen terwijl er niets gesynchroniseerd wordt.
Wat hier per se **niet** bewezen wordt, en wat dus niet als "werkt" gemeld mag worden: gedrag op arm64,
overleven van een herstart, gedrag onder umbrelOS, en bereikbaarheid van buiten het thuisnetwerk.
@@ -0,0 +1,25 @@
# Voortgang - Proefopstelling
> 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).
## 25-08-2026 - plan werd actief
Dit project bestond uit één document, `trezor-suite-sync-umbrel-app-plan.md`, met het vooronderzoek van
dezelfde dag erin. Dat is bij het inrichten van de repo over vier plannen verdeeld; dit is het enige dat
meteen actief werd, omdat het als enige nu al iets te doen heeft en omdat de andere drie er hard van
afhangen. Het origineel staat ongewijzigd in `Plannen/Masterplannen/Archief/Vooronderzoek.PLAN.md`, want
het is de bron onder elke regel die met "onderzocht 25-08-2026" gemerkt is.
**Eén ding is bij het verdelen omgedraaid.** Het vooronderzoek zet "clone en draai" als eerste stap. Er
zit een goedkopere weerlegging vóór: of Trezor Suite überhaupt naar een eigen sync-server kan wijzen. Dat
kost een minuut in de interface, en is het antwoord nee, dan is er niets te pakketteren. Dat staat nu als
"Volgende stap"; de clone is fase 2.
**De remote is anoniem bereikbaar, en dat is hier een eis en geen hygiëne.** Getest met de
credential-helper leeg gezet, want een gewone `ls-remote` slaagt op deze machine ook als de repo dicht
staat. De repo is nog leeg, dus dit bewijst bereikbaarheid en nog niet dat umbreld de store-inhoud kan
ophalen; die controle hoort bij het masterplan **Umbrelapp**.
**Geraakt:** de hele `Docs/`-boom, `.gitignore`, `.gitattributes`, `CLAUDE.md`, `README.md`.
**Tests:** niet van toepassing, er is nog geen bron. Zie `Docs/README.md`, "Projectspecifiek".
@@ -0,0 +1,58 @@
# Taken - Proefopstelling
> Prioriteit: **A** | Afhankelijk van: -
>
> Actief sinds 25-08-2026, bij het inrichten van dit project. Dit is het enige plan met een tier: de drie
> masterplannen wachten allemaal op de uitkomst hiervan.
## Volgende stap
- [ ] **Controleren of Trezor Suite een eigen sync-server accepteert, en op welk platform.** Dit is de
goedkoopste weerlegging van het hele project en het kost een minuut in de interface. Noteer waar het
veld staat, hoe het adres eruit moet zien (met of zonder schema, met of zonder poort), en of het
alleen op desktop bestaat. **Eigenaar: gebruiker**
## Fase 1 - Kan dit überhaupt
- [ ] Het veld voor een eigen sync-server in Trezor Suite gevonden, met de vorm die het verwacht
- [ ] `.env.sample`, `docker-compose.yaml` en de relay-broncode van `trezor/trezor-suite-sync` gelezen op
de vraag of de quota-manager verplicht is. **Lezen, nog niet draaien:** als het antwoord in de
broncode staat, scheelt dat een halve middag proberen
- [ ] Vastgesteld of Trezor een image publiceert of alleen een Dockerfile levert. Zie
[Upstream-evolu-relay.md](../../../Referenties/Upstream-evolu-relay.md) §4 punt 1; dit bepaalt of
het masterplan **Publicatie** haalbaar is
## Fase 2 - De stack lokaal draaien
- [ ] `trezor/trezor-suite-sync` gekloond
- [ ] `docker compose up` met **alleen** Postgres en `evolu-relay`, de quota-manager weggelaten
- [ ] De relay antwoordt op poort 4000 en blijft draaien. Blijft hij niet draaien, dan is de foutmelding
de bevinding: schrijf hem letterlijk op in [PROGRESS.md](PROGRESS.md)
- [ ] **Blocker-check:** is de relay hard afhankelijk van de quota-manager? Zo ja, die erbij en
uitzoeken wat hij zelf nodig heeft. Vraagt hij een externe dienst, dan is dat geen taak meer maar
open punt 4 in [OPEN.md](OPEN.md)
## Fase 3 - Echt synchroniseren
- [ ] Trezor Suite op apparaat A naar `http://<ip-van-de-machine>:4000` laten wijzen
- [ ] Een label toevoegen op apparaat A
- [ ] Datzelfde label zien verschijnen op apparaat B. **Dit is de enige controle die telt**; de twee
hierboven kunnen slagen terwijl er niets gesynchroniseerd wordt
- [ ] Geprobeerd wat er gebeurt als de relay even weg is en terugkomt. Niet omdat het nu moet werken,
maar omdat het gedrag straks op een Umbrel bij elke update voorkomt
## Fase 4 - Vastleggen wat het pakket moet worden
- [ ] Per container opgeschreven: image en herkomst, poorten, volumes, en de variabelen die **echt** nodig
bleken. Niet de hele `.env.sample` overnemen
- [ ] Opgeschreven hoe Trezor Suite zich tegen de relay authenticeert, of dat het helemaal niet doet. Dit
bepaalt of de app achter de inlog van umbrelOS kan staan; zie het masterplan **Umbrelapp**, §4b
- [ ] Opgeschreven of Suite een `http://`-adres accepteert of TLS eist. Hier hangt het masterplan
**Bereikbaarheid** aan
- [ ] [Upstream-evolu-relay.md](../../../Referenties/Upstream-evolu-relay.md) bijgewerkt: alles wat daar
als "onderzocht 25-08-2026" staat en nu bevestigd of weerlegd is, met de nieuwe datum erbij
- [ ] Het masterplan **Umbrelapp** herzien met wat hier uitkwam, en pas daarna promoveren
## Geblokkeerd / wacht op
- [ ] Fase 3 wacht op een tweede apparaat met Trezor Suite. Zie [OPEN.md](OPEN.md) punt 3
+144
View File
@@ -0,0 +1,144 @@
# Open punten - Appstore
> Beslissingen die nog een **eigenaar** of een **moment** nodig hebben. Staat een punt hier zonder
> allebei, dan is dat de eerste fout om op te lossen. Wordt een punt een taak, dan verhuist het naar
> [TAKEN.md](TAKEN.md).
>
> **Nummers blijven staan**, ook als een punt beslist is: er kan elders naar verwezen worden, ook vanuit
> codecommentaar. Beslissen betekent verplaatsen naar de kop hieronder, niet hernummeren.
## Nog te beslissen
2. **Wat gebeurt er met de bestaande handmatige installatie?** - **In twee stappen, en de eerste is niet
meer uit te stellen** (bijgewerkt 20-08-2026).
De container is gestopt maar de compose staat er nog: een `alpine:latest` met een `apk add stunnel` bij
elke start, met `restart: unless-stopped` en een mount op de certificaten van Zoraxy. Wat er nu aan de
orde is:
- **de container weghalen, nu.** Hij bindt poort **50002**, en dat is precies de poort die Fulcrum op de
host wil. Zolang hij bestaat kan het omschakelen naar Fulcrum niet slagen, en dat staat in fase 6 als
taak. `restart: unless-stopped` betekent bovendien dat hij een herstart van de Umbrel overleeft;
- **de map laten staan tot de herstart-controle gedaan is.** Dat is een compose plus een
`entrypoint.sh` en het kost niets. Het is de gedocumenteerde weg terug, en die vervalt pas als
bewezen is dat de app na een herstart vanzelf terugkomt. Let op de volgorde: de container mount zijn
`entrypoint.sh` uit die map, dus eerst de container weg en dan de map.
**Moment:** stap 1 nu, stap 2 na de herstart-controle · **Eigenaar:** gebruiker
4. **Eigen icoon en gallery.**
Het manifest wijst nu naar het Electrs-icoon en een screenshot van Electrs in `getumbrel/umbrel-apps`,
dus naar andermans bestanden en naar plaatjes van een andere app. Er moet een eigen SVG komen. De
gallery is minder dringend: die is er voor een winkelpagina die hier niemand bezoekt, zie
[KNOWLEDGE.md](../../../KNOWLEDGE.md). Vraag is wie het icoon maakt en of het in de repo komt of extern
gehost wordt.
**Moment:** fase 5 · **Eigenaar:** gebruiker
7. **Hoe wordt de herstart-controle alsnog gedaan?**
Dit is de controle waar het hele plan om begon: komt de app na een herstart van de Umbrel vanzelf
terug. Hij is op 18-08-2026 uitgesteld omdat de Umbrel een productiemachine is en niet op verzoek
herstart wordt. Dat is een geldige reden, maar het betekent wel dat het plan **niet afgerond kan
worden**: alles wat er nu ligt is een sterke aanwijzing en geen bewijs.
Wat er specifiek niet mee getest is, en waar de enige echte twijfel zit: de **volgorde bij het
opstarten**. Zoraxy kan later klaar zijn dan deze app, en dan bestaat het certificaat nog niet.
Bijgewerkt op 20-08-2026: hier stond dat de compose daarvoor een wachtlus heeft. Die is er niet meer,
want juist die lus was de klem van 0.0.3. De opvolger is de vangnettak in de herlaadlus, die TLS
aanzet zodra `cert.conf` verschijnt zonder dat er een vlag bij hoort. Dat is precies het
Zoraxy-is-later-klaar-geval, en het is nog nooit echt voorgekomen; alleen getoetst tegen tijdelijke
bestanden.
**Moment:** bij de eerstvolgende herstart die er toch komt, bijvoorbeeld een umbrelOS-update of een
stroomonderbreking. Noteer de uitkomst dan in `PROGRESS.md`. · **Eigenaar:** gebruiker
9. **Volgt de app-map de conventie van andere apps?** Gevraagd door de gebruiker op 20-08-2026, die
opmerkte dat hij bij andere apps geen app-code in `app-data` ziet. Uitgezocht en vastgelegd in
[Umbrel-appstore-spec.md](../../../Referenties/Umbrel-appstore-spec.md); de korte versie is dat
config-in-app-data een bestaand patroon is (`electrs` mount zijn `torrc` zo) en dat code-in-app-data
het gevolg is van geen eigen image bouwen, wat een besluit is en geen ongeluk.
**Beide vervolgpunten zijn diezelfde dag gedaan in 0.0.9**, nadat de gebruiker zei dat hij de app wil
publiceren als standaard-app: de data staat onder `data/` met een `.gitkeep` per map, en `backupIgnore`
noemt de sessielog en `status.json`. Niet `data/runtime/config`, want daar zit de certificaatkeuze.
Wat het uitzoeken daarna opleverde is groter dan dit punt en staat daarom in een eigen masterplan
**Publicatie**: de eisen van de officiële store, wat er al aan voldoet, en het risico dat niet in een
checklist staat, namelijk de leesmount op de certificaten van Zoraxy.
**Moment:** afgerond · **Eigenaar:**
## Beslist
8. **Wordt de repo hernoemd, en wat wordt dan het store-id?** - **Store-id `whatsnext`, app-id
`whatsnext-electrum-gate`, repo-naam nog niet** (19-08-2026, gebruiker).
Het app-id **moet** met het store-id beginnen en de mapnaam moet gelijk zijn aan het app-id (zie
[Umbrel-appstore-spec.md](../../../Referenties/Umbrel-appstore-spec.md)). De aanname bij het stellen
van deze vraag was dat de repo-naam daar ook in zat, want de store-URL ís de repo-URL. **Dat klopte
niet:** het store-id is gewoon een veld in `umbrel-app-store.yml` en staat los van de naam van de
repo. Daardoor kon het store-id meteen goed gezet worden en kan de repo-naam wachten.
De store heet nu naar de maker en niet naar deze ene app. Dat is de variant die later een tweede app
toelaat zonder opnieuw te hernoemen, en dat weegt hier zwaarder dan de kosten, want die zijn nul: de
gebruiker gaf aan de app zonder bezwaar opnieuw te kunnen installeren.
**Afgerond op 25-08-2026, en eerder dan gedacht.** Hier stond dat de repo nog `ElectrumTLS` heette en
dat het hernoemen zou meeliften op een publieke versie op GitHub. Wat het versnelde was Evolu Relay:
umbrelOS leest per store één repo, dus een tweede app dwong de vraag af. De repo is
`UmbrelApps` geworden, met `website`, `repo`, `support`, `submission` en `icon` mee, in 0.0.15.
**Het app-id is niet meegegaan** en dat is de reden dat dit goedkoop was: `whatsnext-electrum-gate`
hangt aan het store-id en niet aan de URL. Voor umbrelOS is het dus dezelfde app in een andere store.
**Wat er wél open blijft, en nu voor het eerst echt aan de orde is:** of een geïnstalleerde app een
wisseling van **store-URL** overleeft, of dat de store verwijderd en opnieuw toegevoegd moet worden en
de app daarna opnieuw geïnstalleerd. Niet uitgezocht, en niet aannemen dat het meevalt. **Eigenaar:**
gebruiker, bij het omzetten van de store in umbrelOS.
1. **Draait `nginx:alpine` met de `stream`- en `stream_ssl`-module?** - **Ja** (18-08-2026).
Op de Umbrel gecontroleerd met `docker run --rm nginx:alpine nginx -V`. Aanwezig zijn
`--with-stream`, `--with-stream_realip_module`, `--with-stream_ssl_module` en
`--with-stream_ssl_preread_module`, alle vier statisch meegebouwd, dus zonder `load_module`.
Daarmee gaat het ontwerp uit [PLAN.md](PLAN.md) §4c door in de kleine variant: één container die de
web-UI serveert én TLS termineert, geen stunnel-installatie bij het starten, geen losse
`cert-monitor`, en de Docker-socket-mount vervalt.
Twee dingen die deze uitkomst meebracht en die het opschrijven waard zijn. nginx wil de **volledige
keten** in `ssl_certificate`, terwijl stunnel het gesplitst wilde; de `awk`-splitsing uit
`entrypoint.sh` is daarmee overbodig in plaats van overgenomen. En dit antwoord geldt voor de tag
`nginx:alpine` van vandaag: bij het pinnen op een digest wordt dezelfde controle op díe digest
herhaald, anders is er iets anders bewezen dan er uitgeleverd wordt.
6. **Hoe komen `entrypoint.sh` en `web/` in `${APP_DATA_DIR}`?** - **Bij installatie automatisch, bij
een update niet** (18-08-2026).
umbreld doet bij installatie `rsync --archive` van de hele app-map naar `${APP_DATA_DIR}`, dus de
mounts in de compose kloppen. Bij een **update** wordt alleen een whitelist ververst:
`docker-compose.yml`, `*.template`, `exports.sh`, `torrc`, `hooks` en `umbrel-app.yml`.
Gevolg dat het ontwerp stuurt: een gewijzigde `entrypoint.sh` bereikt een bestaande installatie nooit,
zonder foutmelding. Logica die later nog moet kunnen wijzigen hoort daarom in de compose, in de image,
of in een `*.template`-bestand; dat laatste staat in de whitelist én wordt bij elke start met de
omgevingsvariabelen ingevuld. Zie de naslag
[Umbrel-appstore-spec.md](../../../Referenties/Umbrel-appstore-spec.md).
3. **Wanneer wordt er voor het eerst gepusht?** - **Meteen, vóór de opschoning** (18-08-2026).
De repo staat sindsdien publiek op `https://sc.kamenier-hamer.nl/sysop/ElectrumTLS.git`, met het
domein en het certificaatpad er nog in. Publiek zijn was geen keuze (umbreld kloont anoniem), alleen
de volgorde was dat. Afweging van de gebruiker: de repo is voor eigen gebruik en wordt niet
aangekondigd, dus vindbaarheid is laag.
Gevolg dat vastligt: die waarden staan nu in de **historie**. Het plan **Configuratie** haalt ze uit de
bestanden, niet uit de historie; dat laatste zou een herschrijving vragen. Behandel het domein dus als
bekend, en laat het geen reden zijn om Configuratie uit te stellen of juist te haasten.
**Bijgewerkt 25-08-2026: die historie is bij de verhuizing achtergebleven.** `UmbrelApps` is als lege
repo begonnen met alleen de huidige toestand erin, op verzoek van de gebruiker die de historie niet
nodig had. Daarmee vervalt de vaststelling hierboven, maar **pas als de oude repo weg is**: zolang
`ElectrumTLS` op de Git-server staat, staat het domein daar nog in de historie. Dat weghalen is dus
geen opruimwerk maar het laatste stuk van deze beslissing. **Eigenaar:** gebruiker
5. **Mag de app-id later nog wijzigen?** - **Ja** (18-08-2026).
Er zijn geen andere installaties, dus achterwaartse compatibiliteit is geen eis. Een wijziging kost
één keer opnieuw installeren. Genoteerd omdat de prefix-regel anders zwaarder lijkt dan hij is: de
keuze `electrumtls-electrum-tls` mag herzien worden zolang dat vóór fase 6 gebeurt.
+193
View File
@@ -0,0 +1,193 @@
# Appstore - plan
> Ontwerp en afbakening. **Dit bestand lees je zelden**, alleen bij twijfel over scope of architectuur.
> Status staat in [TAKEN.md](TAKEN.md), geschiedenis in [PROGRESS.md](PROGRESS.md), onbesliste punten in
> [OPEN.md](OPEN.md).
## 1. Doel
De app draait nu als een met de hand neergezette docker-compose die umbrelOS niet kent. Gevolg: na een
herstart van de Umbrel, of als een app waar deze van afhangt omvalt, moet er met de hand
`docker compose down` en `up` gedaan worden. Dit plan maakt er een echte Umbrel-app van in een eigen
community app store: een tegel met icoon, die umbrelOS zelf installeert, start en na een herstart weer
opbrengt.
Als dit af is, is de repo tegelijk de app store: de URL erin plakken in umbrelOS is genoeg om de app te
installeren, en een `git push` is genoeg om een update uit te leveren.
## 2. Afbakening
- De repo omzetten naar de vorm die umbrelOS voor een community app store verwacht.
- `docker-compose.yml` omzetten naar de moderne vorm: `app_proxy`, geen zelfgebouwd netwerk, geen
handmatige host-poorten waar dat niet hoeft.
- De Electrum-backend via de afhankelijkheid aanspreken in plaats van via een hardgecodeerde
containernaam, zodat Electrs, Fulcrum en ElectrumX alle drie werken.
- Een image die gepind kan worden, in plaats van `alpine:latest` met `apk add` bij elke start.
- Het manifest compleet en eerlijk maken: eigen icoon, eigen gallery, kloppende velden.
- De oude `install.sh` en `uninstall.sh` weghalen.
De volledige spec waar dit tegenaan moet, met bronvermelding per feit, staat in
[Referenties/Umbrel-appstore-spec.md](../../../Referenties/Umbrel-appstore-spec.md). Die is bij het
schrijven van dit plan uitgezocht en hoeft niet opnieuw opgezocht te worden.
## 3. Niet-doelen
- **De hardgecodeerde waarden eruit halen.** Het domein `sync.kamenier-hamer.nl` en het Zoraxy-pad blijven
in dit plan staan zoals ze zijn. Dat is het plan **Configuratie**, en het apart houden is bewust: een
commit die tegelijk de structuur omgooit en de configuratie herontwerpt is niet meer na te lezen.
- **De web-UI eerlijk maken.** De pagina toont verzonnen status. Dat is het plan **Webinterface**. Hier
wordt de pagina alleen verhuisd en aan `app_proxy` gehangen, niet herschreven.
- **Meerdere apps in de store.** De store krijgt de vorm die meer apps toelaat, maar er komt er één in.
- **Indienen bij de officiële Umbrel App Store.** Een community store is er juist om dat niet te hoeven.
Als het later toch aantrekkelijk wordt, is de spec-eis grotendeels dezelfde, dus dit sluit niets af.
## 4. Ontwerp
### 4a. De repo-vorm
```
UmbrelApps/
├── umbrel-app-store.yml id: whatsnext
├── whatsnext-electrum-gate/
│ ├── umbrel-app.yml id: whatsnext-electrum-gate
│ ├── docker-compose.yml
│ ├── *.template
│ └── data/{certs,runtime}/.gitkeep
├── whatsnext-evolu-relay/ komt er bij het masterplan Umbrelapp
├── Docs/
├── tests/
└── README.md
```
De store-id is `whatsnext`. De prefix-eis is hard: mapnaam en manifest-`id` moeten gelijk zijn en allebei
met de store-id beginnen.
**Bijgewerkt op 25-08-2026**, toen de repo een store met meer dan één app werd. Hier stond nog de vorm van
18-08-2026 met store-id `electrumtls`; die was al achterhaald door fase 7. Wat de tweede app bewijst is dat
de keuze van 19-08 om de store naar de maker te noemen in plaats van naar deze ene app, klopte: er hoefde
niets voor te hernoemen.
### 4b. Backend-onafhankelijk, en waarom dat bijna niets kost
umbrelOS 1.3 heeft swappable dependencies. Fulcrum en ElectrumX declareren allebei `implements: [electrs]`
en hun `exports.sh` aliast `APP_ELECTRS_IP`, `APP_ELECTRS_NODE_IP` en `APP_ELECTRS_NODE_PORT` naar hun
eigen waarden. De gebruiker kiest de implementatie in de umbrelOS-instellingen; umbrelOS laadt de
`exports.sh` van de gekozen app.
Deze app hoeft daarvoor dus **geen keuzemechanisme te bouwen**. Het is dit:
```yaml
dependencies:
- electrs
```
en in de compose `${APP_ELECTRS_NODE_IP}:${APP_ELECTRS_NODE_PORT}` gebruiken.
Twee vallen om te vermijden. De eerste: de huidige compose zet `ELECTRS_HOST=${APP_ELECTRS_IP}`, en dat
is de **web-UI-container** van Electrs, niet de Electrum-server; dat moet `APP_ELECTRS_NODE_IP` worden.
De tweede: alleen `IP`, `NODE_IP` en `NODE_PORT` worden gealiast, dus alles wat Electrs-specifiek is
(zoals `APP_ELECTRS_RPC_HIDDEN_SERVICE`) mag hier niet gebruikt worden.
### 4c. De image: nginx `stream` in plaats van stunnel
Het huidige `alpine:latest` plus `apk add stunnel` bij elke start is op drie manieren fout: het is niet
te pinnen, het faalt zonder internet, en het maakt de starttijd afhankelijk van een Alpine-mirror. De
app-store-eis is een image gepind op de multi-arch index-digest.
Er zijn drie wegen, en de aanbeveling is de derde:
1. **Eigen image bouwen** met een `Dockerfile` en een workflow die multi-arch naar een registry duwt.
Correct, maar het voegt CI, een registry en een tweede uitleverstroom toe aan een app die verder uit
twee shellscripts bestaat.
2. **Een bestaande stunnel-image pinnen.** Er is geen onderhouden multi-arch stunnel-image die het
vertrouwen waard is. Afgevallen.
3. **De officiële `nginx`-image gebruiken en de `stream`-module de TLS-terminatie laten doen.** Die image
is multi-arch, wordt onderhouden en is gewoon te pinnen. Dan valt er meer weg dan alleen het
bouwprobleem:
- dezelfde container serveert de web-UI én termineert TLS, dus van drie containers blijft er één over.
**Bijgesteld 19-08-2026:** het zijn er weer twee, want er is een agent bijgekomen. De winst die hier
bedoeld werd blijft wel staan: geen Docker-socket, geen installatie bij het starten, en de
certificaatwissel is een reload. Zie het plan **Webinterface**, `PLAN.md` §4a0;
- een certificaatwissel wordt `nginx -s reload` **binnen** de container, dus de `cert-monitor` heeft de
Docker-socket niet meer nodig. Die socket is nu read-only gemonteerd, maar read-only op de
Docker-socket beschermt niets: wie de socket kan lezen kan containers starten en is daarmee root op
de host. Dat weghalen is de grootste beveiligingswinst in dit plan;
- een reload verbreekt bestaande verbindingen niet, een containerherstart wel.
**Te verifiëren voordat hierop gebouwd wordt:** dat de officiële `nginx:alpine` daadwerkelijk met
`--with-stream` en `--with-stream_ssl_module` gebouwd is. Dat is de aanname waar deze hele keuze op
rust en hij is in één commando te controleren (`nginx -V`). Klopt hij niet, dan valt dit terug op weg 1.
De `awk`-splitsing van de certificaat-chain uit `entrypoint.sh` blijft bruikbaar en wordt overgenomen.
### 4d. Poorten
`port:` in het manifest is de **web-UI-poort** van de tegel, niet de TLS-poort. Dat staat nu op 50002 en
is daarmee fout.
De TLS-poort blijft een gepubliceerde host-poort; daar helpt `app_proxy` niet, want dat is voor HTTP.
Welke poort dat wordt is een open punt in **Configuratie**: Fulcrum bezet host-poort 50002 en botst dus
met de huidige keuze.
### 4e. De eigen Git-server als app store
De repo staat op `https://sc.kamenier-hamer.nl/sysop/UmbrelApps.git` (tot 25-08-2026:
`.../ElectrumTLS.git`). umbreld valideert de URL alleen
met de `URL`-constructor en kloont met isomorphic-git; er is geen GitHub-eis. Wat er wél uit die aanroep
volgt, en op 18-08-2026 in orde is bevonden:
- **HTTPS, niet SSH.** In orde.
- **Anoniem kloonbaar.** In orde sinds 18-08-2026, maar het kostte moeite en de oorzaak was niet de
voor de hand liggende.
umbrelOS gaf `HTTP Error: 401 Unauthorized` bij het toevoegen van de store. De repo stond op public en
`REQUIRE_SIGNIN_VIEW` stond op `false`, en toch weigerde Gitea. De oorzaak was de zichtbaarheid van het
**account** `sysop`, die op "limited" stond. **Gitea staat niet toe dat een repo zichtbaarder is dan
zijn eigenaar**, dus de repo werd stilzwijgend teruggezet naar "intern", wat voor een niet-ingelogde
bezoeker hetzelfde is als privé. De knop "make public" leek te werken maar het label bleef op
"intern" staan, en dat is het enige zichtbare spoor.
Dit is eerst verkeerd beoordeeld, en de manier waaróp is het onthouden waard. Een `git ls-remote` vanaf
de werkmachine slaagde, ook met `GIT_TERMINAL_PROMPT=0`, en dat leek bewijs van anonieme toegang. Het
was het niet: Git Credential Manager stuurde de bij de push opgeslagen inloggegevens stilzwijgend mee.
`GIT_TERMINAL_PROMPT=0` onderdrukt alleen de **vraag** om een wachtwoord, niet het **aanleveren** ervan.
De test die het wel aantoont, zet de credential-helper leeg:
```
GIT_TERMINAL_PROMPT=0 git -c credential.helper= ls-remote <url>
```
Die faalt met `could not read Username`, en dat is de toestand die umbreld ziet.
- **SHA-1 als objectformaat.** In orde, na het opnieuw aanmaken van de repo. isomorphic-git kan geen
SHA-256; zie de naslag.
- **Geldig certificaat op `sc.kamenier-hamer.nl`.** Nog niet expliciet gecontroleerd vanaf de Umbrel.
- **Alles op de standaardbranch.** In orde: `main`, zowel lokaal als op de remote.
- **De URL is de identiteit van de store.** Wijzigt hij, dan ziet umbrelOS een andere store en moet hij
opnieuw toegevoegd worden.
## 5. Raakvlakken
**Configuratie** herschrijft dezelfde bestanden en wacht op dit plan. Twee dingen komen daarvandaan
terug: de TLS-poort (open punt daar, want Fulcrum bezet 50002) en het feit dat het domein na dit plan nog
steeds vast in de code staat.
**Webinterface** wacht op Configuratie en raakt hier alleen de verhuizing van `web/index.html`. Het
containerontwerp uit §4c is wel de reden dat dat plan zonder backend kan: de proxy en de webserver worden
één container, dus als de pagina geserveerd wordt, draait de proxy ook.
## 6. Verificatie
Er zijn geen tests in dit project en die zijn hier ook niet zinvol: de app is configuratie, geen code.
Wat er wél moet, en wat per se handmatig op het apparaat gebeurt:
- de store laat zich in umbrelOS toevoegen en de app verschijnt met icoon en tegel;
- installeren via de umbrelOS-interface werkt zonder SSH;
- **na `sudo reboot` komt de app vanzelf terug.** Dit is de aanleiding voor het hele plan en dus de
belangrijkste controle;
- de app komt ook terug nadat Electrs handmatig gestopt en gestart is;
- een Electrum-wallet verbindt over TLS en verifieert het certificaat;
- omschakelen naar Fulcrum in de umbrelOS-instellingen laat de app werken zonder aanpassing.
Wat automatisch gecontroleerd kan worden, en de moeite waard is omdat het de fouten vangt die je niet
ziet: dat de YAML geldig is, dat het `id` in het manifest gelijk is aan de mapnaam, en dat elke `image:`
een `@sha256:`-digest heeft.
@@ -0,0 +1,222 @@
# Voortgang - Appstore
> 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).
## 20-08-2026 - alles van de verse installatie nagekeken; dit plan zakt naar B
De hele checklist van de herinstallatie is bevestigd door de gebruiker: `data/` met `certs/` en `runtime/`
eronder, de pagina komt op zonder gekozen certificaat, `v0.0.9` staat achter de tagline, de keuzelijst voor
de Electrum-server verscheen bij het installeren, en daarna werkten kiezen én uploaden.
**Daarmee is dit plan in de kern af en gaat het van A naar B.** Alles waar het om begon is bewezen: de app
is een echte umbrelOS-app, hij komt op uit een schone installatie, en een wallet van buiten verbindt over
TLS. Wat er nog staat is niet te plannen (de herstartcontrole) of hoort bij het masterplan **Publicatie**.
**Webinterface** is daarmee tier A geworden en van 020 naar 005 hernummerd; dit plan houdt 010.
**Geraakt:** alleen documentatie. **Tests:** niet van toepassing.
## 20-08-2026 - verse installatie van 0.0.9, en die werkte
De gebruiker heeft de app gedeïnstalleerd en opnieuw geïnstalleerd in plaats van geüpdatet, zodat de
app-data schoon is. **Dat werkte, en het bewijst iets wat op een bestaande installatie niet te bewijzen
was:** de pagina komt omhoog op een installatie waar nog nooit een certificaat gekozen is. Dat is precies
de situatie waarin 0.0.3 stukliep, en de reparatie uit 0.0.4 was tot nu toe alleen getoetst op een
installatie die die keuze al had.
Wat er nog níet uit volgt: of de twee `.gitkeep`-bestanden hun werk deden. Een werkende app bewijst dat
niet, want Docker maakt een ontbrekende bind-mount-map zelf aan; het verschil zit alleen op schijf. Staat
als losse taak, want het is een eis voor het masterplan **Publicatie**.
**Geraakt:** alleen documentatie. **Tests:** niet van toepassing.
## 20-08-2026 - data onder data/, en een doel dat de maatstaf verandert
De gebruiker vroeg of het klopt dat alle runtime-bestanden in de installatiemap staan, want bij andere apps
ziet hij daar geen app-code. **Dat klopt, en het verschil zit niet in umbrelOS:** voor elke app wordt de
hele app-map naar `app-data` gekopieerd, maar andere apps hebben hun code in een image. Config uit
`app-data` mounten is wél een bestaand patroon; `electrs` doet zijn `torrc` precies zo, en `torrc` en
`*.template` staan met naam in de update-whitelist. Bijvangst: de invulling gebeurt met `envsubst`, en dat
is de onderbouwing van de architectuurregel over accolade-variabelen.
Waar deze app echt afweek was de plaats van de data, en dat is in **0.0.9** verholpen: `runtime/` en
`certs/` staan nu onder `data/`, met een `.gitkeep` per map. Aanleiding was het antwoord op de vervolgvraag:
**de gebruiker wil de app publiceren als standaard-app voor Umbrel.** De packaging-documentatie van umbrel
zegt woordelijk dat gebruikersstaat onder `${APP_DATA_DIR}/data/...` hoort, dus dit was geen smaak.
Dat doel verandert de maatstaf van "hij werkt hier" naar "iemand anders keurt het pakket goed", en dat is
een eigen plan geworden: **Publicatie**, als masterplan. Het meeste blijkt al goed; wat er nog moet is de
images pinnen, het app-id kaal maken en de manifestvelden op orde brengen. Het risico staat niet in die
lijst: deze app leest de certificaatmap van een andere app, en dat is precies waar een review over valt.
**Geraakt:** `docker-compose.yml`, `umbrel-app.yml` (0.0.9 en `backupIgnore`), twee `.gitkeep`-bestanden,
`Referenties/Umbrel-appstore-spec.md`, `OPEN.md` punt 7 en 9, nieuw
`Plannen/Masterplannen/Publicatie.PLAN.md`, `tests/test_server_start_zonder_certificaat.py`.
**Tests:** 48 goed 0 fout en 33 goed 0 fout, niets overgeslagen; de data-conventie is mutatie-getest.
## 20-08-2026 - de app werkt: pagina, keuze, TLS en een wallet van buiten
**De reparatie werkt.** Na de uitrol van 0.0.4 laadt het dashboard, met de melding "No certificate in use"
en de reden van de agent erbij. De agent vond **veertien certificaten in Zoraxy** en koos daarom niets; dat
is de weigering die zo bedoeld is, en het antwoord op de vraag die de log van 0.0.3 openliet.
Wat de gebruiker er meteen van zei: die reden somde alle veertien domeinnamen op, en dat is een muur tekst
die zegt wat de keuzelijst eronder al toont. **0.0.5** geeft alleen het aantal. De weigering zelf blijft.
**En daarna liep het hele pad.** Een certificaat kiezen op de pagina werkt: de agent nam de keuze aan, nginx
kwam met poort 50022 door, en na het aanpassen van de doorstuurregel in de router verbindt een wallet van
buiten. **Daarmee is fase 6 in de kern klaar** en is de app voor het eerst als umbrelOS-app af.
**Geraakt:** `agent.py.template` (`choose`), `umbrel-app.yml` (0.0.5), `tests/test_agent_certificates.py`,
`CHANGELOG.md`. **Tests:** 21 goed 0 fout en 15 goed 0 fout, niets overgeslagen; de ingekorte reden is
mutatie-getest. Nog open op de Umbrel: de herstartcontrole en het omschakelen naar Fulcrum.
## 20-08-2026 - 0.0.3 geinstalleerd, geen UI, en de oorzaak was het ontwerp
**De app is geinstalleerd en er kwam geen pagina.** De log van `app_proxy` meldde alleen dat de server niet
te bereiken was, en die van `server` herhaalde "Wachten tot de agent een certificaat gekozen heeft". Dat is
geen storing maar een klem in het ontwerp: het stream-blok met `listen 50022 ssl` includeerde de `cert.conf`
van de agent, nginx weigert te starten zonder dat certificaat, dus ook de pagina kwam niet omhoog. En de
pagina is precies waar je dat certificaat kiest.
**Verholpen in 0.0.4** door het TLS-deel naar `stream.conf.template` te verplaatsen; het `command`-blok legt
dat pas in `/var/lib/gate/tls/` als `cert.conf` bestaat, en `nginx.conf` haalt die map met een jokerteken op.
Meegenomen: een mislukte `nginx -s reload` brak de herlaadlus af, want die draait onder `set -e`.
Wat de log wél bewees: de agent start en luistert (`[gate] api listening on port 8000`), en Tor bootstrapt.
Wat de agent in de certificaatmappen vond, is nog onbekend; dat leest de pagina van 0.0.4 straks voor.
**Geraakt:** `nginx.conf.template`, `docker-compose.yml`, nieuw `stream.conf.template`, `umbrel-app.yml`
(0.0.4 en release notes), `CLAUDE.md`, `CHANGELOG.md`, nieuw
`tests/test_server_start_zonder_certificaat.py`. **Tests:** 21 goed 0 fout en 15 goed 0 fout, niets
overgeslagen. De reparatie zelf is **niet** geverifieerd: dat vraagt de uitrol op de Umbrel.
## 19-08-2026 - sessie afgesloten; dit plan is de enige tier A
Bij de prioriteitsherziening blijft **Appstore de enige tier A**, en de reden is dat er precies één
handeling openstaat waar al het andere achter wacht: de app installeren. **Webinterface is naar C gezakt**;
dat plan is af voor zover het zonder de Umbrel kan.
Wat er van de gebruiker moet, in deze volgorde: de doorstuurregel in de router naar 50022, dan de oude app
verwijderen, de store-URL opnieuw laten ophalen en `whatsnext-electrum-gate` installeren. Versie staat op
0.0.3 en de release notes kloppen, dus daar hoeft niets meer aan.
Ook opgemerkt en als taak vastgelegd: het plan **Configuratie** is grotendeels ingehaald door het werk van
vandaag. De agent doet de certificaatbronnen en de keuze al, en het hardgecodeerde domein is uit de compose
en uit `nginx.conf.template` verdwenen. Dat plan moet dus eerst opgeschoond worden voordat promotie nog
zin heeft, anders lijkt het groter dan het is.
**Geraakt:** alleen documentatie in deze entry.
**Tests:** 21 goed, 0 fout.
## 19-08-2026 - hernoemd naar Electrum Gate, fase 7 grotendeels af
De wallet-verbinding had de nacht doorstaan, dus de `proxy_timeout`-fix is bewezen en de storing waar de
sessie van gisteren mee begon is verklaard en verholpen.
De app heet nu **Electrum Gate**, met store-id `whatsnext` en app-id `whatsnext-electrum-gate`. Bij het
voorleggen van die keuze zat een fout in mijn eigen redenering: ik nam aan dat de repo-naam het store-id
bepaalt, omdat de store-URL de repo-URL is. Dat is niet zo, het store-id is gewoon een veld. Daardoor kon
het meteen goed en kan de repo-naam wachten tot er een publieke versie op GitHub komt.
Twee dingen die er vooruitlopend op andere plannen bij konden, omdat er tóch opnieuw geïnstalleerd wordt:
de TLS-poort is 50022 in plaats van 50002, wat de botsing met Fulcrum wegneemt, en de web-UI is
herbouwd. **Dat vraagt eenmalig een aanpassing van de doorstuurregel in de router**, anders komt een
wallet van buiten niet meer binnen.
De tagline en de beschrijving zijn Engels geworden en gaan nu over de afweging Tor tegenover snelheid, in
plaats van over TLS als middel.
**Geraakt:** `umbrel-app-store.yml`, de app-map (hernoemd), `umbrel-app.yml`, `docker-compose.yml`,
`nginx.conf.template`, `README.md` en de plannen.
**Tests:** dit project heeft geen suite. **Niets hiervan is op de Umbrel gedraaid**: de nieuwe app is nog
niet geïnstalleerd, en `version` staat nog op 2.0.1 omdat de gebruiker de UI eerst wilde zien.
## 18-08-2026 - storing na tien minuten, en de besluiten over naam en dashboard
Kort na de installatie viel de wallet-verbinding weg. De container bleef onafgebroken draaien
(`status=running`, `restarts=0`, schone nginx-log), wat de container als oorzaak uitsloot en één
verdachte overliet: `proxy_timeout` in een `stream`-blok staat standaard op tien minuten, en een
Electrum-wallet houdt een langlopende, grotendeels stille verbinding open. **Dit was een regressie van
diezelfde dag:** stunnel hanteerde twaalf uur, en dat verschil is bij de vertaling naar nginx niet
opgemerkt. Nu op twaalf uur, met `proxy_socket_keepalive`.
Meegenomen omdat ze bij het uitzoeken bovenkwamen: nginx draaide als achtergrondproces onder een shell,
die daardoor PID 1 was en signalen niet doorgaf, en het script eindigde met exitcode 0 als nginx omviel,
waardoor `restart: on-failure` niet zou ingrijpen. Nginx is nu met `exec` het hoofdproces.
De gebruiker vroeg of umbrelOS een update wel zou zien, en dat was de goede vraag: het `version`-veld
stond nog op 2.0.0, dus de fix was nooit uitgerold. Zonder melding en zonder fout. Na 2.0.1 kwam de
update binnen en werkte hij, wat meteen de hele uitleverketen aantoont.
Aan het eind besloten: de app gaat **Electrum Gateway** heten, want de oude naam beschreef het middel en
niet de waarde. En het dashboard krijgt alleen lokale gegevens; koersdata valt af omdat de browser van
elke bezoeker dan bij een derde partij aanklopt.
**Geraakt:** `docker-compose.yml`, `nginx.conf.template`, `umbrel-app.yml`, en de plannen.
**Tests:** niet van toepassing. **De `proxy_timeout`-fix is nog niet bewezen**: die brak pas na tien
minuten stilte, dus dat vraagt een langere observatie. Ook de herstart-controle staat nog open.
## 18-08-2026 - geinstalleerd op de Umbrel, wallet verbindt
De app is als Umbrel-app geïnstalleerd en een Electrum-wallet krijgt over TLS direct data terug. Dat
bewijst het hele pad in één keer: het `stream`-blok, het certificaat, en de verbinding naar de
Electrum-server via `APP_ELECTRS_NODE_IP`.
De weg erheen kostte drie omwegen die allemaal buiten de app zelf lagen. De Gitea-repo was als SHA-256
aangemaakt terwijl isomorphic-git alleen SHA-1 kan. Daarna weigerde Gitea anonieme toegang, en de oorzaak
bleek niet de repo maar de zichtbaarheid van het **account**: Gitea staat niet toe dat een repo
zichtbaarder is dan zijn eigenaar en zet hem dan zwijgend terug naar "intern". En de installatie faalde
drie keer op `Bind for 0.0.0.0:50002 failed: port is already allocated`, omdat de oude handmatige
container nog draaide. Dat laatste was de voorspelde en gewenste faalrichting: de bestaande dienst bleef
gewoon werken.
Eén les die het onthouden waard is: een `git ls-remote` vanaf de eigen machine bewees niets over anonieme
toegang, want de credential-helper stuurde de opgeslagen inloggegevens stilzwijgend mee. Dat leidde tot
een verkeerde diagnose die pas onderuitging met `-c credential.helper=`.
**Geraakt:** niets in de app; alleen documentatie en de configuratie van de Gitea-server.
**Tests:** niet van toepassing. Wallet-verbinding handmatig geverifieerd. **De herstart-controle staat
nog open**, en dat is de aanleiding van het hele plan.
## 18-08-2026 - fases 1 tot en met 5 gebouwd
De repo heeft de vorm van een community app store, de compose is omgezet naar
`app_proxy`, de backend loopt via de afhankelijkheid, en het manifest klopt met een eigen icoon.
Twee dingen die het uitzoekwerk opleverde en die het ontwerp veranderd hebben. De controle op de Umbrel
bevestigde dat `nginx:alpine` alle vier de stream-modules meebrengt, waardoor drie containers er één
werden en de Docker-socket kon vervallen; dat was de grootste beveiligingswinst en hij was gratis. En
umbreld blijkt bij een **update** alleen een whitelist te verversen (`docker-compose.yml`, `*.template`,
`exports.sh`, `torrc`, `hooks`, `umbrel-app.yml`), terwijl bij installatie de hele map wordt gekopieerd.
Daarom staat de nginx-configuratie in een `*.template` en niet in een los script: anders had een `git
push` stilzwijgend niets gedaan bij een bestaande installatie.
Ook bleek de `awk`-splitsing van de certificaatketen niet overgenomen te hoeven worden maar juist fout
te zijn: nginx wil de volledige keten in `ssl_certificate`, waar stunnel hem gesplitst wilde.
**Geraakt:** `umbrel-app-store.yml`, `electrumtls-electrum-tls/` (compose, manifest, `nginx.conf.template`,
`icon.png`), verwijderd zijn `entrypoint.sh`, `cert-watch.sh`, `install.sh` en `uninstall.sh`.
**Tests:** niet van toepassing, dit project heeft er geen. **Niets is op de Umbrel geïnstalleerd of
gedraaid**; dat is fase 6.
## 18-08-2026 - plan werd actief
Dit plan is als eerste actief geworden omdat het de aanleiding van de hele sessie oplost: de app draait
als een handmatig neergezette docker-compose die umbrelOS niet kent, en moet na elke herstart of na een
storing in een afhankelijkheid met de hand opgestart worden.
De repo is dezelfde dag onder versiebeheer gebracht en gepusht naar
`https://sc.kamenier-hamer.nl/sysop/ElectrumTLS.git`. Dat kostte één omweg: de Gitea-repo was als SHA-256
aangemaakt, en de push faalde met een melding die de oorzaak niet noemt. Bij het uitzoeken bleek het
probleem groter dan de push, want umbreld kloont met isomorphic-git en die kan uitsluitend SHA-1. De repo
is opnieuw aangemaakt als SHA-1.
Twee dingen die het uitzoekwerk opleverde en die het werk kleiner maken dan gedacht. umbrelOS doet géén
hostnaamcontrole op de store-URL, dus een eigen Gitea kan de app store zijn. En wisselen tussen Electrs,
Fulcrum en ElectrumX vraagt geen eigen mechanisme: die declareren `implements: [electrs]` en aliassen
zichzelf naar `APP_ELECTRS_*`, dus twee regels volstaan. Dat was oorspronkelijk een apart plan waard en
is nu fase 3.
**Geraakt:** promotie vanuit `Plannen/Masterplannen/Appstore.PLAN.md`; nog geen app-bestanden.
**Tests:** niet van toepassing, dit project heeft er geen. Er is nog niets op de Umbrel geverifieerd.
+174
View File
@@ -0,0 +1,174 @@
# Taken - Appstore
> Prioriteit: **B** | Afhankelijk van:
>
> **Van A naar B op 20-08-2026**, bij de prioriteitsherziening na de verse installatie van 0.0.9. Alles
> waar dit plan om begon is af: de app is een echte umbrelOS-app, hij komt op uit een schone installatie,
> en een wallet van buiten verbindt over TLS. Wat er nog staat is niet te plannen (de herstartcontrole
> wacht op een herstart die er toch komt) of hoort bij het masterplan **Publicatie**. Het plan houdt zijn
> nummer 010; alleen **Webinterface** kreeg een nieuw nummer bij de promotie naar A.
## Volgende stap
- [x] **De maphiërarchie na een verse installatie nagekeken. In orde (20-08-2026):** in
`~/umbrel/app-data/whatsnext-electrum-gate/` staat `data/` met `certs/` en `runtime/` eronder. Dat is
de vorm die de appstore-eis vraagt, en samen met de twee `.gitkeep`-bestanden in de repo is dat punt
van het masterplan **Publicatie** dus af. Wat deze controle niet onderscheidt is wie de mappen
aanmaakte, `rsync` of Docker; voor de eis maakt dat niet uit, want die gaat over wat er in de repo
staat
- [x] **0.0.9 opnieuw geïnstalleerd in plaats van geüpdatet. Gelukt (20-08-2026).** De gebruiker koos een
deïnstallatie plus verse installatie omdat de app-data dan schoon is, en dat werkte. Daarmee is
bewezen wat op een bestaande installatie niet te bewijzen was: **de pagina komt omhoog op een
installatie waar nog nooit een certificaat gekozen is.** Dat is precies de situatie waarin 0.0.3
stukliep
- [ ] **Na `sudo reboot` controleren dat de app vanzelf terugkomt.** Dit is de aanleiding voor het hele
plan en nu het enige wat er in de kern nog openstaat; zie fase 6. **Eigenaar: gebruiker, op een
moment dat het kan**
- [x] **De doorstuurregel in de router naar poort 50022 gezet (20-08-2026).** Nodig omdat de app niet meer
op 50002 luistert; zonder deze regel komt een wallet van buiten niet binnen
- [x] **Een certificaat kiezen op de pagina, en daarmee het hele pad. Gelukt (20-08-2026):** de keuze werd
aangenomen, nginx kwam met poort 50022 door, en een wallet van buiten verbindt. De app doet waarvoor
hij bestaat
- [x] **0.0.4 uitrollen en kijken of de pagina nu komt. Gelukt (20-08-2026).** De pagina laadt, de
melding "No certificate in use" staat er met de reden van de agent, en de veertien certificaten uit
Zoraxy staan als keuzelijst. Daarmee is de klem uit 0.0.3 bewezen verholpen
- [x] De oude app `electrumtls-electrum-tls` verwijderen in umbrelOS, de store-URL opnieuw laten ophalen,
en `whatsnext-electrum-gate` installeren. **Gedaan (20-08-2026), en het leverde meteen een fout op:**
er kwam geen UI. Oorzaak was geen installatiefout maar een ontwerpfout, verholpen in 0.0.4, zie
[PROGRESS.md](PROGRESS.md) en [CHANGELOG-electrum-gate.md](../../../CHANGELOG-electrum-gate.md)
Daarna, in deze volgorde:
- [x] `version` op **0.0.3** en de release notes herschreven (19-08-2026). Niet 3.0.0: de gebruiker heeft
de nummering opnieuw onder 1.0 gezet, omdat 1.0.0 en 2.0.x een rijpheid beweerden die er niet was.
De stap terug in het nummer kan omdat de oude app gedeïnstalleerd wordt en dit een nieuwe app-id is,
dus er is geen geïnstalleerd manifest om tegen te vergelijken. **Vanaf hier moet het nummer altijd
omhoog**; zie [CHANGELOG-electrum-gate.md](../../../CHANGELOG-electrum-gate.md)
- [x] Controleren of de wallet-verbinding de nacht heeft doorstaan. **Gelukt (19-08-2026):** de
verbinding stond er 's ochtends nog. Daarmee is de `proxy_timeout`-fix bewezen en de storing
verklaard en verholpen
- [x] Het plan **Webinterface** promoveren naar `Actief/`; het werk daaraan is begonnen
- [ ] Het plan **Configuratie** herzien vóórdat het gepromoveerd wordt. Bij het afsluiten van de sessie op
19-08-2026 bleek het grotendeels ingehaald: de agent doet §4e en §4f al, en het hardgecodeerde
domein is uit `docker-compose.yml` én uit `nginx.conf.template` verdwenen, dus fase 2 is op de
README na klaar. Wat er nog echt in zit, is kleiner dan het plan suggereert. Eerst opschonen, dan
beslissen of promotie nog nodig is
## Fase 1 - De repo-vorm
- [x] `umbrel-app-store.yml` aanmaken met `id: electrumtls` en een `name`
- [x] Map `electrumtls-electrum-tls/` aanmaken en de app-bestanden erheen verplaatsen
- [x] `id` in `umbrel-app.yml` op `electrumtls-electrum-tls` zetten, gelijk aan de mapnaam
- [x] `install.sh` en `uninstall.sh` verwijderen; umbreld beheert dit onder umbrelOS 1.x
- [x] Verplaatsen en inhoudelijk wijzigen in **twee** commits houden, anders is de verhuizing niet als
"alleen verplaatst" na te lezen
## Fase 2 - De compose
- [x] `app_proxy`-service toevoegen met `APP_HOST: electrumtls-electrum-tls_server_1`
- [x] Het externe `umbrel_main_network` en het top-level `networks:`-blok verwijderen
- [x] `version: "3.7"` weghalen; die sleutel is verouderd
- [x] Certificaatmount op `${UMBREL_ROOT}/app-data/zoraxy/...` in plaats van een absoluut pad
- [x] De handmatige `${APP_ELECTRUM_TLS_WEB_PORT}`-plaatshouder eruit; de web-UI loopt via `app_proxy`
- [x] **Uitzoeken hoe `entrypoint.sh`, `cert-watch.sh` en `web/` in `${APP_DATA_DIR}` terechtkomen.**
Bij installatie kopieert umbreld de hele app-map met `rsync --archive`, dus de mounts kloppen. Bij
een **update** wordt alleen een whitelist ververst; zie [OPEN.md](OPEN.md) punt 6
- [ ] Logica die later nog moet kunnen wijzigen uit `entrypoint.sh` halen en in de compose of in een
`*.template` zetten, anders levert een `git push` geen update op bij een bestaande installatie
## Fase 3 - Backend-onafhankelijk
- [x] `dependencies: [electrs]` in het manifest laten staan en controleren
- [x] `ELECTRS_HOST` van `${APP_ELECTRS_IP}` naar `${APP_ELECTRS_NODE_IP}` (nu wijst hij naar de
web-UI-container van Electrs, niet naar de Electrum-server)
- [x] `ELECTRS_PORT` van de vaste 50001 naar `${APP_ELECTRS_NODE_PORT}`
- [x] Controleren dat er nergens een Electrs-specifieke variabele gebruikt wordt; alleen `IP`, `NODE_IP`
en `NODE_PORT` worden door Fulcrum en ElectrumX gealiast
- [x] De terugval `electrs_electrs_1` in `entrypoint.sh` vervangen door hard stoppen met een melding;
een oude containernaam als standaard laat de app draaien terwijl hij naar niets wijst
- [ ] Op de Umbrel verifiëren: omschakelen naar Fulcrum en controleren dat het zonder aanpassing werkt
(staat ook in fase 6)
## Fase 4 - De image
- [x] Uitkomst van de "Volgende stap" verwerken: nginx `stream`, of terugvallen op een eigen image
- [ ] Image pinnen op de multi-arch index-digest, geverifieerd met `docker buildx imagetools inspect`
(kan alleen op de Umbrel; staat als TODO in de compose). **Sinds 20-08-2026 ook de grootste
openstaande eis voor het masterplan Publicatie**, want de officiële store eist
`repo:versie@sha256:<digest>` met beide architecturen erin. Blijft hier staan tot dat plan actief
wordt; een taak op twee plekken loopt uit elkaar
- [x] `apk add` bij het starten verwijderen
- [x] De `cert-monitor`-service samenvoegen met de proxy en de Docker-socket-mount schrappen
- [x] Certificaatwissel omzetten naar een reload binnen de container in plaats van een containerherstart
- [x] `entrypoint.sh` en `cert-watch.sh` verwijderen; de logica staat nu in de compose en in
`nginx.conf.template`, die allebei bij een update wél ververst worden
## Fase 5 - Het manifest
- [x] `port` op de web-UI-poort zetten in plaats van 50002
- [x] Eigen icoon toevoegen; verwijst nu naar het Electrs-icoon in andermans repo
- [x] `gallery` invullen of tot het minimum beperken; het wijst nu naar een screenshot van Electrs
- [x] `developer`, `website`, `repo`, `support`, `submitter` en `submission` kloppend maken
- [x] `releaseNotes` en `version` bijwerken
- [x] `tagline` en `description` bijwerken; die noemden stunnel, dat er niet meer is
- [x] Controleren dat Gitea `icon.png` als afbeelding serveert. **Ja**, na het omzetten van de
accountzichtbaarheid: 33,9 kB als `image/png`. De eerdere 401 en 404 kwamen van dezelfde oorzaak
als de mislukte kloon, dus er is geen `data:`-URI nodig
## Fase 6 - Installeren en verifiëren
- [x] De bestaande handmatige installatie stoppen; die hield poort 50002 vast en liet de installatie drie
keer falen. Stond in `~/umbrel/home/Containers/ElectrumTLS`
- [x] Store-URL toevoegen in umbrelOS en de app installeren
- [x] Verbinden met een Electrum-wallet over TLS. Werkt: de wallet krijgt direct data terug, wat het hele
pad bewijst, dus TLS-terminatie, certificaat en de verbinding naar de Electrum-server
- [ ] **Na `sudo reboot` controleren dat de app vanzelf terugkomt.** Dit is de aanleiding voor het plan
en de enige controle die er nog echt toe doet. **Uitgesteld op 18-08-2026:** de Umbrel is een
productiemachine en wordt niet op verzoek herstart. Zie [OPEN.md](OPEN.md) punt 7
- [ ] Vervanger die nu wél kan: de app stoppen en starten via umbrelOS. Dat gebruikt hetzelfde
`app-script start`-pad als het opstarten na een herstart, maar bewijst niet de volgorde ten
opzichte van Zoraxy
- [ ] Controleren dat de app terugkomt nadat Electrs gestopt en gestart is
- [ ] **De oude stunnel-container weghalen, vóór de Fulcrum-test.** Hij bindt poort 50002 en dat is de
poort die Fulcrum op de host wil, dus die test kan niet slagen zolang hij bestaat. Met
`docker compose -f ~/umbrel/home/Containers/ElectrumTLS/docker-compose.yml down`. Het externe
`umbrel_main_network` blijft daarbij staan; dat verwijdert compose nooit. **Eigenaar: gebruiker**
- [ ] Omschakelen naar Fulcrum in de umbrelOS-instellingen en controleren dat het zonder aanpassing werkt
- [ ] Pas ná een geslaagde herstart de oude map `~/umbrel/home/Containers/ElectrumTLS` opruimen; tot dan
is dat de terugweg. Let op de volgorde: de container mount zijn `entrypoint.sh` uit die map, dus
eerst de container weg en dan de map
## Fase 7 - Naam en identiteit
Toegevoegd 18-08-2026. De naam beschreef het middel (TLS) en niet wat je ermee kunt.
- [x] Naam wordt **Electrum Gate**. Bijgesteld op 19-08-2026; op 18-08-2026 stond hier nog Electrum
Gateway. TLS is uit de naam en uit de tagline verdwenen; die gaat nu over de afweging Tor tegenover
snelheid
- [x] Store-id `whatsnext`, app-id en mapnaam `whatsnext-electrum-gate`. Zie [OPEN.md](OPEN.md) punt 8
- [x] `developer` en `submitter` op `WhatsNext?` (19-08-2026)
- [x] `tagline` en `description` herschreven, in het Engels, met de Tor-afweging en de lijst met clients
waarvoor de app nuttig is
- [ ] Eigen icoon van de gebruiker inbouwen, in de kleuren van het design-systeem. Het huidige
`icon.png` staat er nog en is niet het definitieve
- [x] **De repo hernoemd naar `UmbrelApps`, met `icon`, `website`, `repo`, `support` en `submission` mee
(25-08-2026, in 0.0.15).** Niet uitgesteld tot GitHub, zoals hier stond, maar afgedwongen door Evolu
Relay: umbrelOS leest per store één repo, dus een tweede app maakte een naam naar deze ene app
onhoudbaar. Het **app-id** ging niet mee en dat hoefde ook niet; dat hangt aan het store-id en niet
aan de URL
- [ ] **In umbrelOS de oude store verwijderen en de nieuwe URL toevoegen.** Hier valt te zien of een
geïnstalleerde app een wisseling van store-URL overleeft, en dat is **niet uitgezocht**; ga er niet
van uit. Zie [OPEN.md](OPEN.md) punt 8. **Eigenaar: gebruiker**
- [ ] Pas daarna de repo `ElectrumTLS` op de Git-server weghalen. Dat is niet alleen opruimen: zolang hij
bestaat, staat het domein nog in een publieke historie. Zie [OPEN.md](OPEN.md) punt 3.
**Eigenaar: gebruiker**
## Geblokkeerd / wacht op
- [x] Keuze van de TLS-poort - **vervallen 19-08-2026**. Open punt 1 van het plan **Configuratie** is
beslist op 50022 en meteen doorgevoerd, omdat de app toch opnieuw geïnstalleerd wordt. Daarmee is
ook de botsing met Fulcrum weg en kan de omschakelcontrole in fase 3 en fase 6 echt gedaan worden