Het relay-programma: de relay van Evolu met onze eigen allowlist

tools/evolu-relay/src/ bevat nu een eigen programma dat createRelay uit
@evolu/nodejs aanroept met de twee terugroepfuncties die het bedoelde
uitbreidpunt vormen. De relay zelf komt uit npm en wordt niet nagebouwd of
aangepast; de opstartvolgorde is overgenomen uit apps/relay/src/index.ts van
Evolu zelf.

De opzet is drie bestanden met een harde scheiding, en die scheiding is de reden
dat hier iets te testen valt. policy.js bevat het beleid als pure functies: geen
bestanden, geen netwerk, geen klok. store.js is de enige plek met schijf erin.
index.js doet niets anders dan lezen, doorgeven en opslaan.

Het beleid: de eerste eigenaar die zich meldt wordt geleerd, een schakelaar
bepaalt of er nog nieuwe bij mogen, en een eigenaar is te blokkeren, alsnog toe
te laten of te vergeten. Geweigerde pogingen worden onthouden voor de pagina,
afgekapt op twintig, want elke poging is een id dat de ander zelf verzint. Een
onleesbaar owners.json wordt opzij geschoven en de app gaat dan dicht in plaats
van open: we weten dan niet wie er toegelaten was, en met de leerstand aan zou de
eerstvolgende die verbindt de nieuwe eigenaar worden.

Met test: node tests/test_limiter.mjs, 60 toetsen, en de toetsen gaan over de
guards en niet over het gelukkige pad. De beslissende regel is muteertest gedaan
en de juiste toets viel om: een geblokkeerde eigenaar mag er niet alsnog in
doordat de leerstand aanstaat. Het bestand is .mjs omdat de repo-root geen
package.json heeft en een .js daar als CommonJS gelezen zou worden.

build.sh bouwt niet langer de repo van Trezor maar onze eigen Dockerfile, dus git
is er niet meer voor nodig en de pin zit nu in package.json. De image is
node:24-slim en niet alpine, want better-sqlite3 heeft binaries voor glibc en niet
voor musl. Er is nog geen package-lock.json; het script waarschuwt daarvoor en het
staat als taak.

Onderweg bleek een aanname van vanmiddag fout: de 1 MB uit de gepubliceerde image
geldt per schrijfactie en niet per eigenaar. Er valt dus geen labelgeschiedenis
tegenaan te lopen. Dat is rechtgezet in het plan en in de naslag, en het getal is
overgenomen als bewuste keuze met RELAY_MAX_WRITE_BYTES ernaast.

Wat er niet in zit en ook niet gegokt is: data per eigenaar wissen. Het beleid kan
een eigenaar vergeten, maar zijn berichten staan in de SQLite van de relay, en dat
is andermans schema.

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:12:41 +02:00
co-authored by Claude Opus 5
parent edb91c4749
commit 89aa06639b
12 changed files with 819 additions and 72 deletions
+5 -3
View File
@@ -141,9 +141,11 @@ eigen image. Wat níet terugkomt is de Postgres, het wachtwoord en het onderhoud
bouwrecept in `tools/evolu-relay/` blijft dus bestaan, maar het bouwt straks niet meer de repo van Trezor;
het bouwt een klein eigen programma tegen `@evolu/nodejs`.
**Twee dingen om niet over te slaan.** De gepubliceerde image zet `isOwnerWithinQuota` op **1 MB per
eigenaar** en laat `isOwnerAllowed` weg. Nemen wij die grens over zonder erbij na te denken, dan stopt het
synchroniseren zodra de labelgeschiedenis daar tegenaan loopt, en dat merk je pas als het gebeurt. En de
**Twee dingen om niet over te slaan.** De gepubliceerde image laat `isOwnerAllowed` weg en zet
`isOwnerWithinQuota` op 1 MB **per schrijfactie**, niet per eigenaar: de relay geeft door hoeveel bytes déze
actie nodig heeft en telt niets op. Trezor telt wel op, maar uit hun eigen tabel, en die hebben wij niet.
Wij nemen die 1 MB over als bewuste keuze, instelbaar met `RELAY_MAX_WRITE_BYTES`: één labelwijziging is
klein, dus een schrijfactie die daarboven uitkomt is eerder een fout of misbruik dan normaal gebruik. En de
allowlist is **staat**: die hoort onder `${APP_DATA_DIR}/data/`, naast de relay-database en niet erin.
**Wat er bewust níet in gaat: een geheim pad in de URL.** De cliënt neemt een pad in de relay-URL letterlijk