Files
UmbrelApps/Docs/Referenties/Upstream-evolu-relay.md
T
HarmenandClaude Opus 5 1b136609f0 Wat Trezor er zelf over publiceert, met de bronnen erbij
Nagetrokken op hun documentatiepagina's nadat op iOS niets bleek te werken. Drie
feiten die dit pakket raken, en ze staan verspreid over pagina's die elkaar niet
noemen.

Een eigen relay is bij Trezor een gedocumenteerde functie: "Custom server" staat
gewoon in de interface, met een invoerveld voor je eigen adres, ook op mobiel. Wat
wij verpakken is dus een ondersteund gebruikspatroon en geen omweg. Let op dat dat
iets anders is dan het dev-utils-veld waarmee wij getest hebben.

Suite Sync werkt op Safe 3, 5 en 7 en vraagt altijd een bevestiging op het
apparaat, ook op mobiel. En op iOS werkt alleen de Safe 7, want die verbindt over
Bluetooth terwijl de andere modellen USB gebruiken en dat ondersteunt iOS niet.

De combinatie van die twee wordt nergens genoemd, en dat is precies waar deze
store een dag aan kwijt was: de Suite Sync-pagina beschrijft de mobiele stappen
alsof ze op elk toestel werken. Er bestaat een forumdraad over verwarring rond
apparaatcompatibiliteit, maar die is zonder inloggen niet te lezen, dus wat daar
staat is niet bevestigd.

Tot slot een getal dat het verschil laat zien: hun eigen dienst kapt af op 1 MB,
ongeveer 2500 bewerkingen per apparaat, waarna de rest lokaal blijft. Dat is een
totaal en het is wat hun quota-manager bewaakt. Een zelf-gehoste relay kent die
grens niet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 13:41:47 +02:00

402 lines
24 KiB
Markdown

# Upstream: `trezor/trezor-suite-sync` en Evolu Relay
Naslag, geen plan. **Nagetrokken tegen de bron op 25-08-2026**, met de URL's onderaan. Dat is een andere
status dan de eerste versie van dit document had: die leunde op vooronderzoek van dezelfde dag en meldde
per regel dat het niet geverifieerd was. Wat hieronder staat komt uit de bestanden zelf.
Op drie punten week de werkelijkheid af van het vooronderzoek, en alle drie raken het pakket. Ze staan in
§2, §3 en §4.
## 0. Let op: er zijn twee relays, en wij pakketteerden de zware
**Toegevoegd 25-08-2026, aan het eind van de dag, nadat de gebruiker
[evolu.dev/docs/relay](https://www.evolu.dev/docs/relay) aandroeg.** Dit staat bovenaan omdat het de rest
van dit document in een ander licht zet.
**Evolu is een eigen project** (`evoluhq/evolu`) en heeft een eigen relay. `trezor/trezor-suite-sync` is
niet dat project maar Trezor's **inzet** ervan, met een eigen opslaglaag en de quota-manager eromheen. Het
verschil is groot:
| | Evolu, `apps/relay` | Trezor, `trezor-suite-sync` |
|-|-|-|
| Containers | één | drie: relay, quota-manager, Postgres |
| Image | **gepubliceerd**: `docker.io/evoluhq/relay:latest` | geen; eigen Amazon ECR, dus zelf bouwen |
| Opslag | een datavolume op `/app/data`; de documentatie noemt de relay **stateless** en geschikt voor serverless | Postgres |
| Toegangscontrole | niets over gedocumenteerd | eigenaar zonder limietenrij wordt geweigerd |
| Poort | 4000, `ws://` | 4000 |
**De vraag die hieruit volgt is de belangrijkste die dit project nu heeft: praat Trezor Suite met een kale
Evolu-relay?** Zo ja, dan vervalt vrijwel alles wat dit pakket ingewikkeld maakt: geen bouwstap, geen eigen
register, geen onderhoudsplicht op een image, geen Postgres, geen wachtwoord, en vooral geen
eigenaarsregistratie, wat nu de blokkade is.
### Wat de cliëntkant zegt, en dat is beslissend
Nagelezen in de broncode van Trezor Suite zelf, die lokaal staat; zie §7. Drie bevindingen, en de tweede is
de belangrijkste van dit hele document.
**1. Suite kan naar een eigen relay wijzen, en het zijn twee losse adressen.** In de instellingen onder
**dev-utils** staan een relay-URL en een quota-manager-URL, elk met een eigen opslaan-knop. De e2e-test van
de mobiele app vult ze met `http://10.0.2.2:4000` en `http://10.0.2.2:4001`, dus **`http://` volstaat** en
de poorten zijn precies die van ons. Het aanzetten van de synchronisatie vraagt daarna een bevestiging op
het Trezor-apparaat zelf.
**2. Met een eigen relay-URL negeert Suite de quota-manager volledig.** Letterlijk, uit
`suite-common/suite-sync-quota-manager/src/createSuiteSyncQuotaManagerCompositionRoot.ts`:
```ts
// We only want to use QM for our own relay servers. In case custom URL has been set, QM is ignored,
// unless enforceQuotaManager is set (used for e2e tests with a local relay).
const getIsQuotaManagerEnabled = () =>
deps.getIsUsingTrezorRelay() || selectEnforceQuotaManager(deps.getState());
```
`enforceQuotaManager` staat standaard op `false` en bestaat voor hun eigen e2e-tests.
**Dat is een klem, en hij zit in Trezor's relay en niet in ons pakket.** De cliënt registreert geen eigenaar
zodra je een eigen relay gebruikt, maar de relay van Trezor blijft `isOwnerAllowed()` doen en weigert
iedereen zonder limietenrij (§3). Die rij komt er dus nooit, want het enige dat hem zou maken wordt door de
cliënt overgeslagen. **Trezor's relay is daarmee als zelf-gehoste relay in de kern onbruikbaar**, tenzij je
de rij met de hand in de database zet of de quota-manager buitenom aanroept.
**3. En dat maakt de kale Evolu-relay niet alleen eenvoudiger maar waarschijnlijk de enige die werkt.** Daar
is geen limietentabel en geen eigenaarscontrole, en de cliënt wil sowieso geen quota-manager bij een eigen
relay. De twee kanten passen op elkaar.
**Wat nog steeds gemeten moet worden**, want dit is redeneren uit code en geen proef: of de protocolversie
klopt. Suite gebruikt `@evolu/web@3.0.0-next.1` **met een eigen patch** in `.yarn/patches/`, en
`docker.io/evoluhq/relay:latest` hoeft daar niet bij te passen. Dat is nu het enige echte risico, en het is
in minuten te weerleggen: `docker run --rm -p 4000:4000 docker.io/evoluhq/relay:latest`, Suite ernaartoe
wijzen, label maken.
**4. De relay-URL bevat een pad, en de cliënt gebruikt hem letterlijk.** Toegevoegd 28-08-2026 uit
`suite-common/suite-sync/src/relay/relayUrl.test.ts`. Hun eigen standaardwaarden zijn
`https://suite-sync.trezor.io/evolu/` en lokaal `http://127.0.0.1:4000/evolu/`: de relay van Trezor hangt
dus achter een pad, en een eigen URL wordt overgenomen zoals hij is. De kale Evolu-relay luistert op de
wortel; dat is gemeten met `http://10.0.0.11:4000` zonder pad.
**Dat opent een goedkope toegangscontrole die in geen van de drie wegen van Bereikbaarheid §4b stond:** een
onraadbaar pad in de URL, afgedwongen door de reverse proxy die er toch al staat. Geen eigen image, geen
regel code.
**En het verklaart de foutmelding op iOS.** De URL wordt gevalideerd met een `.url()`-regel uit yup
(`relayServerSettings.ts`), en die accepteert `ws://` niet. "Enter a valid url" ging dus over het schema en
niet over het adres.
**5. Eén `OwnerId` per wallet, niet per apparaat en niet per installatie.** Toegevoegd 28-08-2026, nadat er
op de eigen relay tot verrassing twéé eigenaars verschenen.
De eigenaar komt van het apparaat zelf: `createRetrieveSuiteSyncOwner.ts` roept
`trezorConnect.evoluGetNode` aan met een proof of delegated identity onder de header `EvoluGetNode`. De
afleiding gebeurt dus op de Trezor. `createEnsureSuiteSyncOwner.ts` bewaart het resultaat vervolgens per
**`device.state.staticSessionId`**, en dat is in Suite een *device state*: een wachtwoordzin geeft een
andere state.
Wat daaruit volgt en wat makkelijk verkeerd gedacht wordt:
- **twee installaties met dezelfde Trezor en dezelfde wallet komen op dezelfde `OwnerId` uit.** Dat is wat
synchroniseren tussen apparaten mogelijk maakt zonder dat die apparaten elkaar kennen, en het betekent
dat een tweede apparaat géén nieuwe eigenaar oplevert;
- **meer dan één `OwnerId` betekent meer dan één wallet**, bijvoorbeeld een verborgen wallet achter een
wachtwoordzin. Niet meer dan één app.
Voor een relay met een eigenaars-allowlist is dat bepalend: het aantal eigenaars dat je toelaat is het
aantal wallets dat je synchroniseert. "De eerste eigenaar die verbindt" is dus te smal zodra iemand een
tweede wallet gebruikt, en dat is op 28-08-2026 meteen gebeurd.
Dat het huidige pakket de zware variant bevat is geen fout maar het gevolg van de volgorde waarin het
gevonden is: de laag van Trezor was eerder zichtbaar dan de laag eronder.
## 1. Wat het is
Trezor Suite kan labels en accountnamen synchroniseren tussen apparaten. De server die dat doet heet in de
interface "custom sync server"; het onderdeel zelf heet **Evolu Relay** en staat in de repo
`trezor/trezor-suite-sync` (standaardbranch `main`, licentie in `LICENSE.md`, door GitHub geclassificeerd
als "other").
Evolu is een local-first synchronisatielaag: de client houdt zijn eigen kopie bij en de relay is een
doorgeefluik voor versleutelde wijzigingen. Volgens Trezor is de data client-side end-to-end versleuteld,
dus de relay ziet niets leesbaars. **Zelf hosten haalt Trezor uit de vergelijking, het verandert de
privacygaranties niet.**
## 1b. Wat een relay is, wat hij ziet, en waarom hij niets hoeft te vertrouwen
Uitgezocht op 25-08-2026 op de vraag van de gebruiker of iemand met kennis van de API de labels kan
opvragen als de relay op een domein staat. Bronnen: [evolu.dev/docs/privacy](https://www.evolu.dev/docs/privacy),
[Owner](https://www.evolu.dev/docs/api-reference/common/local-first/Owner) en
[Protocol](https://www.evolu.dev/docs/api-reference/common/local-first/Protocol).
**Het is geen key-value-store.** De cliënt houdt zijn eigen SQLite bij; de relay bewaart versleutelde
CRDT-berichten per eigenaar en helpt twee kanten met range-based set reconciliation uitzoeken wat ze
allebei al hebben. Dichter bij een append-only berichtenlog dan bij een sleutel-waardetabel.
**Wat de relay ziet:** een `OwnerId`, tijdstempels, IP-adressen, en versleutelde blobs die gevuld zijn om
hun omvang te verhullen. Meer niet.
**Drie waarden, met SLIP-21 afgeleid uit één `OwnerSecret`:**
| Waarde | Wat het doet |
|-|-|
| `OwnerId` | de **publieke** identificatie |
| `OwnerEncryptionKey` | ontsleutelt de data |
| `OwnerWriteKey` | rotatable token dat schrijven autoriseert; zonder is een cliënt `ReadonlyOwner` |
**Het antwoord op de vraag is dus nee**, en de reden is scherper dan "het is versleuteld". De `OwnerId` is
expres niet geheim: hij gaat onversleuteld naar de relay en de beheerder ziet hem. De beveiliging leunt er
niet op dat niemand hem kent. Dat is het tegenovergestelde van een systeem waar een onraadbare URL de grens
is, en het is precies waarom je deze relay bij een onbekende partij kunt neerzetten.
Dat de drie waarden **onafhankelijk** zijn, is wat dat mogelijk maakt: je geeft de relay je identiteit
zonder iets over je sleutel prijs te geven, en je kunt de write key roteren zonder je identiteit te
verliezen. De protocol-documentatie zegt dat de write key gecontroleerd wordt **vóór** het verwerken van
berichten.
**Twee dingen die hier níet uit volgen, en die je niet moet gladstrijken:**
1. **Of iemand die een `OwnerId` kent de versleutelde blobs kan ophálen, staat nergens.** De
protocolpagina zwijgt erover. Kan het, dan leert zo iemand dat die eigenaar bestaat, wanneer hij actief
is en bij benadering hoeveel data er is. Niet de inhoud.
2. **Onraadbaarheid van de `OwnerId` beschermt tegen aflopen van de relay, niet tegen lezen.** Het is
verkeersbescherming en geen vertrouwelijkheid.
**En het echte risico van een publieke relay is een ander dan vertrouwelijkheid: misbruik als opslag.** Een
kale Evolu-relay kent geen accounts en geen toegangscontrole, dus iedereen kan een eigen owner aanmaken en
hem als gratis versleutelde opslag gebruiken. Je schijf groeit en je kunt niet zien van wie het is, want
dat is nu juist het ontwerp. Dit verklaart met terugwerkende kracht waarom Trezor een quota-manager heeft:
die is niet alleen voor facturering maar ook hun misbruikbeheersing. Wie hem eruit haalt, haalt die tweede
functie mee weg. Zie het masterplan **Bereikbaarheid**.
## 2. Eén codebase, vier processen, en een compose die de app niet draait
**Correctie op het vooronderzoek.** Daar stond dat er een `Dockerfile` en een `docker-compose.yaml` in de
repo staan en dat containeriseren dus "al gedeeltelijk gedaan is door Trezor zelf". Het eerste klopt, het
tweede niet: **de compose van Trezor draait de relay niet.** Er staan precies twee diensten in, en dat is
een ontwikkelopstelling:
- `db`: `image: postgres`, ongepind, met `PGDATA: /data/postgres` en `5432:5432` naar buiten;
- `prometheus`: `network_mode: host`, die `localhost:4003` afloopt.
De README bevestigt de bedoeling: `docker compose up` in de ene terminal voor de database, en in de andere
`yarn start-quota-manager`, `yarn start-evolu-relay` en `yarn start-metrics`. Wat Trezor zélf uitrolt staat
niet in de compose maar in `.k8s/`, met Kustomize-overlays voor dev en prod.
**Alle processen komen uit dezelfde codebase en dus uit dezelfde image.** Dat is de belangrijkste
pakketteerwinst van dit hele onderzoek: je hebt geen drie images nodig, maar één image met per service een
ander `command`. De `Dockerfile` is `node:24-alpine`, twee fasen, Yarn 4.12.0, met `EXPOSE 4000 4001` en
`CMD ["yarn", "start"]`. Bij dat `CMD` staat bovenstrooms zelf een commentaar dat het misschien
`start-quota-manager` of `start-evolu-relay` had moeten zijn, dus reken er niet op dat `yarn start` het
juiste doet; zet het commando expliciet.
De poorten, met hun standaardwaarde uit `src/config.ts`:
| Proces | Poort | Waarvoor |
|-|-|-|
| evolu-relay | 4000 | de sync-relay, dit is wat we nodig hebben |
| quota-manager | 4001 | quota- en betaalserver |
| health | 4002 | statuscontrole |
| metrics | 4003 | Prometheus |
In `.env.sample` staan ze alle vier uitgecommentarieerd, dus de standaardwaarden gelden tenzij je ze zet.
`DATA_DIR` heeft standaard de waarde `data`.
## 3. Is de quota-manager verplicht? Ja, in de praktijk wel
**Dit is de vraag waar het vooronderzoek op hoopte dat het antwoord nee zou zijn.** Dat is het niet, en de
reden is preciezer dan "de relay heeft de quota-manager nodig".
De relay bevat twee controles, in `src/evoluRelay/createEvoluRelay.ts`:
- **`isOwnerAllowed()`** vraagt `getLimitsForOwner({ ownerId })` op en staat de eigenaar alleen toe als daar
iets terugkomt dat niet leeg is;
- **`isOwnerWithinQuota()`** controleert bij schrijven of `usedBytes + requiredBytes` binnen de limiet
blijft.
De strekking: **een eigenaar zonder rij in de limietentabel wordt geweigerd.** En de quota-manager is
precies het onderdeel dat die rijen aanmaakt. Zonder hem is de relay dus niet "open", maar dicht voor
iedereen.
Twee dingen die dit relativeren en die het pakket bepalen:
- **er is geen HTTP-koppeling tussen de twee.** In `src/config.ts` staat geen URL of hostnaam voor de
quota-manager, alleen een poort waarop hij zelf luistert. Ze delen de Postgres. Praktisch gevolg: de
relay hoeft de quota-manager niet te kunnen bereiken, alleen de tabel moet gevuld zijn;
- **daarmee zijn er twee wegen**, en ze zijn allebei legitiem: de quota-manager meepakketteren en er één
eigenaar in registreren, of de limietenrij één keer zelf in de database zetten. Het tweede is minder
bewegende delen, maar het is wél zelf in andermans schema schrijven, en dat breekt bij een migratie.
**Wat hier niet is uitgezocht**, en het is de eerste vraag voor wie hier verder gaat: hoe je bij de
quota-manager een eigenaar registreert, en of daar iets extern voor nodig is. De README verwijst voor de
API-specificatie naar Notion en voor het uitproberen naar Bruno; er zit een `bruno-collection/` in de repo
die dat waarschijnlijk laat zien.
### De onopgeloste tegenspraak rond dev en prod
In `.env.sample` staat `SERVER_ENV="dev" # dev | prod (PROD enables evolu relay authentication)`. Dat
suggereert dat de controles in dev uit staan, en dat zou een self-hosted relay eenvoudig maken.
**Dat is niet terug te vinden in de code die ik gelezen heb.** In `src/config.ts` wordt
`isDevServer = SERVER_ENV === 'dev'` gezet, en in `createEvoluRelay.ts` bepaalt die vlag alleen het
logniveau (`debug` tegenover `info`). `createEvoluRelayCompositionRoot.ts` hangt `getLimitsForOwner`
onvoorwaardelijk in, zonder tak voor dev.
Dus: óf het commentaar in `.env.sample` gaat over iets anders dan deze twee controles, óf het is
achterhaald. **Ga er niet van uit dat `SERVER_ENV=dev` de deur opent**, en ga er ook niet van uit dat hij
dicht is: dit is precies het soort aanname waar een middag in gaat zitten. Het is met één keer starten te
meten en dat hoort in **Proefopstelling** fase 2.
## 4. Er is geen publieke image, en dat is de grootste consequentie
**Tweede correctie op het vooronderzoek**, en de duurste. Trezor bouwt wel een image, maar publiceert hem
niet ergens waar jij hem kunt halen.
Uit `.github/workflows/build-and-push-image.yml`: de image gaat via `aws-actions/amazon-ecr-login` naar een
**Amazon ECR in eu-central-1**, met de registry-hostnaam als uitvoer van die stap en de naam als
invoerparameter. Er is geen Docker Hub en geen ghcr in dat werkproces. Onder de namespace `trezor` op
Docker Hub staat niets dat hierop lijkt.
Wat daaruit volgt voor dit project:
- **je bouwt en publiceert de image zelf**, of er is geen Umbrel-app. Dat is geen eenmalige handeling maar
een doorlopende verplichting: bij elke nieuwe versie van Trezor bouw je opnieuw;
- **`linux/arm64` is geen risico meer.** Datzelfde werkproces bouwt `platforms: linux/amd64,linux/arm64`
met QEMU, dus de Dockerfile is bovenstrooms bewezen op arm64. Dat was punt 2 van de oude versie van dit
document en die vraag is hiermee weg;
- **de licentie wordt nu wél een vraag.** GitHub classificeert `LICENSE.md` als "other". Zolang je alleen
zelf draait is dat academisch; zodra je een image publiceert, distribueer je hun software. Lees dat
bestand voordat er iets in een openbaar register komt.
## 5. Wat er nog helemaal niet uitgezocht is
Deze staan hier omdat ze het pakket bepalen en omdat er in deze ronde geen antwoord op kwam. Ze zijn geen
taak (dat zijn ze in **Proefopstelling** en **Umbrelapp**).
1. **Kan Trezor Suite überhaupt naar een eigen sync-server wijzen, en op welk platform?** Onveranderd de
goedkoopste weerlegging van het hele project, en niets in deze repo zegt er iets over: dit is de
serverkant.
2. **Accepteert Trezor Suite een `http://`-adres, of eist het TLS?** Daar hangt het masterplan
**Bereikbaarheid** aan.
3. **Hoe registreer je een eigenaar bij de quota-manager**, en heeft die daarvoor iets extern nodig? Zie
§3. Begin bij `bruno-collection/`.
4. **Wat doet `SERVER_ENV=prod` dat `dev` niet doet?** Zie de tegenspraak in §3.
5. **Wat staat er in `LICENSE.md`?** Zie §4.
## 6. Bronnen
Alles hierboven komt uit deze bestanden, geraadpleegd op 25-08-2026:
- [`docker-compose.yaml`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/docker-compose.yaml)
- [`.env.sample`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/.env.sample)
- [`Dockerfile`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/Dockerfile)
- [`README.md`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/README.md)
- [`src/config.ts`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/src/config.ts)
- [`src/evoluRelay/createEvoluRelay.ts`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/src/evoluRelay/createEvoluRelay.ts)
- [`src/evoluRelay/createEvoluRelayCompositionRoot.ts`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/src/evoluRelay/createEvoluRelayCompositionRoot.ts)
- [`.github/workflows/build-and-push-image.yml`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/.github/workflows/build-and-push-image.yml)
- Docker Hub, namespace `trezor`: geen image die hierop lijkt
Het oorspronkelijke vooronderzoek van 25-08-2026 staat ongewijzigd in
[Vooronderzoek.PLAN.md](../Plannen/Masterplannen/Archief/Vooronderzoek.PLAN.md). Wat daar niet meer klopt,
staat hierboven in §2 en §4.
## 7. De broncode van Trezor Suite staat lokaal
Aangedragen door de gebruiker op 25-08-2026, en het beantwoordde in een half uur wat uit de serverkant
alleen niet te halen was. In een ander project van de gebruiker staat een kloon van de cliënt:
```
D:\HomeGit\Trezor\Repos\trezor-suite
```
Dat is de tegenkant van deze relay, en bij een vraag als "verwacht de cliënt dit eigenlijk wel" is dat de
snelste bron. De plekken die hier iets opleverden:
| Pad | Wat je er vindt |
|-|-|
| `suite-common/suite-sync-quota-manager/src/createSuiteSyncQuotaManagerCompositionRoot.ts` | de regel dat de quota-manager genegeerd wordt bij een eigen relay-URL |
| `suite-native/app/e2e/pageObjects/settingsActions.ts` | hoe je het in de interface instelt, met werkende voorbeeld-URL's op 4000 en 4001 |
| `suite-native/module-settings/src/hooks/useSuiteSyncRelayUrlForm.ts` | het formulier: keuze tussen de standaardserver en een eigen URL |
| `suite-native/suite-sync/src/createSuiteSyncNativeCompositionRoot.ts` | hoe de Evolu-cliënt wordt samengesteld |
| `.yarn/patches/@evolu-web-npm-3.0.0-next.1-*.patch` | dat Trezor de Evolu-cliënt **patcht**, en op welke versie |
Twee waarschuwingen bij die bron. Het meeste hierboven komt uit **`suite-native`**, dus de mobiele app; de
desktopversie kan andere instellingen op een andere plek hebben, en dat is niet nagekeken. En het is een
kloon op een moment in de tijd: bij twijfel de datum van die kloon opzoeken voordat je er een conclusie op
bouwt.
## 8. De uitbreidpunten van de kale relay
Nagetrokken 28-08-2026 in de broncode van `evoluhq/evolu`, nadat de proef bewees dat de kale relay bruikbaar
is. Dit is wat een eigen limiter mogelijk maakt, en het is aanzienlijk minder werk dan **Bereikbaarheid**
§4b aannam.
**Er wordt geen relay nagebouwd; er worden twee functies meegegeven.** `createRelay` uit `@evolu/nodejs`
heeft deze vorm:
```ts
createRelay({ port = 443, name, isOwnerAllowed, isOwnerWithinQuota }): Task<Relay, never, RelayDeps>
```
`isOwnerAllowed(ownerId)` beslist of een eigenaar er überhaupt in mag, `isOwnerWithinQuota(ownerId,
requiredBytes)` of een schrijfactie past. Beide zijn asynchroon. De opslag wordt intern gemaakt met
`createRelaySqliteStorage(deps)({ isOwnerWithinQuota })`, dus SQLite, en `createRelayDeps()` levert de
driver.
**Trezor doet exact dit en niets meer.** Hun `createEvoluRelay.ts` roept de relay aan met die twee
functies, en beide kijken in hun limietentabel via `getLimitsForOwner()`. De hele quota-manager plus
Postgres bestaat dus alleen om die tabel te vullen. Dat is het bewijs dat ons pakket met dezelfde twee
haken hetzelfde kan zonder die machinerie.
**De gepubliceerde image vult ze zelf al deels in**, en dat is een valstrik om te kennen. In
`apps/relay/src/index.ts` staat `isOwnerAllowed` **uitgecommentarieerd** (dus iedereen mag erin) en
`isOwnerWithinQuota` op **1 MB per eigenaar**. Dat is geen theorie maar de image die wij draaien:
- **die grens geldt per schrijfactie en niet per eigenaar.** De relay geeft door hoeveel bytes déze actie
nodig heeft; er wordt niets opgeteld. Trezor telt wél op, maar dat doen ze zelf uit hun eigen tabel, en
die tabel is precies wat een kale relay niet heeft. Een labelgeschiedenis kan hier dus niet tegenaan
lopen;
- **wat het wél tegenhoudt is één grote schrijfactie.** Dat dempt misbruik als gratis opslag, maar het is
geen totaalquotum. Wie dat wil, moet in de opslag van de relay kijken hoeveel een eigenaar al gebruikt,
en dat is schrijven noch lezen in je eigen schema.
De relay zet zijn data in een map `data` naast het programma (`mkdirSync` plus `process.chdir`), en dat is
het volume `/app/data` uit de image. In de database zitten onder meer `evolu_message` en `evolu_history`, en
`ownerId` is een systeemkolom. **Data per eigenaar wissen is daarmee mogelijk maar het is schrijven in
andermans schema**, met dezelfde bezwaren als bij Trezor's limietentabel.
Bronnen, geraadpleegd 28-08-2026:
- [`packages/nodejs/src/local-first/Relay.ts`](https://raw.githubusercontent.com/evoluhq/evolu/main/packages/nodejs/src/local-first/Relay.ts)
- [`apps/relay/src/index.ts`](https://raw.githubusercontent.com/evoluhq/evolu/main/apps/relay/src/index.ts)
- [`src/evoluRelay/createEvoluRelay.ts`](https://raw.githubusercontent.com/trezor/trezor-suite-sync/main/src/evoluRelay/createEvoluRelay.ts) van Trezor
- [evolu.dev/docs/relay](https://www.evolu.dev/docs/relay) en [evolu.dev/docs/evolu-server](https://www.evolu.dev/docs/evolu-server)
## 9. Wat Trezor er zelf over publiceert
Nagetrokken 28-08-2026 op de eigen documentatiepagina's, nadat op iOS niets bleek te werken. Drie feiten
die het pakket raken, en ze staan verspreid over pagina's die elkaar niet noemen.
**Een eigen relay is een gedocumenteerde functie en geen ontwikkelaarstruc.** De pagina
[Suite Sync in Trezor Suite](https://trezor.io/guides/trezor-suite/suite-sync) beschrijft "Custom server"
als keuze in de interface, met een invoerveld voor je eigen adres, ook op mobiel. Dat is prettig voor dit
plan: wat wij verpakken is een ondersteund gebruikspatroon en geen omweg. Let op dat het iets anders is dan
het dev-utils-veld waar wij mee getest hebben.
**Suite Sync werkt op Safe 3, Safe 5 en Safe 7**, en aanzetten vraagt altijd een bevestiging op het
apparaat, ook op mobiel: "Confirm the action on your Trezor hardware wallet". Model One en Model T gebruiken
het oude labelformaat en doen niet mee.
**Op iOS werkt alleen de Safe 7.** Die verbindt over Bluetooth; alle andere modellen gaan over USB en dat
ondersteunt iOS niet, waar alleen view-only via XPUB overblijft. Zie de
[Trezor Suite Lite FAQ](https://trezor.io/guides/trezor-suite/trezor-suite-lite/trezor-suite-lite-faqs).
**De combinatie van die twee wordt nergens genoemd**, en dat is precies waar deze store een dag aan kwijt
was: de Suite Sync-pagina beschrijft de mobiele stappen alsof ze op elk toestel werken, terwijl de
bevestiging op een model dat niet aan een iPhone kan nooit gegeven kan worden. Er bestaat een forumdraad
"Suite Sync: confusion about device compatibility", niet leesbaar zonder inloggen, wat erop wijst dat meer
mensen hier tegenaan lopen.
**En één getal om te onthouden: hun eigen dienst kapt af op 1 MB, ongeveer 2500 bewerkingen per apparaat**,
waarna de rest lokaal blijft. Dat is een totaal, en het is de grens die hun quota-manager bewaakt. Een
zelf-gehoste relay kent die grens niet: wij begrenzen alleen één schrijfactie (zie §8), en dat is dus een
echt verschil in wat de app oplevert en geen detail.