Gate 0.1.0: eigen image, het command-blok is een script, de app-map is leeg

Plan Eigenimage, fase 1 tot en met 3. De vier templates verhuizen naar
tools/electrum-gate/ zonder extensie; daarnaast Dockerfile (nginx:1.30-alpine
plus python3), entrypoint.sh (het command-blok van de compose, zonder $$) en
build.sh naar het voorbeeld van Evolu Relay. Een image voor beide containers,
gebouwd op de Umbrel; open punt 2 en 3 daarmee beslist.

Inhoudelijk anders dan alleen verplaatst: het log_format staat in stream.conf
zelf, het backend-adres komt via twee plaatshouders zonder dollarteken uit de
omgeving (ook in de server-service), en de pagina haalt versie en adres uit
status.json via GATE_APP_VERSION.

Tests mee verhuisd en uitgebreid: entrypoint.sh en Dockerfile in plaats van het
command-blok, en de tag in de compose gelijk aan VERSION in build.sh voor elke
eigen image. Mutatie-getest met drie ingrepen.

Nog niet gebouwd: er is hier geen Docker. De tag staat ongepind tot de eerste
push; dat is fase 4 en die is van de gebruiker.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Harmen
2026-09-07 20:20:42 +02:00
co-authored by Claude Fable 5.1
parent 7b3804df89
commit 973b24a23f
25 changed files with 981 additions and 410 deletions
+141
View File
@@ -0,0 +1,141 @@
#!/bin/sh
# ═══════════════════════════════════════════════════════════════════════════════
# Het startscript van de server-container van Electrum Gate.
#
# Tot 0.0.29 stond dit als inline command-blok in docker-compose.yml, omdat de
# compose in de update-whitelist van umbreld staat en een los script niet. Dat
# betekende shell in een YAML-string, met elk dollarteken verdubbeld omdat
# umbreld anders elke ${...} leegmaakt. Sinds de eigen image is het een gewoon
# bestand; wat het doet is niet veranderd.
#
# Drie dingen die hier gebeuren en waarom ze hier gebeuren en niet in de agent:
#
# 1. de TLS-poort aan- of uitzetten naar de toestand van cert.conf. nginx weigert
# te starten met een 'listen ssl' zonder certificaat, en de pagina waarop je
# dat certificaat kiest moet juist altijd omhoog komen;
# 2. de open verbindingen tellen. Dat kan alleen in deze netwerk-namespace;
# 3. nginx herladen als de agent een vlag neerzet. De agent kan dat niet zelf,
# want dat zou de Docker-socket vragen en die is er bewust uit.
# ═══════════════════════════════════════════════════════════════════════════════
set -eu
STATE=/var/lib/gate
mkdir -p "$STATE" "$STATE/tls"
# ── Het stream-blok invullen ──────────────────────────────────────────────────
# Het backend-adres komt uit de omgeving, dezelfde twee variabelen die de agent
# krijgt. Tot 0.0.29 vulde umbreld ze in via stream.conf.template; nu de
# configuratie in de image zit, gebeurt dat hier. nginx kan in een proxy_pass van
# het stream-blok zelf geen omgevingsvariabele lezen.
#
# Twee plaatshouders zonder dollarteken, zodat het bestand zelf nooit door een
# envsubst-stap beschadigd kan worden, ook niet als iemand het ooit weer als
# template in de app-map zet.
if [ -n "${GATE_ELECTRUM_HOST:-}" ] && [ -n "${GATE_ELECTRUM_PORT:-}" ]; then
sed -e "s|__GATE_ELECTRUM_HOST__|${GATE_ELECTRUM_HOST}|g" \
-e "s|__GATE_ELECTRUM_PORT__|${GATE_ELECTRUM_PORT}|g" \
/etc/nginx/stream.conf.in > /etc/nginx/stream.conf
else
# Zonder backend is er niets om naar door te sturen. De pagina komt wel omhoog
# en de agent meldt daar dat het adres ontbreekt; TLS blijft uit.
rm -f /etc/nginx/stream.conf
echo "GATE_ELECTRUM_HOST of GATE_ELECTRUM_PORT ontbreekt; TLS blijft uit, de pagina werkt."
fi
# ── De TLS-poort aan of uit ───────────────────────────────────────────────────
# Hier zat tot 0.0.3 een lus die wachtte tot de agent een certificaat gekozen had,
# en dat was fout: nginx startte dan niet, dus de pagina kwam niet omhoog, en de
# pagina is juist waar je dat certificaat kiest.
#
# Nu start nginx altijd. Het stream-blok komt erbij zodra cert.conf er is, en
# verdwijnt weer als de agent zijn keuze intrekt. nginx.conf haalt de map tls/ op
# met een jokerteken; die matcht dan niets en dat is geen fout.
#
# Deze functie eindigt altijd geslaagd, en dat is met opzet: het script draait
# onder 'set -e', dus een mislukte cp zou de aanroeper afbreken. In de lus
# hieronder is die aanroeper de wachtlus, en die mag om geen enkele reden stoppen.
sync_tls() {
if [ -f "$STATE/cert.conf" ] && [ -f /etc/nginx/stream.conf ]; then
# Kopiëren en niet linken: nginx opent dit pad als de gebruiker nginx, en een
# symlink is hier nodeloos fragiel.
if cp /etc/nginx/stream.conf "$STATE/tls/stream.conf"; then
echo "Certificaat aanwezig, TLS luistert op 50022."
else
echo "stream.conf kon niet worden weggezet; TLS blijft uit."
fi
else
rm -f "$STATE/tls/stream.conf"
echo "Nog geen certificaat gekozen; TLS staat uit, de pagina werkt."
fi
}
sync_tls
# ── De verbindingsteller ──────────────────────────────────────────────────────
# nginx stream schrijft zijn logregel pas bij het sluiten van een sessie, en een
# wallet houdt zijn verbinding uren open; zonder deze teller lijkt een actieve
# wallet dus afwezig. Dat was de vraag van de gebruiker op 20-08-2026.
#
# /proc/net/tcp geldt per netwerk-namespace, dus dit ziet alleen de sockets van
# deze container, en daarom staat de teller hier en niet in de agent: die zit in
# een andere namespace en kan er niet bij. Kolom 2 is het lokale adres met de
# poort in hex, kolom 4 de toestand. C366 is 50022 en 01 is ESTABLISHED, dus de
# luisterende socket (0A) en de verbindingen naar de backend en naar de pagina
# vallen er buiten.
#
# Geteld wordt alleen het aantal. Geen adres en geen bronpoort, net als in het
# log_format in stream.conf.
count_sessions() {
cat /proc/net/tcp /proc/net/tcp6 2>/dev/null \
| awk '$4 == "01" && $2 ~ /:C366$/ { n++ } END { print n+0 }'
}
# Meteen een 0 neerzetten, zodat de pagina "geen verbindingen" kan tonen in
# plaats van "onbekend" in het minuutje voor de eerste ronde.
count_sessions > "$STATE/sessions"
# ── De herlaadlus ─────────────────────────────────────────────────────────────
# Draait op de achtergrond; nginx wordt hieronder met exec het hoofdproces.
# Andersom gaat twee keer mis: de shell blijft dan PID 1 en geeft signalen niet
# door, en als nginx omvalt eindigt het script met exitcode 0, waardoor Docker een
# geslaagde afsluiting ziet en 'restart: on-failure' niet ingrijpt.
reload_nginx() {
# Een certificaat dat nginx niet aanneemt laat de reload falen. Dat mag niet
# met 'set -e' meegaan, want dan verdwijnt de lus en wordt geen enkele latere
# wijziging meer opgepikt, zonder dat er iets te zien is.
if ! nginx -s reload; then
echo "Herladen mislukt; nginx houdt de vorige configuratie."
fi
}
(
while true; do
sleep 10
# Eerst schrijven, dan mv: de agent leest dit bestand op zijn eigen moment en
# mag geen half bestand zien.
count_sessions > "$STATE/sessions.tmp"
mv "$STATE/sessions.tmp" "$STATE/sessions"
if [ -f "$STATE/reload" ]; then
echo "Certificaat gewijzigd, nginx herladen ..."
# Eerst wegzetten, dan herladen. Andersom zou een herlading die mislukt de
# vlag toch opruimen, en dan probeert hij het nooit meer.
mv "$STATE/reload" "$STATE/reload.done"
# Voor de herlading, niet erna: een reload leest de configuratie opnieuw,
# dus het stream-blok moet er dan al liggen.
sync_tls
reload_nginx
elif [ -f "$STATE/cert.conf" ] && [ ! -f "$STATE/tls/stream.conf" ]; then
# De vangnetregel. De agent zet de vlag bij elke wijziging, dus normaal komt
# hier niets langs; wel als de vlag verloren gaat of als cert.conf van een
# vorige installatie al klaarlag. Zonder deze tak zou TLS dan uit blijven
# staan tot de volgende wijziging.
echo "Certificaat gevonden zonder herlaadvlag, TLS aanzetten ..."
sync_tls
reload_nginx
fi
done
) &
exec nginx -g 'daemon off;'