From d6925e3385bd30adfb0dc0719a9c804a2bad77d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Wed, 1 Jul 2026 15:58:30 +0200 Subject: [PATCH] bank: merchant-model start scripts + configs for taler-bank-hacktivism --- configs/taler-bank/README.md | 84 ++++++++++ configs/taler-bank/bank-overrides.conf | 39 +++++ .../libeufin-bank.conf.includes.txt | 7 + scripts/taler-bank/README.md | 35 +++++ scripts/taler-bank/check_bank-health.sh | 48 ++++++ scripts/taler-bank/start_bank.sh | 109 +++++++++++++ .../start_base_services_for_taler_bank.sh | 148 ++++++++++++++++++ 7 files changed, 470 insertions(+) create mode 100644 configs/taler-bank/README.md create mode 100644 configs/taler-bank/bank-overrides.conf create mode 100644 configs/taler-bank/libeufin-bank.conf.includes.txt create mode 100644 scripts/taler-bank/README.md create mode 100755 scripts/taler-bank/check_bank-health.sh create mode 100755 scripts/taler-bank/start_bank.sh create mode 100755 scripts/taler-bank/start_base_services_for_taler_bank.sh diff --git a/configs/taler-bank/README.md b/configs/taler-bank/README.md new file mode 100644 index 0000000..840c234 --- /dev/null +++ b/configs/taler-bank/README.md @@ -0,0 +1,84 @@ +# taler-bank (container `taler-bank-hacktivism`) + +Image: `docker.io/library/debian:trixie` + package `libeufin-bank` +Publish: **9012/tcp only** +Labels: `org.hacktivism.service=taler-bank`, `host_port=9012`, `site=bank.hacktivism.ch`, `currency=GOA` + +## Role + +Regional **GOA Exploration Bank** for GNU Taler: + +- **No IBAN**, no libeufin-nexus, no fiat conversion (`ALLOW_CONVERSION = no`) +- **`WIRE_TYPE = x-taler-bank`** (payto://x-taler-bank/bank.hacktivism.ch/…) +- Wallet default exchange: `https://exchange.hacktivism.ch/` +- Mobile withdraw: Bank SPA → login → **Withdraw** → Taler Wallet (QR / deeplink) + +## Config layout (merchant/exchange pattern) + +| Path in container | Mirror | +|-------------------|--------| +| `libeufin-bank.conf` | includes: `libeufin-bank.conf.includes.txt` | +| **`bank-overrides.conf`** | **`bank-overrides.conf`** | + +## Manual start + +| Path | User | Role | +|------|------|------| +| `/root/start_base_services_for_taler_bank.sh` | **root** | logs, postgres, DB ensure → shell as `libeufin-bank` | +| `/usr/local/bin/start_bank.sh` | **libeufin-bank** | start / `--restart` `libeufin-bank serve` | +| `/usr/local/bin/check_bank-health.sh` | any | health | + +## Ports / Caddy + +| Port | Role | +|------|------| +| 9012 | libeufin-bank HTTP (podman pasta) | +| 9001 | Caddy HTTPS `bank.hacktivism.ch` → 127.0.0.1:9012 | + +**DNS:** A/AAAA for `bank.hacktivism.ch` → koopa public IP (required for TLS/ACME and phones). + +## Accounts (typical) + +| Login | Purpose | +|-------|---------| +| `admin` | bank admin (password on host `/root/bank-admin-password.txt`) | +| `exchange` | Taler exchange wire account (`is_taler_exchange`) | +| `explorer` | demo user for phone withdraw (`debit_threshold` for regional GOA) | + +## Mobile withdraw (GOA) + +1. Install **GNU Taler Wallet** (Android / iOS / browser). +2. Open **https://bank.hacktivism.ch/** (after DNS + cert work). +3. Login as `explorer` (or registered user with debt headroom). +4. **Withdraw** → amount in GOA → open / scan with wallet. +5. Wallet uses `DEFAULT_EXCHANGE` = exchange.hacktivism.ch. + +Exchange must have wire account pointing at this bank (payto + wire gateway credentials) and offline **enable-account** for `/keys` withdraw to fully work. + +**Details (diagram `/keys` ↔ Wire ↔ Account):** +`configs/taler-exchange/README.md` → Abschnitt **„/keys, Wire und Account“**. + +## Public landing (withdraw QR) + +English intro + QR: **`https://bank.hacktivism.ch/`** → `/intro/` +Details: `configs/bank-landing/`. Refresh demo URI: `scripts/taler-bank/make-demo-withdraw-qr.sh`. + +### Wallet says “transfer money” — auto-confirm + +Normal Taler bank flow after the wallet selects the exchange: + +1. Withdrawal status → **`selected`** +2. Bank must **`POST …/withdrawals/$ID/confirm`** → wires GOA user → exchange +3. Exchange **wirewatch** sees credit → wallet can finish withdraw + +For the demo QR (`explorer`), automate step 2: + +```bash +# once +/root/auto-confirm-withdrawals.sh +# loop (e.g. every 4s) +nohup /root/auto-confirm-withdrawals.sh --loop 4 >>/var/log/auto-confirm-withdrawals.log 2>&1 & +``` + +Script: `scripts/taler-bank/auto-confirm-withdrawals.sh` +Watches IDs in `/var/www/bank-landing/withdraw.uri` and `withdraw-watch.ids`. diff --git a/configs/taler-bank/bank-overrides.conf b/configs/taler-bank/bank-overrides.conf new file mode 100644 index 0000000..83fb887 --- /dev/null +++ b/configs/taler-bank/bank-overrides.conf @@ -0,0 +1,39 @@ +# Regional GOA bank — no IBAN / Nexus / fiat conversion. +# Container: /etc/libeufin/bank-overrides.conf +# Inlined last from /etc/libeufin/libeufin-bank.conf via @inline@ bank-overrides.conf + +[libeufin-bank] +CURRENCY = GOA +WIRE_TYPE = x-taler-bank +BASE_URL = https://bank.hacktivism.ch/ +NAME = "GOA Exploration Bank" +SERVE = tcp +PORT = 9012 +BIND_TO = 0.0.0.0 +DEFAULT_DEBT_LIMIT = GOA:100000 +ALLOW_REGISTRATION = yes +ALLOW_CONVERSION = no +REGISTRATION_BONUS = GOA:0 +WIRE_TRANSFER_FEES = GOA:0 +DEFAULT_EXCHANGE = https://exchange.hacktivism.ch/ + +[currency-goa] +ENABLED = YES +name = "GOA exploration currency" +code = GOA +fractional_input_digits = 8 +fractional_normal_digits = 0 +fractional_trailing_zero_digits = 0 +alt_unit_names_are_symbols = NO +alt_unit_names = {"24":"Yotta-GOA","21":"Zetta-GOA","18":"Exa-GOA","15":"Peta-GOA","12":"Tera-GOA","9":"Giga-GOA","6":"Mega-GOA","3":"Kilo-GOA","0":"GOA","-1":"Deci-GOA","-2":"Centi-GOA","-3":"Milli-GOA","-6":"Micro-GOA","-7":"Deci-Micro-GOA","-8":"Atomic-GOA"} + +[currency-kudos] +ENABLED = NO +[currency-testkudos] +ENABLED = NO +[currency-euro] +ENABLED = NO +[currency-swiss-francs] +ENABLED = NO +[currency-netzbon] +ENABLED = NO diff --git a/configs/taler-bank/libeufin-bank.conf.includes.txt b/configs/taler-bank/libeufin-bank.conf.includes.txt new file mode 100644 index 0000000..735e78f --- /dev/null +++ b/configs/taler-bank/libeufin-bank.conf.includes.txt @@ -0,0 +1,7 @@ +# /etc/libeufin/libeufin-bank.conf (package entrypoint) +# Site overrides last: +@inline@ bank-overrides.conf + +# DB (package default in /usr/share/libeufin/config.d/bank.conf): +# [libeufin-bankdb-postgres] +# CONFIG = postgres:///libeufin diff --git a/scripts/taler-bank/README.md b/scripts/taler-bank/README.md new file mode 100644 index 0000000..2c66b07 --- /dev/null +++ b/scripts/taler-bank/README.md @@ -0,0 +1,35 @@ +# taler-bank scripts + +Container: **`taler-bank-hacktivism`** (libeufin-bank, GOA, **no IBAN**). + +| File | Container path | User | +|------|----------------|------| +| `start_base_services_for_taler_bank.sh` | `/root/` | **root** | +| `start_bank.sh` | `/usr/local/bin/` | **libeufin-bank** | +| `check_bank-health.sh` | `/usr/local/bin/` | libeufin-bank / any | +| `landing-stats.sh` | `/usr/local/bin/` | root (in container) — writes `/var/www/bank-landing/stats.json` | +| `landing-stats-install.sh` | host only | root/podman — copies + runs + optional cron | + +## Usage + +```bash +# root in container +./start_base_services_for_taler_bank.sh +# then as libeufin-bank in /usr/local/bin: +./start_bank.sh --restart +``` + +## Landing stats (inside container) + +```bash +# on koopa host — copy + run (writes /var/www/bank-landing/stats.json) +./landing-stats-install.sh +./landing-stats-install.sh --cron # every minute inside container (* * * * *) +./landing-stats-install.sh --run-only +``` + +Details + JSON schema: `configs/bank-landing/README.md`. + +## Config + +See `configs/taler-bank/`. diff --git a/scripts/taler-bank/check_bank-health.sh b/scripts/taler-bank/check_bank-health.sh new file mode 100755 index 0000000..c01ead2 --- /dev/null +++ b/scripts/taler-bank/check_bank-health.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Health check for manual libeufin-bank (same style as merchant/exchange health). +# Container: taler-hacktivism-bank + +CONF=/etc/libeufin/libeufin-bank.conf +PORT=$(grep -E '^\s*PORT\s*=' /etc/libeufin/bank-overrides.conf 2>/dev/null | tail -1 | awk -F= '{gsub(/ /,"",$2); print $2}') +PORT=${PORT:-9012} + +green() { echo -e "\e[32m$1\e[0m"; } +red() { echo -e "\e[31m$1\e[0m"; } +yellow() { echo -e "\e[33m$1\e[0m"; } + +fail=0 +ok() { green "[OK] $1"; } +bad() { red "[FAIL] $1"; fail=1; } + +echo "=== Taler Bank (libeufin-bank) Health Check ===" + +if pgrep -f 'libeufin-bank serve|MainKt serve|tech.libeufin.bank.MainKt' >/dev/null 2>&1; then + ok "process libeufin-bank serve" +else + bad "process libeufin-bank serve is NOT running" +fi + +if curl -sf -m 3 "http://127.0.0.1:${PORT}/config" >/dev/null 2>&1; then + ok "HTTP /config on 127.0.0.1:${PORT}" +else + bad "no HTTP response on 127.0.0.1:${PORT}/config" +fi + +if curl -sf -m 3 "http://127.0.0.1:${PORT}/taler-integration/config" >/dev/null 2>&1; then + ok "HTTP /taler-integration/config on 127.0.0.1:${PORT}" +else + yellow "[WARN] no HTTP response on /taler-integration/config" +fi + +if pg_isready >/dev/null 2>&1; then + ok "postgresql accepting connections" +else + yellow "[WARN] postgresql not ready (or pg_isready missing)" +fi + +if [ "$fail" -eq 0 ]; then + green "=== ALL CRITICAL CHECKS PASSED ===" + exit 0 +fi +red "=== SOME CHECKS FAILED ===" +exit 1 diff --git a/scripts/taler-bank/start_bank.sh b/scripts/taler-bank/start_bank.sh new file mode 100755 index 0000000..bbd812d --- /dev/null +++ b/scripts/taler-bank/start_bank.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# Start / restart libeufin-bank serve (manual, no systemd). +# Run as: libeufin-bank +# Same role as start_merchant.sh / start_exchange.sh. +# +# Container: taler-hacktivism-bank +# Prerequisite: /root/start_base_services_for_taler_bank.sh (as root) for postgres. +# +# Usage: +# start_bank.sh +# start_bank.sh --restart | -r +# start_bank.sh --help + +set -u + +usage() { + cat <<'EOF' +Usage: start_bank.sh [--restart|-r] [--help|-h] + + (default) Start libeufin-bank serve if not already running. + --restart Stop live serve process, then start cleanly. + Does not touch postgres (use /root/start_base_services_for_taler_bank.sh). +EOF +} + +DO_RESTART=0 +for arg in "$@"; do + case "$arg" in + --restart|-r) DO_RESTART=1 ;; + --help|-h) usage; exit 0 ;; + *) echo "Unknown option: $arg" >&2; usage >&2; exit 2 ;; + esac +done + +if [ "$(id -un)" != "libeufin-bank" ]; then + echo "This script must be run as user libeufin-bank" >&2 + exit 1 +fi + +CONF=/etc/libeufin/libeufin-bank.conf +LOG_DIR=/var/log/libeufin-bank +PORT=$(grep -E '^\s*PORT\s*=' /etc/libeufin/bank-overrides.conf 2>/dev/null | tail -1 | awk -F= '{gsub(/ /,"",$2); print $2}') +PORT=${PORT:-9012} + +# Match both wrapper name and the Java MainKt process that actually listens. +list_serve_pids() { + ps -eo pid=,stat=,args= 2>/dev/null | while read -r pid stat args; do + case "$stat" in Z*) continue ;; esac + case "$args" in + *start_bank.sh*) continue ;; + *check_bank-health*) continue ;; + esac + case "$args" in + *libeufin-bank\ serve*|/usr/bin/libeufin-bank\ serve*|*MainKt\ serve*|*tech.libeufin.bank.MainKt*) + echo "$pid" + ;; + esac + done | sort -u +} + +kill_serve() { + local pids + pids=$(list_serve_pids | tr '\n' ' ') + if [ -z "${pids// }" ]; then + echo "No live libeufin-bank serve to stop." + return 0 + fi + echo "Stopping PIDs: $pids" + # shellcheck disable=SC2086 + kill -TERM $pids 2>/dev/null || true + sleep 2 + local left + left=$(list_serve_pids | tr '\n' ' ') + if [ -n "${left// }" ]; then + echo "SIGKILL remaining: $left" + # shellcheck disable=SC2086 + kill -KILL $left 2>/dev/null || true + sleep 1 + fi + echo "libeufin-bank serve stopped." +} + +if [ "$DO_RESTART" -eq 1 ]; then + echo "=== restart: kill libeufin-bank serve ===" + kill_serve +fi + +echo "Start libeufin-bank serve (port $PORT):" +LOG_FILE="$LOG_DIR/libeufin-bank-$(date +%Y-%m-%d).log" +mkdir -p "$LOG_DIR" +touch "$LOG_FILE" 2>/dev/null || true + +if [ "$DO_RESTART" -eq 0 ] && [ -n "$(list_serve_pids)" ]; then + echo "libeufin-bank serve already running" +else + nohup libeufin-bank serve -c "$CONF" >>"$LOG_FILE" 2>&1 & + disown 2>/dev/null || true + sleep 2 +fi + +echo "Live processes:" +ps -eo pid,stat,args 2>/dev/null | grep -E 'libeufin-bank serve|MainKt serve' | grep -v grep | grep -v ' Z ' || true + +if [ -x /usr/local/bin/check_bank-health.sh ]; then + /usr/local/bin/check_bank-health.sh || exit 1 +elif [ -x ./check_bank-health.sh ]; then + ./check_bank-health.sh || exit 1 +fi +exit 0 diff --git a/scripts/taler-bank/start_base_services_for_taler_bank.sh b/scripts/taler-bank/start_base_services_for_taler_bank.sh new file mode 100755 index 0000000..e8873bb --- /dev/null +++ b/scripts/taler-bank/start_base_services_for_taler_bank.sh @@ -0,0 +1,148 @@ +#!/bin/bash +# Root: base services for manual libeufin-bank (like merchant/exchange start_base). +# Then interactive shell as libeufin-bank → run start_bank.sh there. +# +# Container: taler-hacktivism-bank +# +# Usage: +# /root/start_base_services_for_taler_bank.sh +# /root/start_base_services_for_taler_bank.sh --no-shell +# /root/start_base_services_for_taler_bank.sh --no-shell --start-bank +# /root/start_base_services_for_taler_bank.sh --no-shell --start-bank --restart + +set -e +CONF=/etc/libeufin/libeufin-bank.conf +LOG_DIR=/var/log/libeufin-bank +PWD_BIN=/usr/local/bin +BANK_STARTER=start_bank.sh +BANK_USER=libeufin-bank + +if [ "$(id -u)" -ne 0 ]; then + echo "Run as root" >&2 + exit 1 +fi + +NO_SHELL=0 +START_BANK=0 +BANK_RESTART=0 +for arg in "$@"; do + case "$arg" in + --no-shell|-n) NO_SHELL=1 ;; + --start-bank) START_BANK=1; NO_SHELL=1 ;; + --restart|-r) BANK_RESTART=1 ;; + --help|-h) + cat <<'EOF' +Usage: start_base_services_for_taler_bank.sh [options] + + (default) Start postgres + dirs, then interactive shell as libeufin-bank + --no-shell Start base services only, do not open a shell + --start-bank After base services, run /usr/local/bin/start_bank.sh as libeufin-bank + (implies --no-shell) + --restart With --start-bank: pass --restart to start_bank.sh +EOF + exit 0 + ;; + *) + echo "Unknown option: $arg" >&2 + exit 2 + ;; + esac +done + +# --- Debian postgresql defaults (pg_createcluster layout) --- +ensure_postgresql() { + echo " Debian perms on /etc/postgresql + data/log/run..." + if [ -d /etc/postgresql ]; then + chown -R root:postgres /etc/postgresql + find /etc/postgresql -type d -exec chmod 755 {} \; + find /etc/postgresql -type f -name '*.conf' -exec chmod 640 {} \; + fi + chown -R postgres:postgres /var/lib/postgresql /var/log/postgresql 2>/dev/null || true + mkdir -p /var/run/postgresql + chown postgres:postgres /var/run/postgresql + chmod 2775 /var/run/postgresql 2>/dev/null || chmod 775 /var/run/postgresql + + # Snakeoil TLS key: postgres fails if it cannot read the key (common in rootless images). + # Prefer fixing perms; if still broken, force ssl=off for local-only DB. + if [ -f /etc/ssl/private/ssl-cert-snakeoil.key ]; then + chown root:ssl-cert /etc/ssl/private/ssl-cert-snakeoil.key 2>/dev/null || true + chmod 640 /etc/ssl/private/ssl-cert-snakeoil.key 2>/dev/null || true + fi + for pgconf in /etc/postgresql/*/main/postgresql.conf; do + [ -f "$pgconf" ] || continue + if grep -qE '^\s*ssl\s*=' "$pgconf"; then + sed -i 's/^\s*ssl\s*=.*/ssl = off/' "$pgconf" + else + echo "ssl = off" >>"$pgconf" + fi + done + + if pg_isready -q 2>/dev/null; then + echo " already accepting connections" + pg_isready || true + return 0 + fi + + rm -f /var/run/postgresql/.s.PGSQL.*.lock 2>/dev/null || true + if ! pgrep -u postgres -x postgres >/dev/null 2>&1; then + rm -f /var/lib/postgresql/*/main/postmaster.pid 2>/dev/null || true + fi + + if command -v pg_ctlcluster >/dev/null 2>&1 && command -v pg_lsclusters >/dev/null 2>&1; then + while read -r ver name _rest; do + [ -n "$ver" ] || continue + echo " pg_ctlcluster $ver $name start" + pg_ctlcluster "$ver" "$name" start 2>/dev/null || true + done < <(pg_lsclusters --no-header 2>/dev/null || true) + fi + if ! pg_isready -q 2>/dev/null; then + if [ -x /etc/init.d/postgresql ]; then + /etc/init.d/postgresql start || true + else + service postgresql start || true + fi + fi + sleep 1 + pg_isready || true +} + +echo "Create log + data dirs... giving permission to ${BANK_USER}:" +mkdir -p "$LOG_DIR" /var/lib/libeufin-bank +chown "${BANK_USER}:${BANK_USER}" "$LOG_DIR" /var/lib/libeufin-bank +chmod 755 "$LOG_DIR" /var/lib/libeufin-bank + +echo "Start base services needed for GOA Exploration Bank." +echo "" + +echo "1. postgresql:" +ensure_postgresql +# ensure role + DB (postgres:///libeufin) — Debian createuser/createdb as postgres +su -s /bin/bash postgres -c "psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='libeufin-bank'\" | grep -q 1 || createuser -s libeufin-bank" || true +su -s /bin/bash postgres -c "psql -tc \"SELECT 1 FROM pg_database WHERE datname='libeufin'\" | grep -q 1 || createdb -O libeufin-bank libeufin" || true +pg_isready || true + +if [ "$START_BANK" -eq 1 ]; then + echo "" + echo "2. start_bank.sh as ${BANK_USER}:" + if [ ! -x "$PWD_BIN/$BANK_STARTER" ]; then + echo "missing $PWD_BIN/$BANK_STARTER" >&2 + exit 1 + fi + if [ "$BANK_RESTART" -eq 1 ]; then + runuser -u "$BANK_USER" -- "$PWD_BIN/$BANK_STARTER" --restart + else + runuser -u "$BANK_USER" -- "$PWD_BIN/$BANK_STARTER" + fi + exit $? +fi + +if [ "$NO_SHELL" -eq 1 ]; then + echo "Base services started (--no-shell). Next: runuser -u ${BANK_USER} -- $PWD_BIN/$BANK_STARTER [--restart]" + exit 0 +fi + +echo "2. Switching now to user ${BANK_USER}, in $PWD_BIN; find executable $BANK_STARTER there!" +echo "" +cd "$PWD_BIN" +# util-linux: -u and -s are mutually exclusive (same as exchange/merchant) +exec runuser -u "$BANK_USER" -- bash