From a694a9cbd1e3db01d1534c349a10df4bf3532f57 Mon Sep 17 00:00:00 2001 From: Harmen Date: Tue, 8 Sep 2026 16:36:30 +0200 Subject: [PATCH] Relay 0.6.0: alles in een image, de app-map is leeg Plan Eigenimage, fase 5, op keuze van de gebruiker: een image met de relay erbij en geen tweede recept. agent.py, nginx.conf en index.html verhuizen naar tools/evolu-relay/ naast src/; de Dockerfile blijft op node:24-slim en haalt nginx en python3 uit apt. Drie containers uit een image: de relay als node via de compose, de agent en nginx als root. Anders dan alleen verplaatst: user www-data in nginx.conf (Debian heeft geen gebruiker nginx), geen USER meer in de image, de versie in de kop via api/status met RELAY_APP_VERSION. VERSION 0.6.0, manifest 0.6.0, drie keer dezelfde tag in de compose, ongepind tot de eerste push. Tests mee verhuisd; de vormtest toetst de drie tags tegen VERSION. Niet gebouwd: er is hier geen Docker. Co-Authored-By: Claude Fable 5.1 --- .dockerignore | 11 +- CLAUDE.md | 26 +++-- Docs/CHANGELOG-evolu-relay.md | 22 ++++ Docs/CONTINUE_HERE.md | 2 +- Docs/KNOWLEDGE.md | 5 +- .../Plannen/Actief/004-Eigenimage/PROGRESS.md | 13 +++ Docs/Plannen/Actief/004-Eigenimage/TAKEN.md | 31 ++++-- Docs/Plannen/Actief/008-Umbrelapp/TAKEN.md | 12 +- README.md | 33 +++--- tests/test_relay_agent.py | 36 +++--- tools/evolu-relay/Dockerfile | 46 ++++++-- .../evolu-relay/agent.py | 17 ++- tools/evolu-relay/build.sh | 91 ++++++++++----- .../evolu-relay/index.html | 19 ++-- .../evolu-relay/nginx.conf | 24 ++-- tools/voorbeeldpagina.mjs | 1 + whatsnext-evolu-relay/docker-compose.yml | 104 ++++++++---------- whatsnext-evolu-relay/umbrel-app.yml | 11 +- 18 files changed, 327 insertions(+), 177 deletions(-) rename whatsnext-evolu-relay/agent.py.template => tools/evolu-relay/agent.py (95%) rename whatsnext-evolu-relay/index.html.template => tools/evolu-relay/index.html (98%) rename whatsnext-evolu-relay/nginx.conf.template => tools/evolu-relay/nginx.conf (74%) diff --git a/.dockerignore b/.dockerignore index 23d6f3b..41f254b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,10 @@ -# De bouwcontext van tools/electrum-gate/build.sh is de repo-root, omdat de -# Dockerfile icon.png uit de app-map kopieert. Alles wat de Dockerfile niet -# noemt blijft hier buiten, zodat de context klein is en een wijziging in de -# documentatie de bouwcache niet ongeldig maakt. +# De bouwcontext van tools/*/build.sh is de repo-root, omdat elke Dockerfile +# icon.png uit zijn app-map kopieert. Alles wat de Dockerfiles niet noemen blijft +# hier buiten, zodat de context klein is en een wijziging in de documentatie de +# bouwcache niet ongeldig maakt. * !tools/electrum-gate !whatsnext-electrum-gate/icon.png +!tools/evolu-relay +!whatsnext-evolu-relay/icon.png +tools/evolu-relay/node_modules diff --git a/CLAUDE.md b/CLAUDE.md index 5fab68b..879c1a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,8 +26,8 @@ Deze gelden voor élke app hier, want het zijn eigenschappen van umbrelOS. Met b sloopt Python-code, en in een nginx-config betekent het geen `$host`, geen `log_format` en geen `access_log` met variabelen. Moet er tóch een dollarteken in een nginx-directive, dan hoort die directive in een bestand dat het `command`-blok van de compose wegschrijft (daar is `$$` te ontsnappen), of in een -eigen image, waar geen envsubst aan te pas komt. Electrum Gate doet sinds 0.1.0 het tweede; Evolu Relay -heeft zijn agent en pagina nog als `*.template`. +eigen image, waar geen envsubst aan te pas komt. Beide apps doen sinds september 2026 het tweede; er +staat geen `*.template` meer in deze repo, maar de regel blijft gelden voor wie er een bij zet. **Wat bij een update meekomt is een whitelist**: `docker-compose.yml`, `*.template`, `exports.sh`, `torrc`, `hooks` en `umbrel-app.yml`. Al het andere bereikt een bestaande installatie nooit, zonder foutmelding. @@ -78,11 +78,12 @@ Er is ook een test over de **pagina's** van beide apps, en die is geen Python: node tests/test_paginas_parsen.mjs ``` -Hij toetst dat de JavaScript in elke statuspagina parseert. Een pagina staat op een van twee plekken: als -`index.html.template` in de app-map (Evolu Relay; dan toetst hij ook dat het script heel blijft ná de -invulling door umbreld) of als `index.html` in `tools//` (Electrum Gate sinds 0.1.0; dan toetst hij -dat er geen accolade-variabele meer in staat). Dat is de enige klasse paginafouten die niet op het -apparaat gevonden hoeft te worden; alles wat de pagina *toont* blijft handwerk in een browser. +Hij toetst dat de JavaScript in elke statuspagina parseert. Een pagina kan op twee plekken staan: als +`index.html.template` in de app-map (dan toetst hij ook dat het script heel blijft ná de invulling door +umbreld) of als `index.html` in `tools//` (dan toetst hij dat er geen accolade-variabele meer in +staat). Sinds september 2026 staan beide pagina's op de tweede plek. Dat is de enige klasse paginafouten +die niet op het apparaat gevonden hoeft te worden; alles wat de pagina *toont* blijft handwerk in een +browser. **Wil je een pagina bekijken, gebruik `tools/voorbeeldpagina.mjs`.** Een statuspagina is niet zomaar te openen: de gegevens komen van een agent die alleen in de app bestaat, en in een `*.template` staan @@ -215,8 +216,17 @@ relay ergens kunt neerzetten is dat hij de sleutel niet heeft, en het manifest b Onderhoudsfuncties die de inhoud kennen horen in de tool in `HomeGit/Trezor`. Zie §11 van hetzelfde naslagdocument en `OPEN.md` punt 11. +**Alles zit in één image, sinds 0.6.0 (08-09-2026): de relay, de agent en de pagina.** `tools/evolu-relay/` +heeft naast `src/` ook `agent.py`, `nginx.conf` en `index.html`; in de app-map staan alleen nog compose, +manifest, icoon en `data/`. Basis is `node:24-slim` (om `better-sqlite3`, dat geen musl-binaries heeft) +met nginx en python3 uit apt. Twee gevolgen die je moet kennen: nginx draait als `www-data`, want Debian +heeft geen gebruiker `nginx`; en de image heeft geen `USER`, dus de compose zet `user: node` op de +relay-service en de andere twee draaien als root. De versie in de kop van de pagina komt uit `api/status` +via `RELAY_APP_VERSION`. Verhoog je `VERSION` in `build.sh`, dan ook het manifest en de tag in de compose, +**drie keer**. + **Deze app draait en wordt gebruikt sinds 28-08-2026**, maar dat geldt niet voor elke wijziging: er zit een -bouwstap tussen de repo en het apparaat. Een wijziging in `tools/evolu-relay/src/` is pas uitgerold als de +bouwstap tussen de repo en het apparaat. Een wijziging in `tools/evolu-relay/` is pas uitgerold als de image gebouwd, geduwd en met zijn digest in de compose gezet is, en dat kan alleen de gebruiker. Meld dus per wijziging wat er wél geverifieerd is; "de app werkt" is geen uitspraak over de code van vandaag. diff --git a/Docs/CHANGELOG-evolu-relay.md b/Docs/CHANGELOG-evolu-relay.md index 816043c..0e72433 100644 --- a/Docs/CHANGELOG-evolu-relay.md +++ b/Docs/CHANGELOG-evolu-relay.md @@ -19,6 +19,28 @@ De regel is: **verhoog je `VERSION`, dan verhoog je ook het manifest; andersom n > geschreven uit de `releaseNotes` in het manifest en uit `PROGRESS.md` van het plan **Umbrelapp**. Ze zijn > daarom korter dan de rest. +## 0.6.0 - 08-09-2026 + +**Nog niet gebouwd.** `VERSION` in `tools/evolu-relay/build.sh` staat op 0.6.0 en de compose verwijst +drie keer naar die tag zonder digest; die komt erbij zodra de gebruiker de image op de Umbrel gebouwd en +geduwd heeft. Tot die tijd is dit een versie in de repo en niet op het apparaat. + +**Alles in één image.** Tot 0.5.6 zat alleen de relay in de eigen image en draaiden de agent en de pagina +als `*.template` op `python:3-alpine` en `nginx:alpine`. Nu zitten `agent.py`, `nginx.conf` en `index.html` +in dezelfde image als de relay, in `tools/evolu-relay/` naast `src/`. De basis blijft `node:24-slim` (om +`better-sqlite3`), met nginx en python3 uit apt erbij. Drie containers uit één image: de relay als +gebruiker `node` via de compose, de agent en nginx als root. Beslist door de gebruiker op 08-09-2026, na +dezelfde stap bij Electrum Gate; zie het plan **Eigenimage**, `PLAN.md` §8. + +Wat er daardoor anders is dan alleen verplaatst: + +- nginx draait als `www-data`, want Debian heeft geen gebruiker `nginx`; +- de pagina haalt de versie uit `api/status`; de agent krijgt hem als `RELAY_APP_VERSION` uit de compose. + umbreld vulde hem tot nu rechtstreeks in de pagina in; +- `icon.png` zit in de image en is daarmee updatebaar. Het blijft ook in de app-map, want het manifest + wijst ernaar; +- de app-map bevat nog `docker-compose.yml`, `umbrel-app.yml`, `icon.png` en `data/`. + ## 0.5.6 - 07-09-2026 **Alleen de pagina, dus geen nieuwe image.** `VERSION` in `tools/evolu-relay/build.sh` blijft op 0.5.0. diff --git a/Docs/CONTINUE_HERE.md b/Docs/CONTINUE_HERE.md index e6ab1c3..eed9e54 100644 --- a/Docs/CONTINUE_HERE.md +++ b/Docs/CONTINUE_HERE.md @@ -23,7 +23,7 @@ | Plan | App | Volgende stap | Status | |-|-|-|-| -| [Eigenimage](Plannen/Actief/004-Eigenimage/TAKEN.md) | **beide** | **Electrum Gate draait sinds 08-09-2026 op zijn eigen image (0.1.0), gepind, herinstalleerd, app-datamap schoon.** De bouw slaagde in één keer en de pagina toont v0.1.0. Voor Gate is dit plan af. **Volgende stap: hetzelfde voor de agent en de pagina van Evolu Relay** (§8), die nog als `*.template` op `python:3-alpine` en `nginx:alpine` draaien. Eerste keuze daar: een tweede recept naast de relay-image, of één image met de relay erbij. Daarvóór: **fase 1 tot en met 3 gedaan op 07-09-2026.** Het recept staat in `tools/electrum-gate/` (Dockerfile, build.sh, entrypoint.sh en de vier voormalige templates), de app-map bevat alleen nog compose, manifest, icoon en `data/`, en de compose is voor het eerst zonder shellblok. Eén image voor beide containers, gebouwd op de Umbrel; open punt 2 en 3 zijn daarmee beslist. **Volgende stap is van de gebruiker en kan alleen op de Umbrel:** `sh tools/electrum-gate/build.sh`, duwen, de digest twee keer in de compose, committen, updaten, en dan de controles van fase 4. Reken op minstens één ronde, want de image is nog nooit gebouwd; de tests toetsen de tekst van het recept en niet het resultaat. **Daarna:** hetzelfde voor de agent en de pagina van Evolu Relay (§8), pas als de weg bij Gate bewezen is. Multi-arch en de anonieme pull horen bij **Publicatie-Gate** | 🔶 | +| [Eigenimage](Plannen/Actief/004-Eigenimage/TAKEN.md) | **beide** | **Evolu Relay 0.6.0 staat klaar in de repo (08-09-2026), ongebouwd: één image met relay, agent en pagina erin**, op keuze van de gebruiker. `tools/evolu-relay/` heeft nu naast `src/` ook `agent.py`, `nginx.conf` en `index.html`; de app-map bevat alleen nog compose, manifest, icoon en `data/`. **Volgende stap is van de gebruiker en kan alleen op de Umbrel:** `sh tools/evolu-relay/build.sh`, duwen, de digest drie keer in de compose, committen, updaten, en nakijken (v0.6.0 in de kop, een sync, de allowlist en labels intact). Eerste image met apt erin, dus reken op een ronde. **Daarna is dit plan voor beide apps af en gaat het naar `Archief/`.** Eerder vandaag: **Electrum Gate draait op zijn eigen image (0.1.0)**, gepind, herinstalleerd, app-datamap schoon; de bouw slaagde in één keer. Het recept staat in `tools/electrum-gate/` (Dockerfile, build.sh, entrypoint.sh en de vier voormalige templates), de app-map bevat alleen nog compose, manifest, icoon en `data/`, en de compose is voor het eerst zonder shellblok. Eén image voor beide containers, gebouwd op de Umbrel; open punt 2 en 3 zijn daarmee beslist. **Volgende stap is van de gebruiker en kan alleen op de Umbrel:** `sh tools/electrum-gate/build.sh`, duwen, de digest twee keer in de compose, committen, updaten, en dan de controles van fase 4. Reken op minstens één ronde, want de image is nog nooit gebouwd; de tests toetsen de tekst van het recept en niet het resultaat. **Daarna:** hetzelfde voor de agent en de pagina van Evolu Relay (§8), pas als de weg bij Gate bewezen is. Multi-arch en de anonieme pull horen bij **Publicatie-Gate** | 🔶 | ## B - Los oppakbaar (geen blokkade, geen vaste volgorde) diff --git a/Docs/KNOWLEDGE.md b/Docs/KNOWLEDGE.md index fe4486c..a38be4b 100644 --- a/Docs/KNOWLEDGE.md +++ b/Docs/KNOWLEDGE.md @@ -24,8 +24,9 @@ De keten die eronder ligt, van achter naar voren: zelf uitgeeft, en die is er precies één per app. Stand op 08-09-2026: Electrum Gate draait op zijn eigen image sinds 0.1.0, gepind, en zijn twee vreemde -images zijn weg. Bij Evolu Relay staan `python:3-alpine` en `nginx:alpine` er nog voor de agent en de -pagina, en daarvoor geldt de regel hierboven onverkort. +images zijn weg. Evolu Relay heeft sinds 0.6.0 alles in zijn eigen image; de pin daarvan komt erbij bij de +eerste push. Daarmee is de keten hieronder afgelopen en is er per app precies één image om te pinnen. De +regel blijft staan voor wie er een vreemde image bij zet. Twee dingen die dit níet zegt, want anders slaat het de andere kant op door: diff --git a/Docs/Plannen/Actief/004-Eigenimage/PROGRESS.md b/Docs/Plannen/Actief/004-Eigenimage/PROGRESS.md index ee05865..8ed00c2 100644 --- a/Docs/Plannen/Actief/004-Eigenimage/PROGRESS.md +++ b/Docs/Plannen/Actief/004-Eigenimage/PROGRESS.md @@ -3,6 +3,19 @@ > 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). +## 08-09-2026 (later) - Evolu Relay: alles in één image, in de repo klaar + +Direct erna, op verzoek van de gebruiker en met zijn keuze: één image met de relay erbij, geen tweede +recept. De drie templates zijn met `git mv` naar `tools/evolu-relay/` gegaan, naast `src/`; de Dockerfile +blijft op `node:24-slim` (om `better-sqlite3`) en haalt nginx en python3 uit apt. Twee dingen die Debian +anders doet dan de nginx-image en die je moet kennen: geen gebruiker `nginx` (dus `www-data`), en geen +`USER` meer in de image, zodat de compose de relay met `user: node` laat draaien en de andere twee als root. + +De versie in de kop loopt nu via `api/status`, zoals bij Gate. Versie 0.6.0 in manifest, `VERSION` en drie +keer in de compose, ongepind. Tests: alle zes groen, niets overgeslagen; de vormtest toetst de drie tags +tegen `VERSION`. In de voorbeeldpagina staat v0.6.0 in de kop. **Niet geverifieerd: de image**, en dit is +de eerste met apt erin. Bouwen is aan de gebruiker. + ## 08-09-2026 - gebouwd, gepind, geïnstalleerd: Gate draait op zijn eigen image De gebruiker bouwde en duwde de image op de Umbrel; de bouw slaagde in één keer, dus de zorg over `apk add diff --git a/Docs/Plannen/Actief/004-Eigenimage/TAKEN.md b/Docs/Plannen/Actief/004-Eigenimage/TAKEN.md index a47fe26..0d2d7d0 100644 --- a/Docs/Plannen/Actief/004-Eigenimage/TAKEN.md +++ b/Docs/Plannen/Actief/004-Eigenimage/TAKEN.md @@ -29,9 +29,10 @@ mutatie-getest; details in [PROGRESS.md](PROGRESS.md) - [x] **Fase 4 gedaan (08-09-2026): gebouwd, geduwd, gepind, geüpdatet, herinstalleerd.** Electrum Gate draait op zijn eigen image en de app-datamap is schoon. Voor Gate is dit plan klaar -- [ ] **Volgende stap: hetzelfde voor de agent en de pagina van Evolu Relay.** Zie "Daarna" onderaan. De - weg is nu bij Gate bewezen, dus dit kan zonder onbekenden: `tools/evolu-relay/` krijgt naast de - relay-image een tweede recept, of één image met de relay erbij; dat is de eerste keuze om te maken +- [x] **Evolu Relay in de repo gedaan (08-09-2026), als 0.6.0:** één image met de relay erbij, zie fase 5 +- [ ] **Volgende stap: de Relay-image bouwen, duwen, pinnen en updaten.** Van de gebruiker; zie fase 5. + Daarna is dit plan voor beide apps af en gaat het naar `Archief/`; wat overblijft (multi-arch, + anonieme pull per verhoging) hoort bij **Publicatie-Gate** en **Publicatie-Relay** - [x] **Open punt 1 beantwoord (27-08-2026): voor nu Gitea, bij inlevering opnieuw kijken.** Daarmee is de enige vraag weg die vóór het bouwen beantwoord moest zijn. Zie [OPEN.md](OPEN.md) @@ -94,12 +95,26 @@ - [x] **`icon.png` is hiermee updatebaar**, want die zit in de image. Dat was de enige echte uitzondering op "alles komt al aan bij een update"; zie [PLAN.md](PLAN.md) §1 -## Daarna - Evolu Relay +## Fase 5 - Evolu Relay -- [ ] **Hetzelfde voor de agent en de pagina van Evolu Relay**, die nog als `*.template` op - `python:3-alpine` en `nginx:alpine` draaien; zie [PLAN.md](PLAN.md) §8. Pas na fase 4 hierboven: - eerst zien dat de weg werkt voordat de tweede app hem gaat +- [x] **Beslist (08-09-2026, gebruiker): één image met de relay erbij**, geen tweede recept. Basis blijft + `node:24-slim` om `better-sqlite3`; nginx en python3 komen uit apt +- [x] **Agent, nginx-config en pagina naar `tools/evolu-relay/`** met `git mv`, zonder `.template`. + Dockerfile en build.sh herschreven; bouwcontext is de repo-root, zoals bij Gate. `VERSION` 0.6.0, + manifest 0.6.0, drie keer dezelfde tag in de compose, ongepind tot de eerste push +- [x] **Wat er anders is dan verplaatst:** `user www-data;` in nginx.conf (Debian heeft geen gebruiker + nginx), `user: node` op de relay-service in de compose (de image heeft geen `USER` meer), de versie + via `api/status` met `RELAY_APP_VERSION`, en `PYTHONUNBUFFERED` als `ENV` in de image +- [x] Tests mee: `test_relay_agent.py` laadt `tools/evolu-relay/agent.py`; de vormtest toetst de drie + tags tegen `VERSION`; de paginatest en `voorbeeldpagina.mjs` vinden de pagina zelf +- [ ] **Bouwen, duwen, pinnen, updaten, nakijken.** Van de gebruiker, op de Umbrel: + `sh tools/evolu-relay/build.sh`, dan de digest **drie keer** in de compose. Nakijken: de pagina met + v0.6.0 in de kop, een sync vanuit Trezor Suite, en of `owners.json` en de labels de update overleefd + hebben (die staan onder `data/relay` en zijn niet aangeraakt). **Eerste keer voor deze image met + apt erin**, dus reken op een ronde: of `nginx` uit Debian zonder `www-data`-verrassingen start, en of + de relay als `node` nog in `data/relay` kan schrijven nu de map niet meer door de image aangemaakt + wordt ## Geblokkeerd / wacht op -Niets. Het Relay-deel kan beginnen; bouwen blijft werk van de gebruiker op de Umbrel. +De laatste stap van fase 5 wacht op de gebruiker: bouwen kan alleen op de Umbrel. diff --git a/Docs/Plannen/Actief/008-Umbrelapp/TAKEN.md b/Docs/Plannen/Actief/008-Umbrelapp/TAKEN.md index 1022315..81c5e5b 100644 --- a/Docs/Plannen/Actief/008-Umbrelapp/TAKEN.md +++ b/Docs/Plannen/Actief/008-Umbrelapp/TAKEN.md @@ -262,10 +262,14 @@ tonen, maar drie klassen fouten hoeven niet op het apparaat gevonden te worden: - [x] **De Postgres-pin is vervallen (28-08-2026):** die database zit niet meer in de app - [x] **De statuspagina verbeteren, op een lijst van de gebruiker.** De lijst kwam op 30-08-2026 en is in één ronde uitgevoerd; zie fase 6 hieronder -- [ ] **`python:3-alpine` en `nginx:alpine` pinnen op hun multi-arch index-digest. WACHT OP het plan - Eigenimage; doe dit niet eerder.** Op aanwijzing van de gebruiker (30-08-2026): die twee images zijn - de plek waar de agent en de pagina van deze app uitgevoerd worden, en dat is precies wat **Eigenimage** - naar een eigen image verhuist. Pin je ze nu, dan pin je iets dat verdwijnt. +- [x] **`python:3-alpine` en `nginx:alpine` pinnen: vervallen (08-09-2026).** Sinds 0.6.0 zitten de agent + en de pagina in de eigen relay-image (plan **Eigenimage**, fase 5), en die twee images staan niet + meer in de compose. Wat er te pinnen is, is de eigen image, en dat gebeurt bij elke verhoging. De + oude tekst hieronder blijft staan als uitleg waarom er tot dan bewust níet gepind is. + + Op aanwijzing van de gebruiker (30-08-2026): die twee images waren + de plek waar de agent en de pagina van deze app uitgevoerd werden, en dat is precies wat **Eigenimage** + naar een eigen image verhuisde. Pin je ze dan, dan pin je iets dat verdwijnt. De eis komt uit **Publicatie-Relay** en die is nog ver weg, dus er is geen haast. De regel en de hele keten erachter staan in [KNOWLEDGE.md](../../../KNOWLEDGE.md); de commando's in diff --git a/README.md b/README.md index 8246f88..56ab8ea 100644 --- a/README.md +++ b/README.md @@ -39,32 +39,25 @@ trade-off is written out in [Docs/Referenties/Clients.md](Docs/Referenties/Clien ### Evolu Relay -> **Packaged, never installed.** The manifest and compose are here; nothing has run on an Umbrel yet. - Trezor Suite syncs labels and account names between devices, and by default that runs through a server -operated by Trezor. That server is open source and called Evolu Relay. This app runs it on your own -machine. The data is end to end encrypted on the device, so self-hosting does not change that guarantee, -it only changes who holds the encrypted copy. +operated by Trezor. That server is built on Evolu, and its relay is published as an npm package. This app +runs that relay on your own machine, with an owner allowlist around it. The data is end to end encrypted +on the device, so self-hosting does not change that guarantee, it only changes who holds the encrypted +copy. -Three containers, two images. +Three containers, one image, built from [tools/evolu-relay/](tools/evolu-relay/) by its `build.sh` +(node on Debian slim, plus nginx and python3). The app folder holds only the compose file, the manifest, +the icon and your data. | Container | What it does | |-|-| -| `relay` | the sync relay on 4000, reached through the app proxy on 3851 | -| `quota-manager` | same image, different command; registers the storage limit the relay requires | -| `db` (`postgres:17-alpine`) | storage, under `${APP_DATA_DIR}/data/postgres` | +| `relay` | the sync relay, published on host port 3852; our `src/index.js` around `@evolu/nodejs`, adding the allowlist | +| `agent` | same image, different command; serves the status API and drops commands from the page into a mailbox the relay empties | +| `server` | same image; nginx serving the status page on port 80 behind the umbrelOS app proxy, with its sign-in | -The app proxy runs with `PROXY_AUTH_ADD: "false"`, because Trezor Suite is not a browser with a session -cookie. That is the same pattern Umbrel's own nostr-relay app uses. The flip side: anything that can reach -port 3851 reaches the relay without signing in. What limits the damage is that the relay refuses any owner -without a storage limit registered in its database. - -**Trezor publishes no image**, so it is built from their Dockerfile, pinned to a commit, by -[tools/evolu-relay/build.sh](tools/evolu-relay/build.sh). Run that before installing. - -Still unverified: whether Trezor Suite accepts this address, whether the database schema creates itself, -and how an owner gets registered. See -[Docs/Referenties/Upstream-evolu-relay.md](Docs/Referenties/Upstream-evolu-relay.md). +The relay publishes its own port because a sync client is not a browser with a session cookie; the page +sits behind the app proxy because it is. New owners are admitted only while a two-minute learning window +is open on the page; everyone else is refused and listed, so you can allow them by hand. ## Documentatie diff --git a/tests/test_relay_agent.py b/tests/test_relay_agent.py index 154666d..df5495d 100644 --- a/tests/test_relay_agent.py +++ b/tests/test_relay_agent.py @@ -22,16 +22,16 @@ Draaien: python tests/test_relay_agent.py -De test laadt `agent.py.template` rechtstreeks. Dat kan omdat dat bestand geen -accolade-variabelen bevat en de invulling door umbreld hem dus onveranderd laat; -de eerste toets hieronder controleert precies dat. +De test laadt `tools/evolu-relay/agent.py` rechtstreeks: het bestand dat de +Dockerfile in de image zet. Tot 0.6.0 heette dat `agent.py.template` en stond het +in de app-map; de eerste toets hieronder is uit die tijd en blijft staan. """ import sys -# Vóór de imports, want anders is het te laat: Python legt bytecode naast -# agent.py.template zodra die geïmporteerd wordt, en die rommel hoort niet in de -# app-map. Een keer is zo'n .pyc meegegaan in een commit. +# Vóór de imports, want anders is het te laat: Python legt bytecode naast agent.py +# zodra die geïmporteerd wordt, en die rommel hoort niet in de repo. Een keer is +# zo'n .pyc meegegaan in een commit. sys.dont_write_bytecode = True import importlib.machinery # noqa: E402 @@ -42,20 +42,18 @@ import tempfile # noqa: E402 from datetime import datetime, timedelta, timezone # noqa: E402 HERE = os.path.dirname(os.path.abspath(__file__)) -APP = os.path.join(HERE, os.pardir, "whatsnext-evolu-relay") -TEMPLATE = os.path.join(APP, "agent.py.template") +AGENT = os.path.join(HERE, os.pardir, "tools", "evolu-relay", "agent.py") def load_agent(state_dir): - """Laadt agent.py.template als module, met zijn staat in een tijdelijke map. + """Laadt agent.py als module, met zijn staat in een tijdelijke map. De agent leest `RELAY_STATE_DIR` op moduleniveau, dus die moet vóór het laden - in de omgeving staan. Met een expliciete loader, want importlib kijkt normaal - naar de extensie en .template staat daar niet tussen. + in de omgeving staan. Met een expliciete loader, buiten sys.path om. """ os.environ["RELAY_STATE_DIR"] = state_dir - loader = importlib.machinery.SourceFileLoader("relay_agent", TEMPLATE) - spec = importlib.util.spec_from_file_location("relay_agent", TEMPLATE, loader=loader) + loader = importlib.machinery.SourceFileLoader("relay_agent", AGENT) + spec = importlib.util.spec_from_file_location("relay_agent", AGENT, loader=loader) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module @@ -94,11 +92,17 @@ def schrijf_owners(state_dir, staat): # ── De aanname waar deze hele test op rust ─────────────────────────────────── def test_template_is_invulbaar_zonder_schade(u): - with open(TEMPLATE, "r", encoding="utf-8") as f: + """Een restant uit de template-tijd dat blijft staan. + + Het bestand zit sinds 0.6.0 in de image en gaat door geen envsubst meer. Maar + als iemand het ooit weer als template in een app-map zet, is dit de toets die + het meteen ziet. + """ + with open(AGENT, "r", encoding="utf-8") as f: inhoud = f.read() - u.check("template bevat geen dollartekens", + u.check("agent.py bevat geen dollartekens", "$" not in inhoud, - "umbreld zou die invullen en de Python-code slopen") + "een envsubst-stap zou die invullen en de Python-code slopen") # ── Het tijdvenster ───────────────────────────────────────────────────────── diff --git a/tools/evolu-relay/Dockerfile b/tools/evolu-relay/Dockerfile index 753e7d8..8eeffa4 100644 --- a/tools/evolu-relay/Dockerfile +++ b/tools/evolu-relay/Dockerfile @@ -1,6 +1,14 @@ # ═══════════════════════════════════════════════════════════════════════════════ # De image voor de app whatsnext-evolu-relay. # +# Eén image voor drie containers. De compose start hem drie keer: als `relay` +# met het standaardcommando (`node src/index.js`, als gebruiker node), als +# `agent` met `python3 /app/agent.py` en als `server` met nginx. Tot 0.5.6 zat +# alleen de relay in deze image en draaiden de agent en de pagina als *.template +# op python:3-alpine en nginx:alpine; sinds 0.6.0 zit alles hier. Beslist door de +# gebruiker op 08-09-2026, na dezelfde stap bij Electrum Gate: één image scheelt +# een tweede recept en een tweede pin. Zie het plan Eigenimage, PLAN.md §8. +# # Dit bouwt niet de relay van Evolu na: het installeert `@evolu/nodejs` uit npm # en start ons eigen `src/index.js`, dat alleen de twee terugroepfuncties voor de # toegangscontrole toevoegt. @@ -8,28 +16,52 @@ # Waarom `slim` en niet `alpine`: `better-sqlite3` zit onder `@evolu/nodejs` en # heeft kant-en-klare binaries voor glibc, niet voor musl. Op alpine zou hij bij # elke bouw opnieuw gecompileerd moeten worden, met een bouwketen erbij in de -# image. +# image. Dat is ook de reden dat nginx en python3 hier uit apt komen en niet +# andersom node in een nginx-image gezet is. +# +# De bouwcontext is de REPO-ROOT, niet deze map: icon.png blijft in de app-map +# staan (het manifest wijst ernaar voor de tegel in de winkel) en wordt van daar +# gekopieerd, zodat er niet twee exemplaren zijn. build.sh geeft die context mee. # ═══════════════════════════════════════════════════════════════════════════════ FROM node:24-slim +# nginx en python3 uit Debian. Geen aanbevolen pakketten: die halen bij nginx +# onder meer een set modules binnen die de pagina niet gebruikt. De agent heeft +# alleen de standaardbibliotheek nodig. +RUN apt-get update \ + && apt-get install -y --no-install-recommends nginx python3 \ + && rm -rf /var/lib/apt/lists/* + WORKDIR /app # Eerst alleen het manifest, zodat een wijziging in `src/` de installatielaag niet # ongeldig maakt. -COPY package.json ./ +COPY tools/evolu-relay/package.json ./ RUN npm install --omit=dev --no-audit --no-fund -COPY src ./src +COPY tools/evolu-relay/src ./src +COPY tools/evolu-relay/agent.py ./agent.py # De relay maakt zelf `data/` aan bij de start, maar dan als de gebruiker die op # dat moment draait. Vooraf aanmaken met de juiste eigenaar voorkomt dat een -# gemounte map van de host als root wordt aangeraakt. -RUN mkdir -p /app/data && chown -R node:node /app +# gemounte map van de host als root wordt aangeraakt. Alleen `data/` en `src/` zijn +# van node; agent.py en de rest blijven van root, want die worden alleen gelezen. +RUN mkdir -p /app/data && chown -R node:node /app/data /app/src -USER node +# De pagina. Debian zet nginx' eigen voorbeeldpagina onder /var/www/html; wij +# gebruiken het pad dat de nginx-image ook gebruikt, zodat nginx.conf gelijk kan +# blijven aan die van Electrum Gate. +COPY tools/evolu-relay/nginx.conf /etc/nginx/nginx.conf +COPY tools/evolu-relay/index.html /usr/share/nginx/html/index.html +COPY whatsnext-evolu-relay/icon.png /usr/share/nginx/html/icon.png + +# Geen USER hier, en dat is met opzet: nginx moet als root beginnen om poort 80 te +# nemen, en de agent schrijft in de gedeelde map. De relay hoort wél als node te +# draaien, zoals hij tot 0.5.6 deed, en dat regelt de compose met `user: node`. ENV NODE_ENV=production -EXPOSE 4000 +ENV PYTHONUNBUFFERED=1 +EXPOSE 4000 80 CMD ["node", "src/index.js"] diff --git a/whatsnext-evolu-relay/agent.py.template b/tools/evolu-relay/agent.py similarity index 95% rename from whatsnext-evolu-relay/agent.py.template rename to tools/evolu-relay/agent.py index 5eaa56c..64e8f97 100644 --- a/whatsnext-evolu-relay/agent.py.template +++ b/tools/evolu-relay/agent.py @@ -10,10 +10,12 @@ # dan niet kunt reproduceren. De agent schrijft daarom uitsluitend command.json, # en het relay-proces past hem toe en ruimt hem op. # -# LET OP: umbreld haalt dit bestand bij elke start door envsubst. Er mag dus geen -# dollarteken in staan, ook niet in een regex of een tekst. Een accolade-variabele -# die niet bestaat wordt leeg, en dat sloopt Python-code zonder foutmelding. -# tests/test_appstore_vorm.py controleert dat. +# Dit bestand zit in de image (tools/evolu-relay/Dockerfile) als /app/agent.py. +# Tot 0.5.6 was het een *.template in de app-map en haalde umbreld het bij elke +# start door envsubst, en daarom staat er geen dollarteken in en komen alle +# instellingen uit de omgeving. Dat laatste is zo gebleven: de compose zet ze in +# de omgeving neer, en dat is ook de nette weg. tests/test_relay_agent.py houdt +# het dollarteken buiten de deur, voor het geval dit ooit weer een template wordt. # ═══════════════════════════════════════════════════════════════════════════════ import json @@ -48,6 +50,12 @@ PUBLIC_PORT = int(os.environ.get("RELAY_PUBLIC_PORT", "3852")) API_PORT = int(os.environ.get("RELAY_API_PORT", "8000")) PROBE_INTERVAL = int(os.environ.get("RELAY_PROBE_INTERVAL", "15")) +# De versie uit het manifest, voor de kop van de pagina. Tot 0.5.6 vulde umbreld +# die rechtstreeks in de pagina in; nu de pagina in de image zit, loopt het via de +# status. Leeg betekent dat de compose hem niet doorgeeft, en dan toont de pagina +# geen versie in plaats van een verzonnen. +APP_VERSION = os.environ.get("RELAY_APP_VERSION", "") + # Wat de pagina mag vragen. Expliciet en niet doorgeven wat er binnenkomt: dit # bestand wordt door een ander proces uitgevoerd, en een onbekende actie hoort # hier te stranden en niet daar. @@ -260,6 +268,7 @@ def build_status(): learning = learning_facts(state) return { + "version": APP_VERSION or None, "relay": { "reachable": probe["reachable"], "checked": probe["checked"], diff --git a/tools/evolu-relay/build.sh b/tools/evolu-relay/build.sh index 2d2f2d0..8858b96 100644 --- a/tools/evolu-relay/build.sh +++ b/tools/evolu-relay/build.sh @@ -2,9 +2,10 @@ # ═══════════════════════════════════════════════════════════════════════════════ # Bouwt de image voor de app whatsnext-evolu-relay. # -# Wat er gebouwd wordt is ONS programma: `src/index.js` roept `createRelay` uit -# `@evolu/nodejs` aan met onze eigen toegangscontrole erin. De relay zelf komt -# gewoon uit npm en wordt niet nagebouwd of aangepast. +# Eén image voor de drie containers van de app: de relay (ons `src/index.js` om +# `@evolu/nodejs` heen), de agent (`agent.py`) en nginx met de pagina. De relay +# zelf komt uit npm en wordt niet nagebouwd of aangepast. Tot 0.5.6 zat alleen de +# relay in deze image; sinds 0.6.0 alles, zie de Dockerfile. # # Dit verving op 28-08-2026 het vorige recept, dat de repo van Trezor kloonde en # hun Dockerfile bouwde. Dat pakket had een Postgres en een quota-manager nodig en @@ -17,8 +18,13 @@ # bovendien minuten laten hangen. Het recept hoort dus in de repo en het resultaat # in een register; de app verwijst alleen naar de tag. # -# Draaien: sh build.sh -# Vereist: docker op de machine waar je bouwt. Git is niet meer nodig. +# Draaien: sh tools/evolu-relay/build.sh +# Vereist: docker op de machine waar je bouwt. Op de Umbrel is dat met sudo. +# +# Multi-arch: PLATFORMS="linux/amd64,linux/arm64" sh tools/evolu-relay/build.sh +# Vraagt buildx met QEMU en duwt meteen. Nog nooit geprobeerd; zie het masterplan +# Publicatie-Relay. Zonder PLATFORMS bouwt dit één architectuur, die van de +# machine waarop je staat. # ═══════════════════════════════════════════════════════════════════════════════ set -eu @@ -28,15 +34,15 @@ set -eu # zijn dáár de pin. Hier staat alleen het etiket op de uitkomst. # # Dit is het etiket op de IMAGE. Verhoog het als de image verandert, dus als er -# iets in src/, package.json of de Dockerfile wijzigt, en zet dan dezelfde waarde -# achter `image:` in de compose. +# iets in deze map of in icon.png wijzigt, en zet dan dezelfde waarde achter +# `image:` in de compose (drie keer: relay, agent en server delen hem). # # `version` in umbrel-app.yml is een ander nummer en mag hierop vooruitlopen: dat -# moet bij élke wijziging aan de app omhoog, ook als alleen een template -# verandert, want anders rolt umbrelOS hem niet uit. Dat heeft hier een keer een -# dag gekost. Ze zijn dus gelijk zolang alleen de image wijzigt, en lopen uiteen -# zodra er een reparatie in de app-map zit. -VERSION="0.5.0" +# moet bij élke wijziging aan de app omhoog, ook als alleen de compose verandert, +# want anders rolt umbrelOS hem niet uit. Dat heeft hier een keer een dag gekost. +# Sinds alles in de image zit lopen ze meestal gelijk; de regel blijft: verhoog je +# VERSION, dan ook het manifest. +VERSION="0.6.0" # Het register staat er expres in en dit is geen smaakkwestie: umbreld haalt élke # image op via de Docker Engine API, dus een tag die alleen lokaal bestaat is voor @@ -45,12 +51,22 @@ VERSION="0.5.0" IMAGE="sc.kamenier-hamer.nl/sysop/evolu-relay:${VERSION}" RECEPT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +# De bouwcontext is de repo-root, want icon.png komt uit de app-map. Zie de kop +# van de Dockerfile. +REPO="$(CDPATH= cd -- "${RECEPT}/../.." && pwd)" # ── Controles vooraf ────────────────────────────────────────────────────────── # Beter hier hard falen dan een image bouwen die iets anders bevat dan je denkt. -if [ ! -f "${RECEPT}/package.json" ] || [ ! -f "${RECEPT}/src/index.js" ]; then - echo "FOUT: package.json of src/index.js ontbreekt in ${RECEPT}" >&2 +for bestand in Dockerfile package.json src/index.js agent.py nginx.conf index.html; do + if [ ! -f "${RECEPT}/${bestand}" ]; then + echo "FOUT: ${bestand} ontbreekt in ${RECEPT}" >&2 + exit 1 + fi +done + +if [ ! -f "${REPO}/whatsnext-evolu-relay/icon.png" ]; then + echo "FOUT: whatsnext-evolu-relay/icon.png ontbreekt in ${REPO}" >&2 exit 1 fi @@ -63,24 +79,45 @@ if [ ! -f "${RECEPT}/package-lock.json" ]; then echo fi +# Een accolade-variabele hoort hier nergens meer in: deze bestanden gaan niet +# meer door envsubst, dus wat er staat is wat er draait. Staat er tóch een, dan +# is dat een restant van de template-tijd en zou hij letterlijk in de pagina of +# de configuratie terechtkomen. +if grep -l '\${' "${RECEPT}/nginx.conf" "${RECEPT}/index.html" "${RECEPT}/agent.py" 2>/dev/null; then + echo "FOUT: de bestanden hierboven bevatten nog een \${...}; die wordt niet meer ingevuld." >&2 + exit 1 +fi + # ── Bouwen ──────────────────────────────────────────────────────────────────── -echo "Bouwen als ${IMAGE}" -docker build --tag "$IMAGE" "$RECEPT" +PLATFORMS="${PLATFORMS:-}" + +if [ -n "$PLATFORMS" ]; then + echo "Bouwen als ${IMAGE} voor ${PLATFORMS}, en meteen duwen" + docker buildx build --platform "$PLATFORMS" --tag "$IMAGE" --push \ + --file "${RECEPT}/Dockerfile" "$REPO" + echo + echo "Geduwd. De index-digest:" + docker buildx imagetools inspect "$IMAGE" | head -3 +else + echo "Bouwen als ${IMAGE}" + docker build --tag "$IMAGE" --file "${RECEPT}/Dockerfile" "$REPO" + echo + echo "Klaar:" + docker image inspect --format '{{.RepoTags}} {{.Id}}' "$IMAGE" + echo + echo "Bouwen is niet genoeg: de app verwijst naar het register, want umbreld kan" + echo "niet bij een image die alleen lokaal staat. Nog te doen:" + echo + echo " docker login sc.kamenier-hamer.nl" + echo " docker push ${IMAGE}" +fi -echo -echo "Klaar:" -docker image inspect --format '{{.RepoTags}} {{.Id}}' "$IMAGE" -echo -echo "Bouwen is niet genoeg: de app verwijst naar het register, want umbreld kan" -echo "niet bij een image die alleen lokaal staat. Nog te doen:" -echo -echo " docker login sc.kamenier-hamer.nl" -echo " docker push ${IMAGE}" echo echo "Zet daarna de digest uit de push-uitvoer achter de tag in" -echo "whatsnext-evolu-relay/docker-compose.yml, en zet \`version\` in het manifest" -echo "op ${VERSION}. Zonder die verhoging rolt umbrelOS het niet uit." +echo "whatsnext-evolu-relay/docker-compose.yml (drie keer: relay, agent en server)," +echo "en zet \`version\` in het manifest op ${VERSION}. Zonder die verhoging rolt" +echo "umbrelOS het niet uit." echo echo "Controleer dat het anoniem te halen is, want umbreld krijgt geen" echo "inloggegevens mee. Log uit in dezelfde context waarin je inlogde, anders" diff --git a/whatsnext-evolu-relay/index.html.template b/tools/evolu-relay/index.html similarity index 98% rename from whatsnext-evolu-relay/index.html.template rename to tools/evolu-relay/index.html index 0c5229a..cea7f53 100644 --- a/whatsnext-evolu-relay/index.html.template +++ b/tools/evolu-relay/index.html @@ -6,12 +6,13 @@ Zichtbare tekst is Engels: dit is een publieke app store en dit is wat een bezoeker ziet. Commentaar is Nederlands. - LET OP, en dit is de valstrik van dit bestand: umbreld haalt elke *.template - bij het starten door envsubst. Er mag dus GEEN dollarteken in staan behalve de - umbrel-variabelen die hier werkelijk ingevuld moeten worden. Dat sluit ook - JavaScript-template-literals uit, want die gebruiken accolades achter een - dollarteken en zouden stilzwijgend leeg worden. Alles hieronder plakt strings - dus met een plus aan elkaar. tests/test_appstore_vorm.py houdt dit dicht. + Dit bestand zit in de image (tools/evolu-relay/Dockerfile) als + /usr/share/nginx/html/index.html. Tot 0.5.6 was het een *.template in de + app-map die umbreld bij het starten door envsubst haalde, en daarom plakt + alles hieronder strings met een plus aan elkaar in plaats van met een + template-literal. Die beperking is weg, maar de stijl is gebleven: één pagina + in twee stijlen is erger dan één stijl die niet meer nodig is. De versie in de + kop komt uit api/status, waar umbreld hem vroeger rechtstreeks invulde. De maatvoering komt uit Electrum Gate en niet uit een eigen ontwerp: dezelfde breedte, hetzelfde raster, dezelfde kop met een icoon van 64 pixels, hetzelfde @@ -640,7 +641,7 @@ body {

Evolu Relay

-

Encrypted sync and backup for your local-first apps v${APP_VERSION}

+

Encrypted sync and backup for your local-first apps