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:
co-authored by
Claude Fable 5.1
parent
7b3804df89
commit
973b24a23f
@@ -6,21 +6,31 @@ services:
|
||||
APP_HOST: whatsnext-electrum-gate_server_1
|
||||
APP_PORT: 80
|
||||
|
||||
# Beide containers draaien dezelfde image, gebouwd door tools/electrum-gate/
|
||||
# build.sh. Wat erin zit staat in de Dockerfile daar: nginx met de pagina en het
|
||||
# stream-blok, en python3 met de agent. Tot 0.0.29 stonden die bestanden hier
|
||||
# als *.template en draaiden ze op python:3-alpine en nginx:alpine; waarom dat
|
||||
# veranderd is staat in het plan Eigenimage, PLAN.md §3.
|
||||
#
|
||||
# De tag hoort gelijk te zijn aan VERSION in build.sh, en er hoort een digest
|
||||
# achter zodra de image geduwd is. Let op de valstrik uit KNOWLEDGE.md: een
|
||||
# oude digest onder een nieuwe tag levert stilzwijgend de oude image, dus laat
|
||||
# de digest er even van af zolang de nieuwe niet geduwd is.
|
||||
|
||||
# De agent. Hij schrijft status.json, leest de certificaten, bevraagt de
|
||||
# Electrum-server en neemt de certificaatkeuze aan.
|
||||
#
|
||||
# Waarom een tweede container en niet een shell-lus in de server hieronder:
|
||||
# dit werk is inmiddels een programma. Een certificaatdatum uitlezen, een
|
||||
# JSON-RPC-verzoek doen, een geschiedenis bijhouden en een keuze valideren zijn
|
||||
# geen dingen die je met openssl en nc aan elkaar knoopt zonder dat het stil
|
||||
# verkeerde antwoorden gaat geven. Bijkomend voordeel: de app hangt niet meer
|
||||
# af van de vraag of die twee gereedschappen in de nginx-image zitten.
|
||||
# Waarom een tweede container en niet een lus in de server hieronder: dit werk
|
||||
# is een programma. Een certificaatdatum uitlezen, een JSON-RPC-verzoek doen,
|
||||
# een geschiedenis bijhouden en een keuze valideren zijn geen dingen die je met
|
||||
# openssl en nc aan elkaar knoopt zonder dat het stil verkeerde antwoorden gaat
|
||||
# geven.
|
||||
agent:
|
||||
# TODO (fase 4 van het plan Appstore): pinnen op de multi-arch index-digest,
|
||||
# te bepalen met `docker buildx imagetools inspect python:3-alpine` op de
|
||||
# Umbrel. Geldt voor beide images in dit bestand.
|
||||
image: python:3-alpine
|
||||
image: sc.kamenier-hamer.nl/sysop/electrum-gate:0.1.0
|
||||
restart: on-failure
|
||||
command:
|
||||
- python3
|
||||
- /app/agent.py
|
||||
environment:
|
||||
# Het adres van de Electrum-server die de gebruiker in umbrelOS gekozen
|
||||
# heeft. umbrelOS vult dit in op grond van de afhankelijkheid hieronder,
|
||||
@@ -28,6 +38,10 @@ services:
|
||||
GATE_ELECTRUM_HOST: ${APP_ELECTRS_NODE_IP}
|
||||
GATE_ELECTRUM_PORT: ${APP_ELECTRS_NODE_PORT}
|
||||
GATE_TLS_PORT: "50022"
|
||||
# De versie uit het manifest, voor de kop van de pagina. Die stond tot
|
||||
# 0.0.29 rechtstreeks in de pagina, ingevuld door umbreld; nu de pagina in
|
||||
# de image zit, geeft de agent hem door via status.json.
|
||||
GATE_APP_VERSION: ${APP_VERSION}
|
||||
# De zelfcontrole: de agent verbindt met de TLS-poort van de server
|
||||
# hieronder en maakt de handdruk af. Niet localhost: dat is een andere
|
||||
# container en dus een andere netwerk-namespace.
|
||||
@@ -57,19 +71,13 @@ services:
|
||||
# bron beschrijfbaar is, hoort te staan naast de mount die dat toestaat.
|
||||
# Leeg zetten schakelt uploaden uit.
|
||||
GATE_UPLOAD_DIR: /certs/own
|
||||
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
|
||||
# De gedeelde toestand: status.json, cert.conf, de herlaadvlag en de
|
||||
# gekozen certificaat-id. Beide containers zitten hierin.
|
||||
#
|
||||
# Onder data/ en niet naast de templates, want dat is wat andere apps doen:
|
||||
# electrs mount ${APP_DATA_DIR}/data/electrs, mempool ${APP_DATA_DIR}/data.
|
||||
# Verplaatst op 20-08-2026 met het oog op publicatie in de officiele store.
|
||||
# Onder data/, want dat is wat andere apps doen: electrs mount
|
||||
# ${APP_DATA_DIR}/data/electrs, mempool ${APP_DATA_DIR}/data. Verplaatst op
|
||||
# 20-08-2026 met het oog op publicatie in de officiele store.
|
||||
- ${APP_DATA_DIR}/data/runtime:/var/lib/gate
|
||||
# De certificaatbronnen. Zoraxy alleen lezen: dat zijn de certificaten van
|
||||
# een andere app en die raakt deze app niet aan.
|
||||
@@ -88,17 +96,18 @@ services:
|
||||
# deze regel aanzetten en de naam toevoegen aan GATE_CERT_SOURCES.
|
||||
#
|
||||
# - ${UMBREL_ROOT}/app-data/nginx-proxy-manager/data/letsencrypt/live:/certs/npm:ro
|
||||
command:
|
||||
- python
|
||||
- /app/agent.py
|
||||
|
||||
# nginx: de pagina op poort 80 achter de app_proxy, en de TLS-poort 50022 zodra
|
||||
# de agent een certificaat gekozen heeft. Het startscript is entrypoint.sh in de
|
||||
# image; tot 0.0.29 stond dat hier als command-blok van ruim honderd regels, met
|
||||
# elk dollarteken verdubbeld. Wat het doet staat daar uitgelegd.
|
||||
server:
|
||||
image: nginx:alpine
|
||||
image: sc.kamenier-hamer.nl/sysop/electrum-gate:0.1.0
|
||||
restart: on-failure
|
||||
# De agent moet er zijn voordat nginx start, want de proxy_pass naar
|
||||
# http://agent:8000 wordt bij het starten opgelost en een onbekende naam
|
||||
# laat nginx afbreken. Op de cert.conf van de agent wordt niet gewacht: de
|
||||
# pagina komt hoe dan ook omhoog, zie het command-blok hieronder.
|
||||
# De agent moet er zijn voordat nginx start, want de proxy_pass naar de agent
|
||||
# wordt bij het starten opgelost en een onbekende naam laat nginx afbreken.
|
||||
# Op de cert.conf van de agent wordt niet gewacht: de pagina komt hoe dan ook
|
||||
# omhoog, zie entrypoint.sh.
|
||||
depends_on:
|
||||
- agent
|
||||
ports:
|
||||
@@ -111,22 +120,13 @@ services:
|
||||
# buiten is toch al een andere, want die staat in de router doorgestuurd.
|
||||
# Beslist 19-08-2026, open punt 1 van het plan Configuratie.
|
||||
- "50022:50022"
|
||||
environment:
|
||||
# Dezelfde twee als bij de agent. entrypoint.sh vult ze in het stream-blok
|
||||
# in, want nginx kan in een proxy_pass geen omgevingsvariabele lezen. Tot
|
||||
# 0.0.29 deed umbreld dat via stream.conf.template.
|
||||
GATE_ELECTRUM_HOST: ${APP_ELECTRS_NODE_IP}
|
||||
GATE_ELECTRUM_PORT: ${APP_ELECTRS_NODE_PORT}
|
||||
volumes:
|
||||
# Beide door umbrelOS ingevuld uit een .template bij het starten. De
|
||||
# pagina wordt als los bestand gemount en niet als map: de bron staat in
|
||||
# de app-root, want alleen daar wordt hij bij een update ververst.
|
||||
- ${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.
|
||||
# De pagina valt terug op een ingebouwd merkje als de mount er niet is.
|
||||
- ${APP_DATA_DIR}/icon.png:/usr/share/nginx/html/icon.png:ro
|
||||
# Het stream-blok, dus de TLS-poort zelf. Het staat los van nginx.conf
|
||||
# omdat nginx niet start met een 'listen ssl' zonder certificaat, en het
|
||||
# command-blok hieronder zet het pas in /var/lib/gate/tls/ zodra de agent
|
||||
# een certificaat gekozen heeft. Tot die tijd draait alleen de pagina.
|
||||
- ${APP_DATA_DIR}/stream.conf:/etc/nginx/stream.conf:ro
|
||||
# Dezelfde gedeelde toestand als de agent. nginx leest hier cert.conf en
|
||||
# status.json, en schrijft de sessielog.
|
||||
- ${APP_DATA_DIR}/data/runtime:/var/lib/gate
|
||||
@@ -134,131 +134,3 @@ services:
|
||||
# welk pad in cert.conf komt, maar nginx moet het bestand kunnen openen.
|
||||
- ${UMBREL_ROOT}/app-data/zoraxy/data/config/conf/certs:/certs/zoraxy:ro
|
||||
- ${APP_DATA_DIR}/data/certs:/certs/own:ro
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -eu
|
||||
mkdir -p /var/lib/gate
|
||||
|
||||
# Het log_format van de stream-sessies wordt hier geschreven en niet in
|
||||
# nginx.conf.template. Een log_format bestaat uit nginx-variabelen met
|
||||
# een dollarteken, en de template-invulling van umbreld zou die
|
||||
# stilzwijgend leegmaken. In dit blok is een dollarteken als $$ te
|
||||
# ontsnappen, dus hier kan het wel.
|
||||
#
|
||||
# Let op wat er NIET in staat: geen client-adres en geen bronpoort. Dat
|
||||
# is precies het soort gegeven dat deze app van het netwerk af houdt, en
|
||||
# om te zien dat het werkt is het niet nodig.
|
||||
cat > /var/lib/gate/stream-log.conf <<'CONF'
|
||||
log_format gate '$$time_iso8601 $$status $$bytes_received $$bytes_sent $$session_time';
|
||||
access_log /var/lib/gate/stream.log gate;
|
||||
CONF
|
||||
|
||||
# De TLS-poort aan of uit zetten, naar de toestand van cert.conf.
|
||||
#
|
||||
# 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.
|
||||
# Zonder Zoraxy, of met twee kandidaten, hing de app daarmee vast op een
|
||||
# keuze die nergens te maken was.
|
||||
#
|
||||
# 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
|
||||
# deze map op met een jokerteken; die matcht dan niets en dat is geen
|
||||
# fout.
|
||||
mkdir -p /var/lib/gate/tls
|
||||
#
|
||||
# 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 /var/lib/gate/cert.conf ]; then
|
||||
# Kopieren en niet linken: nginx opent dit pad als de gebruiker
|
||||
# nginx, en een symlink naar een read-only mount is nodeloos fragiel.
|
||||
if cp /etc/nginx/stream.conf /var/lib/gate/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 /var/lib/gate/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, en het open punt uit §4e van het
|
||||
# plan Webinterface: hoe die toestand binnen de container af te lezen is.
|
||||
#
|
||||
# Antwoord: /proc/net/tcp. Dat geldt per netwerk-namespace, dus dit ziet
|
||||
# alleen de sockets van deze container, en daarom staat deze 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 hierboven.
|
||||
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 > /var/lib/gate/sessions
|
||||
|
||||
# De herlaadlus. De agent kan nginx niet zelf herladen: dat zou de
|
||||
# Docker-socket vragen en die is er bewust uit. In plaats daarvan zet hij
|
||||
# een vlagbestand neer en herlaadt nginx zichzelf. Een reload leest het
|
||||
# nieuwe certificaat in zonder bestaande verbindingen te verbreken.
|
||||
#
|
||||
# Deze lus draait op de achtergrond en 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.
|
||||
(
|
||||
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 > /var/lib/gate/sessions.tmp
|
||||
mv /var/lib/gate/sessions.tmp /var/lib/gate/sessions
|
||||
|
||||
if [ -f /var/lib/gate/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 /var/lib/gate/reload /var/lib/gate/reload.done
|
||||
# Voor de herlading, niet erna: een reload leest de configuratie
|
||||
# opnieuw, dus het stream-blok moet er dan al liggen.
|
||||
sync_tls
|
||||
# De herlading mag niet met 'set -e' meegaan. Een certificaat dat
|
||||
# nginx niet aanneemt laat de reload falen, en dan zou deze lus
|
||||
# verdwijnen: geen enkele latere wijziging wordt dan nog opgepikt,
|
||||
# zonder dat er iets te zien is.
|
||||
if ! nginx -s reload; then
|
||||
echo "Herladen mislukt; nginx houdt de vorige configuratie."
|
||||
fi
|
||||
elif [ -f /var/lib/gate/cert.conf ] && [ ! -f /var/lib/gate/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
|
||||
if ! nginx -s reload; then
|
||||
echo "Herladen mislukt; nginx houdt de vorige configuratie."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
) &
|
||||
|
||||
exec nginx -g 'daemon off;'
|
||||
|
||||
Reference in New Issue
Block a user