Het pakket omgebouwd: kale relay, statuspagina, poorten omgedraaid

De app-map is niet langer de relay van Trezor met een Postgres en een
quota-manager ernaast, maar de relay van Evolu met onze eigen allowlist eromheen.
Drie containers werden relay, agent en nginx; het wachtwoord en de database zijn
verdwenen.

De poorten zijn omgedraaid en dat is de kern. Tot 0.0.2 hing de relay achter
app_proxy en moest de inlog van umbrelOS dus uit, waardoor een statuspagina net zo
onbeschermd zou zijn als de relay zelf. Nu hangt de pagina daar met de inlog aan,
en publiceert de relay zijn eigen poort 3852 waar Zoraxy met TLS naartoe wijst.
Dat is hetzelfde patroon als Electrum Gate met 50022. Niet 4000 op de host, want
dat is een veelgebruikte poort en een botsing merk je pas als de app niet start.

De agent beslist niets: hij leest wat het relay-proces heeft opgeschreven en legt
opdrachten in een postbus die de relay zelf leegmaakt. Twee processen die in
dezelfde allowlist schrijven is een wedloop die je een keer per jaar treft en dan
niet kunt reproduceren. Hij weigert ook een tweede opdracht zolang de vorige er
nog ligt, want overschrijven zou er stil een laten verdwijnen.

De pagina volgt het ontwerpsysteem van Electrum Gate, zonder de Google
Fonts-verwijzing daaruit: een app op een Umbrel hoort niet te wachten op een
lettertype van buiten. Alles is met stringoptelling geschreven en zonder enig
dollarteken, want umbreld haalt elke template door envsubst en zou een
JavaScript-template-literal stilzwijgend leegmaken.

De image is door de gebruiker gebouwd en geduwd; de compose is gepind op tag plus
digest. Manifest naar 0.2.0, met een beschrijving en releaseNotes die kloppen met
wat er nu draait in plaats van met het vorige pakket.

Wat hier NIET mee bewezen is, en dat is meer dan gebruikelijk: de agent is alleen
op syntaxis gecontroleerd, de pagina is nooit gerenderd, en of een opdracht van de
pagina daadwerkelijk bij het relay-proces aankomt is niet gemeten. Dat kan alleen
op het apparaat en staat als taak.

Tests: alle vier groen (32, 54, 39 en 60 goed, 0 fout).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Harmen
2026-08-28 11:22:32 +02:00
co-authored by Claude Opus 5
parent a738f6c0f1
commit 576acd20dd
8 changed files with 963 additions and 147 deletions
+111 -117
View File
@@ -1,135 +1,129 @@
# ═══════════════════════════════════════════════════════════════════════════════
# Evolu Relay - de sync-server van Trezor Suite op je eigen Umbrel.
# Evolu Relay - je eigen sync-server voor de labels van Trezor Suite.
#
# Drie containers, twee images. De relay en de quota-manager komen uit dezélfde
# image met een ander `command`: bovenstrooms is het één codebase met meerdere
# startscripts. Dat is nagetrokken in de Dockerfile en de README van Trezor; zie
# Docs/Referenties/Upstream-evolu-relay.md §2.
# Drie containers: de relay, een agent voor de statuspagina, en nginx die die
# pagina serveert. Geen Postgres en geen quota-manager meer; die hoorden bij de
# relay van Trezor, en die is als zelf-gehoste relay in de kern onbruikbaar omdat
# de cliënt bij een eigen relay-URL nooit een eigenaar registreert. Zie het plan
# Umbrelapp, OPEN.md punt 6, en de proef in PLAN.md §6a.
#
# De poortindeling is omgedraaid ten opzichte van 25-08-2026, en dat is de kern
# van dit bestand:
#
# - de PAGINA hangt achter app_proxy, mét de inlog van umbrelOS. Vroeger stond de
# relay daar en moest de inlog dus uit, waardoor een statuspagina net zo
# onbeschermd zou zijn als de relay zelf;
# - de RELAY publiceert zijn eigen poort, waar Zoraxy met TLS naartoe wijst.
# Trezor Suite is geen browser met een sessiecookie en zou achter de inlog een
# inlogpagina krijgen in plaats van de relay.
#
# Dat is hetzelfde patroon als Electrum Gate in deze store: de web-UI via de
# proxy, het protocol op een eigen poort. Zie PLAN.md §4h.
# ═══════════════════════════════════════════════════════════════════════════════
services:
# umbrelOS genereert deze service zelf; wij vullen alleen in waar hij heen moet
# wijzen. De hostnaam heeft de vorm <app-id>_<compose-service>_1.
#
# PROXY_AUTH_ADD staat hier bewust NIET op "false". Dat stond er tot 0.0.2 wel,
# toen de relay hierachter hing. Nu wijst dit naar de statuspagina, en die hoort
# juist achter de inlog. Zet het niet terug zonder §4h te lezen.
app_proxy:
environment:
APP_HOST: whatsnext-evolu-relay_relay_1
APP_PORT: 4000
# Trezor Suite is geen browser met een sessiecookie. Achter de inlog van
# umbrelOS zou het een inlogpagina krijgen in plaats van de relay, en dat
# is niet op te lossen aan onze kant.
#
# Dit is geen omweg: het is het patroon dat de eigen nostr-relay-app van
# Umbrel gebruikt, om precies dezelfde reden. Ook Gitea en Budibase in de
# officiele store doen het zo.
#
# De prijs staat er wel bij: wie deze poort kan bereiken, bereikt de relay
# zonder aanmelding. Wat de schade beperkt is dat de relay zelf elke
# eigenaar weigert die geen limietenrij in de database heeft. Zet deze app
# dus niet zonder meer open naar het internet; zie het masterplan
# Bereikbaarheid.
#
# En let op de tweede weg naar binnen, die je niet ziet in deze compose:
# umbrelOS maakt per app een Tor hidden service. Zonder inlog betekent dat
# bereikbaar vanaf het internet, alleen beschermd doordat het .onion-adres
# onraadbaar is. Bij Electrum Gate is dat geen bezwaar, want daar zit de
# inlog van umbrelOS ervoor; hier is die er juist uit. Zet de hidden service
# dus uit in umbrelOS tenzij je hem bewust wilt. Op 25-08-2026 stond hij bij
# de eerste installatie gewoon aan.
PROXY_AUTH_ADD: "false"
APP_HOST: whatsnext-evolu-relay_server_1
APP_PORT: 80
relay:
# Gebouwd uit de broncode van Trezor door tools/evolu-relay/build.sh, en
# geduwd naar het Gitea-register op dezelfde server als deze store. Dat moet:
# umbreld haalt élke image op via de Docker Engine API (docker-modem) en niet
# via compose, dus een lokaal gebouwde tag is voor hem onbereikbaar en
# `pull_policy` wordt nooit gelezen. Op 25-08-2026 op het apparaat
# vastgesteld; zie Docs/Referenties/Umbrel-appstore-spec.md.
# Gebouwd uit tools/evolu-relay/ door build.sh en geduwd naar het
# Gitea-register op dezelfde server als deze store. Dat moet: umbreld haalt
# élke image op via de Docker Engine API en niet via compose, dus een lokaal
# gebouwde tag is voor hem onbereikbaar en de installatie faalt met
# "pull access denied". Op 25-08-2026 op het apparaat vastgesteld.
#
# De digest hoort erbij en niet alleen de tag: een tag kan opnieuw geduwd
# worden en dan draait er iets anders dan hier staat.
# worden en dan draait er iets anders dan hier staat. Staan ze allebei, dan
# bepaalt de digest wat er gehaald wordt en is de tag alleen leesbaarheid.
# Houd de tag gelijk aan `version` in het manifest en aan VERSION in build.sh.
#
# Let op: dit is de digest van één architectuur (amd64), want er is alleen
# amd64 geduwd. Voor de officiele store moet het een multi-arch index-digest
# zijn met arm64 erin; zie het masterplan Publicatie-Relay.
image: sc.kamenier-hamer.nl/sysop/evolu-relay:c03a204@sha256:2fe1e9e90e5cbc12d5ea6363df03bb132fddec0150283063b9103a09bdbe853a
# Geduwd op 28-08-2026. Let op: dit is één architectuur (amd64), want er is
# alleen amd64 gebouwd. Voor de officiele store hoort er een multi-arch
# index-digest met arm64 in; zie het masterplan Publicatie-Relay.
image: sc.kamenier-hamer.nl/sysop/evolu-relay:0.2.0@sha256:5b2b4228c24a7e8a12a99869ca39729f47a0a01d6a346c623b22d6e62f2918c2
restart: on-failure
depends_on:
db:
condition: service_healthy
# Bovenstrooms staat `CMD ["yarn", "start"]` in de Dockerfile, met daarbij een
# eigen commentaar dat het misschien een van de twee specifieke scripts had
# moeten zijn. Daarom hier expliciet, en niet vertrouwen op de standaard.
command: ["yarn", "start-evolu-relay"]
ports:
# De relay zelf. Dit is de poort waar Zoraxy met TLS naartoe wijst, en de
# enige die deze app publiceert; de pagina loopt via app_proxy.
#
# 3852 op de host en niet 4000: dat laatste is een veelgebruikte poort en
# een botsing merk je pas als de app niet start. Dezelfde les als bij
# Electrum Gate, dat om die reden 50022 gebruikt in plaats van 50002.
# Binnen de container blijft het 4000, want dat is wat de relay verwacht.
- "3852:4000"
environment:
RELAY_PORT: "4000"
HEALTH_PORT: "4002"
# dev of prod. Volgens .env.sample zet prod authenticatie aan; in de code
# bepaalt die vlag alleen het logniveau en staan de autorisatiecontroles
# onvoorwaardelijk aan. Een van de twee is achterhaald en dat is nog niet
# uitgezocht. Hier staat prod, want dat is de veilige kant van die
# onduidelijkheid: als het iets aanzet, willen we dat het aanstaat.
SERVER_ENV: prod
POSTGRES_GATE_HOST: whatsnext-evolu-relay_db_1
POSTGRES_GATE_PORT: "5432"
POSTGRES_GATE_USER: suite-sync
POSTGRES_GATE_DB: suite-sync-gate
# Door umbrelOS per installatie afgeleid. Nooit een letterlijke waarde:
# deze repo is publiek.
POSTGRES_GATE_PASSWORD: ${APP_PASSWORD}
POSTGRES_GATE_SSL: "false"
quota-manager:
# Zelfde image als relay, ander commando. Tag en digest moeten dus gelijk
# blijven aan die hierboven.
image: sc.kamenier-hamer.nl/sysop/evolu-relay:c03a204@sha256:2fe1e9e90e5cbc12d5ea6363df03bb132fddec0150283063b9103a09bdbe853a
restart: on-failure
depends_on:
db:
condition: service_healthy
command: ["yarn", "start-quota-manager"]
# Waarom deze container er is, terwijl hij bij Trezor bij hun betaalde
# hosting hoort: de relay weigert elke eigenaar zonder rij in de
# limietentabel, en dit is wat die rijen aanmaakt. Er is geen HTTP-koppeling
# tussen de twee; ze delen alleen de database. Zie Upstream-evolu-relay.md §3.
environment:
QUOTA_MANAGER_PORT: "4001"
HEALTH_PORT: "4012"
SERVER_ENV: prod
POSTGRES_GATE_HOST: whatsnext-evolu-relay_db_1
POSTGRES_GATE_PORT: "5432"
POSTGRES_GATE_USER: suite-sync
POSTGRES_GATE_DB: suite-sync-gate
POSTGRES_GATE_PASSWORD: ${APP_PASSWORD}
POSTGRES_GATE_SSL: "false"
db:
# TODO pinnen op de multi-arch index-digest, met
# `docker buildx imagetools inspect postgres:17-alpine`. Kan alleen op een
# machine met Docker; staat als taak in het plan Umbrelapp.
#
# Niet `postgres` kaal zoals de compose van Trezor doet: dat is `latest` en
# dus niet reproduceerbaar, en een grote-versiesprong van Postgres migreert
# zijn datamap niet vanzelf. Dan start de database niet meer en is de data
# alleen met handwerk terug te halen.
image: postgres:17-alpine
restart: on-failure
environment:
POSTGRES_USER: suite-sync
POSTGRES_DB: suite-sync-gate
POSTGRES_PASSWORD: ${APP_PASSWORD}
# Een submap en niet de wortel van de mount. Postgres weigert een datamap
# die al iets anders bevat, en een mount heeft op sommige bestandssystemen
# een lost+found.
PGDATA: /var/lib/postgresql/data/pgdata
# Per schrijfactie en niet per eigenaar; zo is isOwnerWithinQuota bij Evolu
# bedoeld. Eén labelwijziging is klein, dus een schrijfactie hierboven is
# eerder een fout of misbruik dan normaal gebruik. Zie PLAN.md §4g.
RELAY_MAX_WRITE_BYTES: "1048576"
volumes:
# De database van de relay én onze allowlist ernaast, in dezelfde map. Het
# programma doet chdir naar `data`, net als de relay van Evolu zelf, dus dit
# pad ligt vast.
#
# Onder data/ en niet in een naamloos Docker-volume: dit is wat umbrelOS
# bewaart en meeneemt in de back-up. In een volume zou de database een
# bewaart en meeneemt in de back-up. In een volume zou de synchronisatie een
# herinstallatie van de app niet overleven.
- ${APP_DATA_DIR}/data/postgres:/var/lib/postgresql/data
healthcheck:
# Op de eigen database en gebruiker, niet op `-d postgres` zoals
# bovenstrooms: die database bestaat hier niet en dan is de controle groen
# terwijl het verkeerde antwoord gegeven wordt.
test: ["CMD-SHELL", "pg_isready -U suite-sync -d suite-sync-gate"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
- ${APP_DATA_DIR}/data/relay:/app/data
# De agent bedient de statuspagina. Hij beslist niets: hij leest wat het
# relay-proces heeft opgeschreven en legt opdrachten in een postbus die de relay
# zelf leegmaakt. Twee processen die in dezelfde allowlist schrijven is een
# wedloop die je een keer per jaar treft en dan niet kunt reproduceren.
agent:
# TODO pinnen op de multi-arch index-digest, met
# `docker buildx imagetools inspect python:3-alpine`. Geldt ook voor nginx
# hieronder; staat als taak in het plan Umbrelapp.
image: python:3-alpine
restart: on-failure
environment:
RELAY_STATE_DIR: /var/lib/relay
# De volledige containernaam en niet de servicenaam 'relay'. Bij Electrum
# Gate gaf de korte naam op het apparaat een geweigerde verbinding: de naam
# loste wél op, maar naar een container van een andere app op hetzelfde
# netwerk. De vorm <app-id>_<service>_1 is uniek per app.
RELAY_HOST: whatsnext-evolu-relay_relay_1
RELAY_PORT: "4000"
# Alleen om op de pagina het adres te tonen dat je in Trezor Suite invult.
# Moet gelijk zijn aan de hostpoort hierboven.
RELAY_PUBLIC_PORT: "3852"
RELAY_API_PORT: "8000"
PYTHONUNBUFFERED: "1"
volumes:
# Door umbrelOS ingevuld uit agent.py.template bij het starten. Er staat
# geen accolade-variabele in dat bestand, dus de invulling laat de
# Python-code ongemoeid; de agent leest zijn instellingen uit de omgeving
# hierboven. De test in tests/ controleert die aanname.
- ${APP_DATA_DIR}/agent.py:/app/agent.py:ro
# Dezelfde map als de relay. De agent moet hier kunnen schrijven, want de
# opdrachten van de pagina landen hier.
- ${APP_DATA_DIR}/data/relay:/var/lib/relay
command:
- python
- /app/agent.py
server:
image: nginx:alpine
restart: on-failure
# De agent moet er zijn voordat nginx start: de proxy_pass naar
# http://agent:8000 wordt bij het starten opgelost en een onbekende naam laat
# nginx afbreken.
depends_on:
- agent
volumes:
# Beide door umbrelOS ingevuld uit een .template bij het starten.
- ${APP_DATA_DIR}/nginx.conf:/etc/nginx/nginx.conf:ro
- ${APP_DATA_DIR}/index.html:/usr/share/nginx/html/index.html:ro
# Het app-icoon, voor de kop van de pagina en het tabblad. Geen template,
# dus dit bestand komt alleen bij een installatie mee en niet bij een
# update; voor een plaatje dat vrijwel nooit wijzigt is dat goed genoeg.
- ${APP_DATA_DIR}/icon.png:/usr/share/nginx/html/icon.png:ro