From 65bcddc185d9504f2633176537d94c1d03f3e88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 18 Sep 2026 11:52:23 +0200 Subject: [PATCH 1/3] ops: goa-regio-ng default CTR for ensure/systemd bank+merchant apps --- .../order.conf | 2 +- configs/systemd/user/goa-regio-ng.service | 15 ++++++++++++++ .../user/goa-regio-ng.service.d/apps.conf | 4 ++++ configs/systemd/user/taler-bank-apps.service | 7 ++++--- .../systemd/user/taler-landing-stats.service | 4 ++-- .../systemd/user/taler-merchant-apps.service | 7 ++++--- scripts/taler-shared/ensure-taler-apps.sh | 12 ++++++----- .../taler-shared/install-ensure-taler-apps.sh | 20 +++++++++++++++---- 8 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 configs/systemd/user/goa-regio-ng.service create mode 100644 configs/systemd/user/goa-regio-ng.service.d/apps.conf diff --git a/configs/systemd/user/container-koopa-paivana.service.d/order.conf b/configs/systemd/user/container-koopa-paivana.service.d/order.conf index c0678cc..92b7e6f 100644 --- a/configs/systemd/user/container-koopa-paivana.service.d/order.conf +++ b/configs/systemd/user/container-koopa-paivana.service.d/order.conf @@ -1,4 +1,4 @@ [Unit] # Paivana needs merchant private API — start after merchant apps, not only container shell -After=taler-merchant-apps.service container-taler-hacktivism.service +After=taler-merchant-apps.service goa-regio-ng.service Wants=taler-merchant-apps.service diff --git a/configs/systemd/user/goa-regio-ng.service b/configs/systemd/user/goa-regio-ng.service new file mode 100644 index 0000000..ace6f55 --- /dev/null +++ b/configs/systemd/user/goa-regio-ng.service @@ -0,0 +1,15 @@ +[Unit] +Description=goa-regio-ng Taler AIO container (vanilla live 9010-9015) +Documentation=file:%h/src/koopa/koopa-admin-log/scripts/taler-shared/goa-regio-ng/README.md +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +RemainAfterExit=yes +TimeoutStartSec=120 +ExecStart=/usr/bin/podman start goa-regio-ng +ExecStop=/usr/bin/podman stop -t 20 goa-regio-ng + +[Install] +WantedBy=default.target diff --git a/configs/systemd/user/goa-regio-ng.service.d/apps.conf b/configs/systemd/user/goa-regio-ng.service.d/apps.conf new file mode 100644 index 0000000..332262d --- /dev/null +++ b/configs/systemd/user/goa-regio-ng.service.d/apps.conf @@ -0,0 +1,4 @@ +[Unit] +# Pull in in-container bank/merchant apps after the AIO shell is up +Wants=taler-bank-apps.service taler-merchant-apps.service +Before=taler-bank-apps.service taler-merchant-apps.service diff --git a/configs/systemd/user/taler-bank-apps.service b/configs/systemd/user/taler-bank-apps.service index 7eb3ef2..0dada1f 100644 --- a/configs/systemd/user/taler-bank-apps.service +++ b/configs/systemd/user/taler-bank-apps.service @@ -1,13 +1,14 @@ [Unit] -Description=Start Taler bank apps inside taler-hacktivism-bank +Description=Start Taler bank apps inside goa-regio-ng (auto-confirm + demo-withdraw) Documentation=file:%h/src/koopa/koopa-admin-log/scripts/taler-shared/ensure-taler-apps.sh -After=network-online.target container-taler-hacktivism-bank.service -Wants=network-online.target container-taler-hacktivism-bank.service +After=network-online.target goa-regio-ng.service +Wants=network-online.target goa-regio-ng.service [Service] Type=oneshot RemainAfterExit=yes TimeoutStartSec=300 +EnvironmentFile=-%h/.config/taler-landing/stack.env ExecStart=%h/.local/bin/ensure-taler-apps.sh bank [Install] diff --git a/configs/systemd/user/taler-landing-stats.service b/configs/systemd/user/taler-landing-stats.service index cc6bd88..f9e49b5 100644 --- a/configs/systemd/user/taler-landing-stats.service +++ b/configs/systemd/user/taler-landing-stats.service @@ -3,8 +3,8 @@ Description=Taler landing stats (bank full scan + exchange/merchant) Documentation=file:%h/src/koopa/koopa-admin-log/scripts/taler-landing/README.md After=network-online.target Wants=network-online.target -# Prefer after containers are up (ignore if unit names differ) -After=container-taler-hacktivism-bank.service container-taler-hacktivism.service +# Prefer after AIO container + in-container apps are up +After=goa-regio-ng.service After=taler-bank-apps.service taler-merchant-apps.service [Service] diff --git a/configs/systemd/user/taler-merchant-apps.service b/configs/systemd/user/taler-merchant-apps.service index 22deacd..e4d24bc 100644 --- a/configs/systemd/user/taler-merchant-apps.service +++ b/configs/systemd/user/taler-merchant-apps.service @@ -1,14 +1,15 @@ [Unit] -Description=Start Taler merchant apps inside taler-hacktivism +Description=Start Taler merchant apps inside goa-regio-ng Documentation=file:%h/src/koopa/koopa-admin-log/scripts/taler-shared/ensure-taler-apps.sh -After=network-online.target container-taler-hacktivism.service -Wants=network-online.target container-taler-hacktivism.service +After=network-online.target goa-regio-ng.service +Wants=network-online.target goa-regio-ng.service [Service] Type=oneshot RemainAfterExit=yes # Merchant start_base + helpers can take a while after cold boot TimeoutStartSec=300 +EnvironmentFile=-%h/.config/taler-landing/stack.env ExecStart=%h/.local/bin/ensure-taler-apps.sh merchant [Install] diff --git a/scripts/taler-shared/ensure-taler-apps.sh b/scripts/taler-shared/ensure-taler-apps.sh index 58dca1a..5fa1014 100644 --- a/scripts/taler-shared/ensure-taler-apps.sh +++ b/scripts/taler-shared/ensure-taler-apps.sh @@ -2,7 +2,8 @@ # Host (hernani@koopa): start Taler *apps inside* merchant/bank containers. # # Containers use CMD sleep infinity — podman start alone is not enough. -# Called by user systemd units after container-taler-*.service. +# Called by user systemd units after goa-regio-ng.service (vanilla AIO). +# Override MER_CTR/BANK_CTR via env or ~/.config/taler-landing/stack.env. # # Usage: # ensure-taler-apps.sh # merchant + bank + helpers @@ -11,8 +12,8 @@ # ensure-taler-apps.sh status set -euo pipefail -MER_CTR="${MER_CTR:-taler-hacktivism}" -BANK_CTR="${BANK_CTR:-taler-hacktivism-bank}" +MER_CTR="${MER_CTR:-goa-regio-ng}" +BANK_CTR="${BANK_CTR:-goa-regio-ng}" AUTO_LOOP_SECS="${AUTO_CONFIRM_LOOP_SECS:-2}" WAIT_SECS="${WAIT_SECS:-90}" @@ -140,8 +141,9 @@ ensure_bank_helpers() { } status() { - echo "=== containers ===" - podman ps -a --filter name='taler-hacktivism' --format '{{.Names}} {{.Status}}' + echo "=== containers (MER_CTR=$MER_CTR BANK_CTR=$BANK_CTR) ===" + podman ps -a --format '{{.Names}} {{.Status}}' \ + | awk '/goa-regio-ng|taler-hacktivism/ {print}' echo "=== merchant procs (sample) ===" podman exec "$MER_CTR" bash -lc "ps -eo args= | grep -E 'taler-merchant-httpd|postgres -D|nginx: master' | grep -v grep || true" 2>/dev/null || echo "(container down)" echo "=== bank procs (sample) ===" diff --git a/scripts/taler-shared/install-ensure-taler-apps.sh b/scripts/taler-shared/install-ensure-taler-apps.sh index 60f2367..83d589d 100644 --- a/scripts/taler-shared/install-ensure-taler-apps.sh +++ b/scripts/taler-shared/install-ensure-taler-apps.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash # Install ensure-taler-apps + user systemd units on this host (hernani@koopa). # Run from admin-log checkout or with ADMIN_LOG set. +# Defaults target goa-regio-ng (vanilla AIO). Legacy hacktivism drop-ins stay +# installable but are not required after cutover. set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" @@ -14,14 +16,20 @@ mkdir -p "$BIN_DST" "$UNIT_DST" install -m 0755 "$ADMIN_LOG/scripts/taler-shared/ensure-taler-apps.sh" \ "$BIN_DST/ensure-taler-apps.sh" +install -m 0644 "$UNIT_SRC/goa-regio-ng.service" "$UNIT_DST/" install -m 0644 "$UNIT_SRC/taler-merchant-apps.service" "$UNIT_DST/" install -m 0644 "$UNIT_SRC/taler-bank-apps.service" "$UNIT_DST/" +install -m 0644 "$UNIT_SRC/taler-landing-stats.service" "$UNIT_DST/" mkdir -p \ + "$UNIT_DST/goa-regio-ng.service.d" \ "$UNIT_DST/container-taler-hacktivism.service.d" \ "$UNIT_DST/container-taler-hacktivism-bank.service.d" \ "$UNIT_DST/container-koopa-paivana.service.d" +install -m 0644 "$UNIT_SRC/goa-regio-ng.service.d/apps.conf" \ + "$UNIT_DST/goa-regio-ng.service.d/apps.conf" +# Legacy drop-ins (harmless if old container units stay disabled) install -m 0644 "$UNIT_SRC/container-taler-hacktivism.service.d/apps.conf" \ "$UNIT_DST/container-taler-hacktivism.service.d/apps.conf" install -m 0644 "$UNIT_SRC/container-taler-hacktivism-bank.service.d/apps.conf" \ @@ -30,14 +38,18 @@ install -m 0644 "$UNIT_SRC/container-koopa-paivana.service.d/order.conf" \ "$UNIT_DST/container-koopa-paivana.service.d/order.conf" systemctl --user daemon-reload -systemctl --user enable taler-merchant-apps.service taler-bank-apps.service +systemctl --user enable goa-regio-ng.service \ + taler-merchant-apps.service taler-bank-apps.service echo "Installed:" echo " $BIN_DST/ensure-taler-apps.sh" +echo " $UNIT_DST/goa-regio-ng.service (enabled)" echo " $UNIT_DST/taler-{merchant,bank}-apps.service (enabled)" -echo " drop-ins: container-taler-hacktivism{,-bank}.service.d/apps.conf" -echo " drop-in: container-koopa-paivana.service.d/order.conf" +echo " $UNIT_DST/taler-landing-stats.service (After=goa-regio-ng)" +echo " drop-in: goa-regio-ng.service.d/apps.conf" +echo " drop-in: container-koopa-paivana.service.d/order.conf" echo -echo "Start now (if containers already up):" +echo "Start now (if container already up):" +echo " systemctl --user start goa-regio-ng.service" echo " systemctl --user start taler-merchant-apps.service taler-bank-apps.service" echo " $BIN_DST/ensure-taler-apps.sh status" From 281873a9553e5da212f30b2d2feede7c9aed3d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 18 Sep 2026 12:18:47 +0200 Subject: [PATCH 2/3] ops: GOA auto-confirm DB-selected withdrawals + debt-headroom filter --- .../taler-bank/auto-confirm-withdrawals.sh | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/scripts/taler-bank/auto-confirm-withdrawals.sh b/scripts/taler-bank/auto-confirm-withdrawals.sh index a46c523..c75817c 100755 --- a/scripts/taler-bank/auto-confirm-withdrawals.sh +++ b/scripts/taler-bank/auto-confirm-withdrawals.sh @@ -1,6 +1,11 @@ #!/bin/bash # Auto-confirm bank withdrawals for the community demo pool ONLY (explorer). # +# Discovery: (1) landing watch files (withdraw.uri / withdraw-watch.ids), +# (2) libeufin DB via local psql (selected && !confirmed && username=explorer). +# Watch-file-only misses webui/wallet-selected ops not in the last WATCH_MAX +# IDs — DB scan is required (same idea as testpaysan; here in-CTR, no podman). +# # Run once: auto-confirm-withdrawals.sh # Loop: auto-confirm-withdrawals.sh --loop [SECS] # @@ -9,6 +14,8 @@ # BANK_USER (default explorer) # BANK_PASS or /root/bank-explorer-password.txt # LANDING_DIR (default /var/www/bank-landing) +# DB_NAME (default libeufin) +# DB_LIMIT max selected IDs from DB per tick (default 40) # ALLOW_NON_EXPLORER=1 # WATCH_MAX max IDs kept in withdraw-watch.ids after prune (default 80) # QUIET=1 less skip noise (default 1 in --loop) @@ -22,6 +29,8 @@ LANDING_DIR="${LANDING_DIR:-/var/www/bank-landing}" WATCH_FILE="${LANDING_DIR}/withdraw-watch.ids" URI_FILE="${LANDING_DIR}/withdraw.uri" WATCH_MAX="${WATCH_MAX:-80}" +DB_NAME="${DB_NAME:-libeufin}" +DB_LIMIT="${DB_LIMIT:-40}" LOCK_FILE="${LOCK_FILE:-/var/run/auto-confirm-withdrawals.lock}" LOOP=0 SLEEP=2 @@ -96,6 +105,45 @@ known_ids() { fi } +# Selected, not yet confirmed, owner = explorer only (runs inside goa CTR). +db_selected_explorer_ids() { + if ! command -v psql >/dev/null 2>&1; then + qlog "psql missing — skip DB discovery" + return 0 + fi + if ! id -u postgres >/dev/null 2>&1; then + qlog "postgres user missing — skip DB discovery" + return 0 + fi + # Prefer affordable amounts (newest-first often hits oversized junk that + # exceeds explorer debt headroom → HTTP 409 Insufficient funds forever). + su -s /bin/bash postgres -c "psql -d ${DB_NAME} -At -c \" +WITH explorer AS ( + SELECT a.bank_account_id, a.has_debt, a.balance, a.max_debt + FROM libeufin_bank.bank_accounts a + JOIN libeufin_bank.customers c ON c.customer_id = a.owning_customer_id + WHERE c.username = '${USER}' + LIMIT 1 +), +headroom AS ( + SELECT CASE WHEN has_debt THEN (max_debt).val - (balance).val + ELSE (max_debt).val + (balance).val END AS val + FROM explorer +) +SELECT w.withdrawal_uuid::text +FROM libeufin_bank.taler_withdrawal_operations w +JOIN explorer e ON e.bank_account_id = w.wallet_bank_account +CROSS JOIN headroom h +WHERE w.selection_done = true + AND w.confirmation_done = false + AND w.aborted = false + AND (w.amount).val <= h.val +ORDER BY (w.amount).val ASC, (w.amount).frac ASC, w.creation_date DESC +LIMIT ${DB_LIMIT}; +\"" 2>/dev/null \ + | grep -E '^[0-9a-fA-F-]{36}$' || true +} + # Keep file small: drop confirmed/aborted; keep pending/selected + tail prune_watch() { [ -f "$WATCH_FILE" ] || return 0 @@ -172,16 +220,17 @@ once() { return 1 fi - # Cap work per loop: last WATCH_MAX ids only (newest at end of file) + # Watch files (landing) + DB selected(!confirmed) explorer ops ids=$( { [ -f "$URI_FILE" ] && basename "$(tr -d '\n' <"$URI_FILE")" if [ -f "$WATCH_FILE" ]; then grep -E '^[0-9a-fA-F-]{36}$' "$WATCH_FILE" | awk 'NF && !seen[$0]++' | tail -n "$WATCH_MAX" fi + db_selected_explorer_ids } | awk 'NF && !seen[$0]++' ) if [ -z "$ids" ]; then - qlog "no withdrawal ids to watch" + qlog "no withdrawal ids (selected or watched)" return 0 fi From 7691ac3065a97b32be211c50d52e7a9b4a60fa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 18 Sep 2026 13:05:31 +0200 Subject: [PATCH 3/3] ops: taler-bank auto-confirm + systemd units + README --- scripts/taler-bank/README.md | 10 +++-- .../taler-bank/auto-confirm-withdrawals.sh | 41 +++++++++++++++++++ .../taler-bank/install-demo-withdraw-api.sh | 32 ++++++++++----- .../goa-auto-confirm-withdrawals.service | 19 +++++++++ 4 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 scripts/taler-bank/systemd/goa-auto-confirm-withdrawals.service diff --git a/scripts/taler-bank/README.md b/scripts/taler-bank/README.md index 063261e..d667a21 100644 --- a/scripts/taler-bank/README.md +++ b/scripts/taler-bank/README.md @@ -1,6 +1,6 @@ # taler-bank scripts -Container: **`taler-hacktivism-bank`** (libeufin-bank, GOA, **no IBAN**). +Container: **`goa-regio-ng`** (libeufin-bank, GOA, **no IBAN**; legacy name `taler-hacktivism-bank`). | File | Container path | User | |------|----------------|------| @@ -84,20 +84,24 @@ via `./install-demo-withdraw-api.sh` first. ### Auto-confirm (explorer only) ```bash -# loop inside container — refuses non-explorer unless ALLOW_NON_EXPLORER=1 +# preferred: systemd inside CTR (Restart=always) +systemctl status goa-auto-confirm-withdrawals.service +# manual loop — refuses non-explorer unless ALLOW_NON_EXPLORER=1 auto-confirm-withdrawals.sh --loop 2 ``` Only confirms withdrawals owned by **`explorer`** when status is `selected` (community demo path). Does not confirm arbitrary customer withdraws. +Skips amounts above explorer debt headroom (otherwise libeufin returns HTTP 409). **Stable ops (via `koopa-external` if LAN `koopa` is down):** +- Unit: `goa-auto-confirm-withdrawals.service` (install script enables it) - One process only (`flock` on `/var/run/auto-confirm-withdrawals.lock`) - `QUIET=1` + summary `tick checked=… selected=…` each loop - Watch list capped (`WATCH_MAX=80`, prune of bloated `withdraw-watch.ids`) - Status via `taler-integration/withdrawal-operation/{id}` -- Reinstall: `./install-demo-withdraw-api.sh` on host with podman access to `taler-hacktivism-bank` +- Reinstall: `BANK_CONTAINER=goa-regio-ng ./install-demo-withdraw-api.sh` ## Config diff --git a/scripts/taler-bank/auto-confirm-withdrawals.sh b/scripts/taler-bank/auto-confirm-withdrawals.sh index c75817c..1db8d32 100755 --- a/scripts/taler-bank/auto-confirm-withdrawals.sh +++ b/scripts/taler-bank/auto-confirm-withdrawals.sh @@ -63,6 +63,11 @@ if [ "$LOOP" -eq 1 ]; then if command -v flock >/dev/null 2>&1; then if ! flock -n 9; then echo "auto-confirm already running (lock $LOCK_FILE) — exit" + # Under systemd Restart=always, exit 0 on lock contention flaps the unit + # (activating/auto-restart) while the holder is outside the cgroup. + if [ -n "${INVOCATION_ID:-}" ]; then + exit 75 + fi exit 0 fi fi @@ -105,6 +110,37 @@ known_ids() { fi } +# True if WD amount fits explorer debt headroom (or psql unavailable → allow). +id_affordable() { + local wid="$1" + if ! command -v psql >/dev/null 2>&1 || ! id -u postgres >/dev/null 2>&1; then + return 0 + fi + local ok + ok=$(su -s /bin/bash postgres -c "psql -d ${DB_NAME} -At -c \" +WITH explorer AS ( + SELECT a.bank_account_id, a.has_debt, a.balance, a.max_debt + FROM libeufin_bank.bank_accounts a + JOIN libeufin_bank.customers c ON c.customer_id = a.owning_customer_id + WHERE c.username = '${USER}' + LIMIT 1 +), +headroom AS ( + SELECT CASE WHEN has_debt THEN (max_debt).val - (balance).val + ELSE (max_debt).val + (balance).val END AS val + FROM explorer +) +SELECT 1 +FROM libeufin_bank.taler_withdrawal_operations w +JOIN explorer e ON e.bank_account_id = w.wallet_bank_account +CROSS JOIN headroom h +WHERE w.withdrawal_uuid = '${wid}'::uuid + AND (w.amount).val <= h.val +LIMIT 1; +\"" 2>/dev/null | tr -d '[:space:]') + [ "$ok" = "1" ] +} + # Selected, not yet confirmed, owner = explorer only (runs inside goa CTR). db_selected_explorer_ids() { if ! command -v psql >/dev/null 2>&1; then @@ -184,6 +220,11 @@ confirm_one() { case "$st" in selected) + # Watch-list IDs bypass DB headroom filter — skip oversized (HTTP 409 forever). + if ! id_affordable "$wid"; then + qlog "skip $wid selected but over explorer headroom" + return 0 + fi log "confirming $wid as $USER (status=selected) ..." conf=$(curl -sS -m 15 -o /tmp/acw-conf.out -w '%{http_code}' \ -X POST \ diff --git a/scripts/taler-bank/install-demo-withdraw-api.sh b/scripts/taler-bank/install-demo-withdraw-api.sh index 66b5a37..b220030 100755 --- a/scripts/taler-bank/install-demo-withdraw-api.sh +++ b/scripts/taler-bank/install-demo-withdraw-api.sh @@ -4,11 +4,15 @@ # ./scripts/taler-bank/install-demo-withdraw-api.sh set -euo pipefail ROOT=$(cd "$(dirname "$0")" && pwd) -CTR="${BANK_CONTAINER:-taler-hacktivism-bank}" +CTR="${BANK_CONTAINER:-goa-regio-ng}" podman cp "$ROOT/demo-withdraw-api.py" "$CTR:/usr/local/bin/demo-withdraw-api.py" podman cp "$ROOT/auto-confirm-withdrawals.sh" "$CTR:/usr/local/bin/auto-confirm-withdrawals.sh" podman cp "$ROOT/refresh-demo-withdraw.sh" "$CTR:/usr/local/bin/refresh-demo-withdraw.sh" +if [ -f "$ROOT/systemd/goa-auto-confirm-withdrawals.service" ]; then + podman cp "$ROOT/systemd/goa-auto-confirm-withdrawals.service" \ + "$CTR:/etc/systemd/system/goa-auto-confirm-withdrawals.service" +fi podman exec -u root "$CTR" chmod 755 \ /usr/local/bin/demo-withdraw-api.py \ /usr/local/bin/auto-confirm-withdrawals.sh \ @@ -94,9 +98,7 @@ ps -eo pid=,args= | awk "/demo-withdraw-api\\.py/ && !/awk/" || { tail -20 /var/log/demo-withdraw-api.log >&2 || true exit 1 } -# stop auto-confirm by pid -ps -eo pid=,args= | awk "/auto-confirm-withdrawals\\.sh --loop/ && !/awk/ {print \$1}" | while read p; do kill \$p 2>/dev/null || true; done -sleep 0.5 +# Prefer systemd unit (Restart=always). Fall back to nohup if systemctl missing. if [ -f /var/log/auto-confirm-withdrawals.log ]; then sz=$(wc -c > /var/log/auto-confirm-withdrawals.log fi fi -nohup env QUIET=1 WATCH_MAX=80 \ - /usr/local/bin/auto-confirm-withdrawals.sh --loop 2 \ - >>/var/log/auto-confirm-withdrawals.log 2>&1 /dev/null || true; done +sleep 0.5 +if command -v systemctl >/dev/null 2>&1 && [ -f /etc/systemd/system/goa-auto-confirm-withdrawals.service ]; then + systemctl daemon-reload + systemctl enable --now goa-auto-confirm-withdrawals.service + systemctl restart goa-auto-confirm-withdrawals.service + systemctl --no-pager --full status goa-auto-confirm-withdrawals.service | head -20 +else + nohup env QUIET=1 WATCH_MAX=80 \ + /usr/local/bin/auto-confirm-withdrawals.sh --loop 2 \ + >>/var/log/auto-confirm-withdrawals.log 2>&1