release 1.13.0: devtesting fake-franken CHF via rusty

New phase devtesting (aliases franken/fake-franken) probes
taler-devtesting on rusty.taler-ops.ch: geniban + fake-incoming CHF
to the exchange IBAN. Nexus bounce without reserve pub is OK for the
plumbing probe. Stage host-agent can include the phase.
This commit is contained in:
Hernâni Marques 2026-07-19 02:28:35 +02:00
parent e67ede881c
commit f17200da3f
No known key found for this signature in database
GPG key ID: CB5738652768F7E9
9 changed files with 227 additions and 2 deletions

View file

@ -421,6 +421,20 @@ Soft mode only with `MONPAGES_REQUIRE_PUBLIC=0` (escape hatch).
If pages are missing publicly: `sudo /home/hernani/koopa-caddy/apply-monitoring-live.sh` on koopa
(FP: Infomaniak vhost for `/monitoring*`).
## Devtesting · fake-franken CHF (v1.13+)
Phase **`devtesting`** (aliases: `franken`, `fake-franken`) exercises the
Taler Operations **rusty** host (`taler-devtesting` forced command):
```bash
./taler-monitoring.sh -d stage.taler-ops.ch --currency CHF devtesting
```
Needs SSH to `devtesting@rusty.taler-ops.ch` (Host `taler-rusty-devtesting`).
Probes: CLI help, `geniban` (CH IBAN), `fake-incoming` CHF to exchange credit
IBAN. A nexus bounce *missing reserve public key* is **OK** for the plumbing
probe (no real wallet reserve required).
## Version on monitoring pages (v1.3+ / sticky bar)
The sticky bar shows the suite **tag** (e.g. `v1.11.0`) and links to that tag on Forgejo.

View file

@ -193,3 +193,13 @@ totals: 40 OK, 1 ERROR, 2 WARN, 5 INFO
`NO_COLOR=1` or `CLICOLOR=0` disables boxes/colours (ASCII `[ OK ]` + `-- group --` headers).
Always use `printf --` friendly plain headers when colour is off (leading `---` is not a printf option).
## devtesting · fake-franken (v1.13+)
| ID prefix | What |
|-----------|------|
| **devtesting.cli-** | SSH to rusty · `taler-devtesting` CLI |
| **devtesting.geniban-** | synthetic CH IBAN |
| **devtesting.credit-payto-** | exchange credit IBAN for nexus |
| **devtesting.fake-incoming-** | CHF IN via nexus; bounce missing reserve pub = plumbing OK |

View file

@ -1 +1 @@
1.12.0
1.13.0

View file

@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre
| Tag | Date (UTC) | Notes |
|-----|------------|--------|
| **v1.13.0** | 2026-07-19 | **Feature:** phase **devtesting** / **franken** — fake-franken CHF via `rusty.taler-ops.ch` (`taler-devtesting` geniban + fake-incoming); stage host-agent can include it. |
| **v1.12.0** | 2026-07-19 | **Feature:** collapsible **monitoring env context** (agent header: env_file, suite pin, domain/hosts/flags) — `<details>` default closed; summary shows agent · domain · suite. |
| **v1.11.0** | 2026-07-19 | **Feature:** sticky **error/warn filter** — click counts to show only error or only warn log lines; sticky bar + overviews stay; `#filter-error` / `#filter-warn` (+ `#first-*` still works). |
| **v1.10.11** | 2026-07-19 | **Bugfix:** surface/aptdeploy wrappers **force** MON_HOSTS+PHASES+MONPAGES_INVENTORY (env cannot expand job to bank/exchange); wrap more mon knobs. |

147
check_devtesting.sh Executable file
View file

@ -0,0 +1,147 @@
#!/usr/bin/env bash
# check_devtesting.sh — fake-franken (CHF) via rusty.taler-ops.ch taler-devtesting
#
# Requires SSH to Host DEVTESTING_SSH (default: taler-rusty-devtesting →
# rusty.taler-ops.ch user devtesting, forced command taler-devtesting).
#
# Probes:
# 1) CLI reachable (fake-incoming --help)
# 2) geniban → Swiss IBAN
# 3) fake-incoming CHF amount to exchange credit IBAN (nexus IN)
# Bounce "missing reserve public key" = plumbing OK (no real wallet reserve)
# Full credit needs a real reserve pub as --subject (optional later)
#
# Env:
# DEVTESTING_SSH default taler-rusty-devtesting (or user@host)
# DEVTESTING_AMOUNT default CHF:1.00
# DEVTESTING_CREDIT_PAYTO exchange IBAN payto (auto from EXCHANGE_PUBLIC/keys if empty)
# DEVTESTING_EXCHANGE_IBAN fallback CH6808573105529100001 (TOPS CHF exchange)
# DEVTESTING_SKIP=1 skip phase
# SKIP_SSH=1 skip (same as other SSH phases)
#
set -euo pipefail
ROOT=$(cd "$(dirname "$0")" && pwd)
# shellcheck source=lib.sh
source "$ROOT/lib.sh"
set_area devtesting
section "devtesting · fake-franken CHF (rusty taler-devtesting)"
# Do NOT honor SKIP_SSH here: stage/tops profiles set SKIP_SSH=1 for container
# inside checks, but rusty.taler-ops.ch is a separate SSH hop (devtesting key).
if [ "${DEVTESTING_SKIP:-0}" = "1" ]; then
info "skip" "DEVTESTING_SKIP=1"
exit 0
fi
: "${DEVTESTING_SSH:=taler-rusty-devtesting}"
: "${DEVTESTING_AMOUNT:=CHF:1.00}"
: "${DEVTESTING_EXCHANGE_IBAN:=CH6808573105529100001}"
: "${SSH_CONNECT_TIMEOUT:=8}"
: "${SSH_CMD_TIMEOUT:=45}"
ssh_dt() {
# Prefer Host alias; fall back to explicit user@host if config missing
local target="$DEVTESTING_SSH"
if ! ssh -G "$target" >/dev/null 2>&1; then
target="devtesting@rusty.taler-ops.ch"
fi
if command -v timeout >/dev/null 2>&1; then
timeout "${SSH_CMD_TIMEOUT}" ssh \
-o BatchMode=yes \
-o ConnectTimeout="${SSH_CONNECT_TIMEOUT}" \
-o StrictHostKeyChecking=accept-new \
"$target" "$@"
else
ssh \
-o BatchMode=yes \
-o ConnectTimeout="${SSH_CONNECT_TIMEOUT}" \
-o StrictHostKeyChecking=accept-new \
"$target" "$@"
fi
}
set_group cli
info "ssh" "DEVTESTING_SSH=${DEVTESTING_SSH} amount=${DEVTESTING_AMOUNT}"
help_out=$(ssh_dt fake-incoming --help 2>&1) || true
if printf '%s' "$help_out" | grep -q 'fake-incoming'; then
ok "cli" "taler-devtesting fake-incoming reachable via ${DEVTESTING_SSH}"
else
fail "cli" "cannot run taler-devtesting on ${DEVTESTING_SSH}" \
"$(printf '%s' "$help_out" | tr '\n' ' ' | head -c 200)"
exit 1
fi
set_group geniban
iban_out=$(ssh_dt geniban 2>&1) || true
iban=$(printf '%s\n' "$iban_out" | grep -E '^CH[0-9A-Z]+$' | tail -1 || true)
if [ -n "$iban" ]; then
ok "geniban" "synthetic IBAN $iban"
else
fail "geniban" "expected CH… IBAN" "$(printf '%s' "$iban_out" | tr '\n' ' ' | head -c 160)"
exit 1
fi
# Credit payto: rusty libeufin-nexus only accepts the *production* exchange IBAN
# configured there (DEVTESTING_EXCHANGE_IBAN). Do not use stage /keys first-account.
set_group credit-payto
credit_payto="${DEVTESTING_CREDIT_PAYTO:-}"
if [ -z "$credit_payto" ]; then
credit_payto="payto://iban/${DEVTESTING_EXCHANGE_IBAN}?receiver-name=Taler+Operations+AG"
fi
ok "credit-payto" "$credit_payto"
# Subject: not a real reserve — nexus should bounce missing reserve public key
# (proves CHF fake-incoming path). Real reserve pub would credit wirewatch.
subject="mon-fake-franken-$(date -u +%Y%m%d%H%M%S)-$$"
debit_payto="payto://iban/${iban}?receiver-name=MonDevTest"
set_group fake-incoming
info "fake-incoming" "amount=${DEVTESTING_AMOUNT} subject=${subject}"
fi_rc=0
fi_out=$(ssh_dt fake-incoming \
--amount "${DEVTESTING_AMOUNT}" \
--subject "${subject}" \
--credit-payto "${credit_payto}" \
--debit-payto "${debit_payto}" 2>&1) || fi_rc=$?
# Normalize multi-line for matching
fi_flat=$(printf '%s' "$fi_out" | tr '\n' ' ')
if printf '%s' "$fi_flat" | grep -qiE 'Permission denied|Connection refused|Could not resolve|No such command'; then
fail "fake-incoming" "SSH/CLI failure" "$(printf '%s' "$fi_flat" | head -c 200)"
exit 1
fi
if printf '%s' "$fi_flat" | grep -qiE 'Creditor must be the exchange'; then
fail "fake-incoming" "wrong credit IBAN (not exchange)" "$(printf '%s' "$fi_flat" | head -c 220)"
exit 1
fi
if printf '%s' "$fi_flat" | grep -qiE 'Missing amount|unable to read secrets|CalledProcessError'; then
# secrets warn alone is soft if we still see IN line
if ! printf '%s' "$fi_flat" | grep -qE 'libeufin-nexus - IN .* CHF'; then
fail "fake-incoming" "nexus rejected request" "$(printf '%s' "$fi_flat" | head -c 220)"
exit 1
fi
fi
if printf '%s' "$fi_flat" | grep -qE 'libeufin-nexus - IN .* CHF:[0-9]'; then
if printf '%s' "$fi_flat" | grep -qiE 'bounced.*missing reserve public key'; then
ok "fake-incoming" "CHF IN accepted by nexus · bounced (no reserve pub) — fake-franken plumbing OK"
info "note" "use real reserve pub as --subject for wirewatch credit (not required for mon probe)"
elif printf '%s' "$fi_flat" | grep -qiE 'bounced'; then
warn "fake-incoming" "CHF IN bounced (other reason)" "$(printf '%s' "$fi_flat" | grep -oiE 'bounced[^ ]* [^ ]*' | head -c 160)"
else
ok "fake-incoming" "CHF IN recorded by nexus (no bounce line)"
fi
elif [ "$fi_rc" -eq 0 ] && printf '%s' "$fi_flat" | grep -qi 'Faking incoming CHF'; then
ok "fake-incoming" "CLI completed CHF fake-incoming"
else
fail "fake-incoming" "no CHF IN line from nexus" "rc=${fi_rc} $(printf '%s' "$fi_flat" | head -c 200)"
exit 1
fi
info "done" "devtesting fake-franken probe finished"
exit 0

35
host-agent/env/mytops-stage.env.example vendored Normal file
View file

@ -0,0 +1,35 @@
# Copy to: ~/.config/taler-monitoring/env (user taler-monitoring @ betel)
# mytops CHF stage — NOT lifeline / production
#
SUITE_GIT_URL=https://git.hacktivism.ch/hernani/taler-monitoring.git
SUITE_GIT_REF=main
SUITE_DIR=$HOME/src/taler-monitoring
TALER_DOMAIN=stage.taler-ops.ch
MON_HOSTS="stage.my.taler-ops.ch stage.taler-ops.ch"
HTML_OUT=$HOME/monitoring-sites-staging
DEPLOY_WWW_ROOT=/var/www/monitoring-sites
HTML_URL_OK=/monitoring/
HTML_URL_ERR=/monitoring_err/
HTML_OK_DIR=monitoring
HTML_ERR_DIR=monitoring_err
# urls + monpages + fake-franken CHF (rusty.taler-ops.ch)
PHASES="urls monpages devtesting"
SKIP_SSH=1
LOCAL_STACK=0
INSIDE_PODMAN=0
CONTINUE_ON_ERROR=1
RUN_TIMEOUT=600
STRICT_EXIT=1
MONPAGES_REQUIRE_PUBLIC=1
MONPAGES_INVENTORY=job
TALER_MON_LANG=en
AGENT_LABEL=mytops-stage-host-agent
STATE_NAME=taler-monitoring-mytops-stage
SUITE_UPDATE_STRICT=1
CHECK_BANK=0
MERCHANT_REQUIRED=1
CHECK_LANDING=0
# rusty devtesting (SSH Host taler-rusty-devtesting → devtesting@rusty.taler-ops.ch)
DEVTESTING_SSH=taler-rusty-devtesting
DEVTESTING_AMOUNT=CHF:1.00
# DEVTESTING_SKIP=1

View file

@ -69,6 +69,15 @@
# AUTH401_ADMIN_USER=admin
# FRANCPAYSAN_SECRETS=$HOME/francpaysan-secrets
# --- Devtesting / fake-franken CHF (rusty.taler-ops.ch) ---
# Phase: devtesting | franken | fake-franken
# SSH Host taler-rusty-devtesting (user devtesting, forced taler-devtesting CLI).
# DEVTESTING_SSH=taler-rusty-devtesting
# DEVTESTING_AMOUNT=CHF:1.00
# DEVTESTING_CREDIT_PAYTO= # optional; auto from EXCHANGE_PUBLIC/keys or TOPS IBAN
# DEVTESTING_EXCHANGE_IBAN=CH6808573105529100001
# DEVTESTING_SKIP=1 # skip phase
# --- Ladder (GOA withdraw ladder · explorer pool) ---
# Prefer SECRETS_ROOT / KOOPA_ADMIN_SECRETS (bank-explorer-password.txt).
# Overrides only if needed:

View file

@ -92,6 +92,7 @@ def ui(lang: str, key: str, **kwargs) -> str:
"stage_i3": "Phases: {phases} · suite auto-upgrade each timer run",
"stage_i4": "Not GOA: no surface/aptdeploy/mail pages · no koopa-admin-log",
"stage_i5": "Timer: taler-monitoring-mytops-stage.timer (4h) · user taler-monitoring",
"stage_i6": "devtesting: fake-franken CHF via rusty.taler-ops.ch (geniban + fake-incoming)",
"pages_section_lead": "Monitoring pages themselves (suite):",
"mm_title": "Mattermost chat health",
"mm_summary": "mattermost.taler.net · SPA + /api/v4/system/ping · TLS",
@ -182,6 +183,7 @@ def ui(lang: str, key: str, **kwargs) -> str:
"stage_i3": "Phases : {phases} · auto-upgrade suite à chaque minuterie",
"stage_i4": "Pas GOA : pas de pages surface/aptdeploy/mail · pas de koopa-admin-log",
"stage_i5": "Minuterie : taler-monitoring-mytops-stage.timer (4 h) · user taler-monitoring",
"stage_i6": "devtesting : fake-franken CHF via rusty.taler-ops.ch (geniban + fake-incoming)",
"pages_i6": "Hôte de cette page : {host} · libellé : {label}",
"pages_section_lead": "Les pages de monitoring elles-mêmes (suite) :",
"mm_title": "Santé du chat Mattermost",
@ -612,6 +614,7 @@ def monitoring_scope(
ui(lang, "stage_i3", phases=phase_txt),
ui(lang, "stage_i4"),
ui(lang, "stage_i5"),
ui(lang, "stage_i6"),
ui(lang, "pages_i6", host=host, label=label),
]
return {

View file

@ -104,6 +104,9 @@ Phases:
mail MX/SMTP/IMAP for Taler mail (firefly, pixel/TSA, catalogued domains)
monpages public monitoring HTML via FQDN (obligatory ERROR; GOA full inventory, FP only FP)
uses MON_HOSTS + HTML_URL_OK (same as host-agent); v1.3.1+
devtesting fake-franken CHF via rusty.taler-ops.ch (taler-devtesting)
geniban + fake-incoming; needs SSH Host DEVTESTING_SSH (key to devtesting@rusty)
franken|fake-franken aliases for devtesting
all urls + inside + versions + sanity + e2e (SSH phases only on koopa)
full all + server + ladder + auth401 (maximum; long-running, needs secrets)
NOTE: surface is never included in all/full — pass it explicitly
@ -245,7 +248,7 @@ while [ $# -gt 0 ]; do
CURRENCY_OVERRIDE="$2"; shift 2
;;
--no-probe) NO_PROBE=1; shift ;;
urls|inside|versions|sanity|server|e2e|ladder|goa-ladder|auth401|aptdeploy|apt-deploy|apt_src|surface|ecosystem|mattermost|mail|monpages|pages|all|full) PHASES+=("$1"); shift ;;
urls|inside|versions|sanity|server|e2e|ladder|goa-ladder|auth401|aptdeploy|apt-deploy|apt_src|surface|ecosystem|mattermost|mail|monpages|pages|devtesting|franken|fake-franken|fake_franken|all|full) PHASES+=("$1"); shift ;;
*)
# bare domain shorthand: ./taler-monitoring.sh taler.net
if [[ "$1" == *.* && "$1" != *://* && "$1" != -* ]]; then
@ -532,6 +535,9 @@ for p in "${PHASES[@]}"; do
mattermost) run_phase mattermost "$ROOT/check_mattermost.sh" || ec=1 ;;
mail) run_phase mail "$ROOT/check_mail.sh" || ec=1 ;;
monpages|pages) run_phase monpages "$ROOT/check_monitoring_pages.sh" || ec=1 ;;
devtesting|franken|fake-franken|fake_franken)
run_phase devtesting "$ROOT/check_devtesting.sh" || ec=1
;;
esac
done