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)