194 lines
10 KiB
Markdown
194 lines
10 KiB
Markdown
# 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.
|