UmbrelApps: community app store whatsnext

Electrum Gate, Evolu Relay en Atreides Assets, met de image-recepten in tools/ en de tests in tests/. Begonnen zonder voorgeschiedenis op 09-10-2026.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
HarmenandClaude Opus 5.5 committed 2026-10-09 10:40:25 +02:00
commit 85d85a1295
63 files changed
+13987

No files matched your search

Whitespace-only changes.
+137
View File
@@ -0,0 +1,137 @@
# ═══════════════════════════════════════════════════════════════════════════════
# Evolu Relay - je eigen sync-server voor apps die op Evolu gebouwd zijn.
#
# 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.
# een sync-cliënt 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_server_1
APP_PORT: 80
# Alle drie de containers draaien dezelfde image, 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.
#
# Tot 0.5.6 zat alleen de relay in die image en draaiden de agent en de pagina
# als *.template op python:3-alpine en nginx:alpine. Sinds 0.6.0 zit alles erin;
# zie het plan Eigenimage, PLAN.md §8.
#
# De digest hoort erbij en niet alleen de tag: een tag kan opnieuw geduwd 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 build.sh (drie keer hier) en `version` in het manifest.
#
# De keerzijde bij een verhoging: een OUDE digest onder een NIEUWE tag levert
# stilzwijgend de oude image. Bij 0.5.0 stond hier daarom even een tag zonder
# digest, zodat het hard faalde tot de image bestond, in plaats van stil de
# relay zonder tijdvenster te draaien. Doe dat weer zo bij elke verhoging: eerst
# de digest weg, dan bouwen en duwen, dan de nieuwe erin.
#
# Gebouwd op de Umbrel zelf, dus één architectuur (amd64). Voor de officiele
# store hoort er een multi-arch index-digest met arm64 in; zie het masterplan
# Publicatie-Relay.
relay:
image: sc.kamenier-hamer.nl/sysop/evolu-relay:0.7.1@sha256:a701032aae4aba8fd7acace0c0d6c900ad6ace32eee03762b36b00c7e8c0ce6c
restart: on-failure
# Als node en niet als root, zoals de image tot 0.5.6 zelf al deed met USER.
# Nu de image ook nginx en de agent draagt, staat er geen USER meer in en
# regelt de compose het per container; de relay is de enige die het niet
# nodig heeft. De map data/relay is van deze gebruiker.
user: node
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"
# 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 synchronisatie een
# herinstallatie van de app niet overleven.
- ${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:
image: sc.kamenier-hamer.nl/sysop/evolu-relay:0.7.1@sha256:a701032aae4aba8fd7acace0c0d6c900ad6ace32eee03762b36b00c7e8c0ce6c
restart: on-failure
command:
- python3
- /app/agent.py
environment:
RELAY_STATE_DIR: /var/lib/relay
# De versie uit het manifest, voor de kop van de pagina. Die stond tot 0.5.6
# rechtstreeks in de pagina, ingevuld door umbreld; nu de pagina in de image
# zit, geeft de agent hem door via api/status.
RELAY_APP_VERSION: ${APP_VERSION}
# 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 je cliënt invult.
# Moet gelijk zijn aan de hostpoort hierboven.
RELAY_PUBLIC_PORT: "3852"
RELAY_API_PORT: "8000"
volumes:
# 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
# nginx met de pagina, uit dezelfde image. Geen mounts: pagina, configuratie en
# icoon zitten in de image, en de pagina heeft geen eigen staat.
server:
image: sc.kamenier-hamer.nl/sysop/evolu-relay:0.7.1@sha256:a701032aae4aba8fd7acace0c0d6c900ad6ace32eee03762b36b00c7e8c0ce6c
restart: on-failure
command:
- nginx
- -g
- daemon off;
# De agent moet er zijn voordat nginx start: de proxy_pass naar de agent wordt
# bij het starten opgelost en een onbekende naam laat nginx afbreken.
depends_on:
- agent
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+195
View File
@@ -0,0 +1,195 @@
# De veldvolgorde hieronder is niet vrij: de packaging-documentatie van de
# officiele appstore schrijft hem voor. Zie de tabel in
# HomeGit/Docs/Referenties/UmbrelApps/Umbrel-appstore-spec.md.
#
# Wat de spec niet noemt staat onderaan dit bestand en niet ertussen. Dan is de
# voorgeschreven kop letterlijk goed en hoeft er bij inlevering alleen iets weg.
#
# `dependencies` is weggelaten en niet leeg gezet: deze app hangt van geen enkele
# andere app af. Een leeg veld zou suggereren dat er iets te kiezen valt.
manifestVersion: 1
id: whatsnext-evolu-relay
category: files
name: Evolu Relay
version: "0.7.1"
tagline: Encrypted sync and backup for your local-first apps
description: >-
Local-first apps keep your data on your own device and work whether or not you have a
connection. A relay is what lets a second device catch up: it holds the encrypted changes
until your other devices come online. This app runs that relay on your Umbrel, so the copy
in the middle sits on hardware you own.
It is the relay from the Evolu project, taken straight from the package they publish and not
reimplemented here. Any app built on Evolu can use it.
Your data is encrypted on your device before it leaves. The relay holds encrypted blobs,
padded so their size says nothing, and never has the key. What it can see is an owner id,
which identifies data without identifying you, along with timestamps and the addresses that
connect. Self-hosting does not make the data more secret than it already is; what it changes
is who keeps the encrypted copy, who can see that you are syncing at all, and whether anyone
can pull the service out from under you.
What this app adds is the part a self-hosted relay needs and a public one does not: control
over who may use it. New owners are accepted one at a time. The first one that connects is
remembered, and you close the door again once your own devices are paired; anything new is
refused until you open it. Any owner can be blocked or removed later.
The tile opens a status page behind your umbrelOS login, showing whether the relay is
running, how much it holds, when it was last written to, and which owners are allowed. Point
your app at the address shown there. Away from home you will need a way in, such as Tailscale
or a reverse proxy with your own domain.
releaseNotes: >-
Tidying up the two owner panels. They are called Accepted and Waiting now, the count of owners waiting
sits in that panel's own heading instead of a widget at the top, the note under the relay address is
gone, and a row on the waiting list no longer carries a "Waiting" tag: the panel it sits in already says
that, and a blocked owner is now the only row with a tag.
Every row is the same height as well, in both panels. A row used to grow the moment you gave it a
name, because the name pushed the owner id onto a line of its own; the id now shares the second line
with the timestamps.
About this app no longer insists the sync address must start with http. Which scheme the field in your
app wants depends on the app; the host and the port are the same either way.
Everything in 0.6.0:
The whole app now ships as one image: the relay, the status page and the small helper behind it. Until
now only the relay did, and the other two were mounted into stock images from the app folder. Nothing
changes in what the app does; the app data folder now holds only your data, and the icon updates along
with everything else.
Everything in 0.5.6:
Owners are now listed most recently seen first, in both panels. Until now the rows kept the order in
which owners first showed up, which stops meaning anything after a week. The order follows along on its
own: the page refreshes its lists every few seconds, so a reconnecting owner moves to the top without a
reload.
Everything in 0.5.4:
The status widget has moved to the right of the top row and is now called Status, matching Electrum
Gate. The two pages are one app store and they should read the same way.
Everything in 0.5.3:
Two adjustments to the refresh. The Relay card reads in the accent colour again when it says
Running, and on a phone the page drops back to the flat look: the glow is wider than a phone screen,
so it stopped being a gradient and became a haze over everything.
Everything in 0.5.2:
A visual refresh, shared with the other apps in this store. The page now has a soft glow behind it in
the accent colour, and the cards are translucent with a light edge and a shadow, so they read as
panels sitting on the page instead of flat blocks painted on it. Card corners are a little tighter,
and every framed element now uses the same corner radius. Nothing about what the relay does has
changed.
Everything in 0.5.1:
Fixes a message at the top of the page that never went away. Confirming that a command was sent is worth
saying for a second or two, but it stayed until you reloaded the page, so it was still there long after it
stopped being true. It now clears itself as soon as the relay has picked the command up. A refusal is the
exception and stays put: that is the only explanation you get of what went wrong.
Everything in 0.5.0, released the same day:
Accepting a new owner is now a two minute window instead of a switch you have to remember to turn
back off. Open it, pair your device, and it closes on its own; you can close it early or restart the
two minutes if you need longer. The countdown runs on the Umbrel, so closing the page does not leave
the door open.
Owner ids can be given a name. They are long strings of random characters that say nothing about
which of your devices or wallets they are, so hover a row and label it. Names are kept on your Umbrel
and are never sent anywhere.
Blocked owners and refused attempts were two lists and are now one, because what you do with them is
the same: allow, or forget. Each row carries a badge saying which it was.
The page itself has been rebuilt to match Electrum Gate in this store: the same width, the same
layout, the same header, and the version number where you can see it. Buttons appear when you hover a
row, so a page you are only reading stays quiet. Explanatory paragraphs have moved into a new
"About this app" item in the menu, which also spells out what the relay can and cannot see.
Earlier releases:
0.4.0 fixed a status page that alternated between working and showing an error. It reached its helper
by a short name that another app in this store uses as well, so about half of its requests ended up
at the wrong app.
0.4.0 also dropped the wording that tied the app to one particular client. It is a general purpose
Evolu relay: any app built on Evolu can use it.
0.2.0 replaced the relay this app shipped with. Until 0.0.2 it packaged a vendor's own deployment,
which came with a quota manager and a PostgreSQL database and could not work outside that vendor's
service: clients skip the quota manager as soon as you point them at a relay of your own, while
that relay refuses every owner the quota manager never registered. 0.2.0 uses the relay from the
Evolu project instead. Measured, not assumed: a client synced to it and the data arrived. Three
containers became one relay plus a status page, and the database and its password are gone.
0.2.0 also added the status page behind your umbrelOS login, and control over who may sync.
Not verified yet: reading back from a second device, and whether every client platform can use a
relay of your own.
developer: "WhatsNext?"
website: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
repo: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
support: https://sc.kamenier-hamer.nl/sysop/UmbrelApps/issues
# De poort waarop umbrelOS deze app aanbiedt. Sinds 0.1.0 is dat de STATUSPAGINA
# en niet de relay: de app-proxy zet er de inlog van umbrelOS voor, en daar hoort
# een pagina wel achter en een relay niet. De cliënt wijst naar 3852, die de
# relay zelf publiceert. Zie docker-compose.yml en het plan Umbrelapp, PLAN.md §4h.
#
# Niet 4000, de eigen poort van de relay: die is een veelgebruikte poort en een
# botsing op de host merk je pas als de app niet start. Dat heeft bij Electrum
# Gate een keer een dag gekost, met 50002 tegen Fulcrum. 3851 sluit aan op de 3850
# van die app.
port: 3851
# Leeg laten bij een nieuw pakket; de plaatjes doet het store-team zelf.
gallery: []
path: ""
submitter: "WhatsNext?"
submission: https://sc.kamenier-hamer.nl/sysop/UmbrelApps
# ── Hieronder staat wat de voorgeschreven volgorde niet noemt ────────────────
#
# Het icoon, sinds 28-08-2026. Het bestand staat in deze repo en wordt door Gitea
# rauw uitgeleverd; dezelfde constructie als bij Electrum Gate. Bij inlevering in
# de officiële store moet deze regel juist weg, want die host iconen apart.
# Daarom staat hij als laatste: dan is het één regel schrappen.
icon: https://sc.kamenier-hamer.nl/sysop/UmbrelApps/raw/branch/main/whatsnext-evolu-relay/icon.png
# Wat niet in de back-up hoeft. Paden zijn relatief aan de app-datamap.
#
# Voor deze app is dat vrijwel niets, en dat is expres: de database ís de waarde
# van de app. Electrum Gate gebruikt dit veld wel, maar daar gaat het om een log
# en een statusbestand. Neem die keuze hier niet uit gewoonte over.
backupIgnore: []