# Bank landing (`bank.hacktivism.ch`)

English intro for **GOA exploration** (badge: **Intergalactic · GOA · no IBAN**):
put coins in a GNU Taler wallet, optionally open your own bank account, and
point merchants at `taler.hacktivism.ch`.

| Public URL | What |
|------------|------|
| `https://bank.hacktivism.ch/` · `/intro/` | Landing (nginx in bank container → host **9013**, Caddy) |
| `https://bank.hacktivism.ch/intro/stats.json` | **Live stats** (written *inside* bank container) |
| `https://bank.hacktivism.ch/intro/demo-withdraw.json` | Mint **shared-pool** (`explorer`) withdraw URI |
| `https://bank.hacktivism.ch/intro/auto-account.json` | Auto-create **personal** bank user (credentials once; balance **GOA:0**) |
| `https://bank.hacktivism.ch/webui/` | libeufin-bank SPA (register · login · withdraw) when libeufin is up on **9012** |
| `https://exchange.hacktivism.ch/` | Exchange (ToS `/terms`, keys `/keys`) |
| `https://taler.hacktivism.ch/` | Merchant backend |

## Page layout (top → bottom)

1. Hero (badge, short shared-account line)  
2. **Get the wallet** — Play / F-Droid / iOS / web hub + **Debian/Ubuntu · Command line** (apt) + **Unix / POSIX · from source** (build monorepo or run `.mjs`)
3. **Withdraw GOA** — compact QR_Taler, open CTA, live pool balance note, generate-own-account link  
4. **GOA shop · samples** — always visible (2 products + merchant link)  
5. **How the shared account works** — collapsed fold (click to expand)  
6. **Optional · own bank account** — collapsed fold: auto-create account + credentials + QR_Taler for bank UI  
7. **GOA flow** stats  
8. **Performance**  
9. Footer  

## User flow (documented on the page)

### Primary path (works without your own bank account)

1. **Install wallet** — Android (Play / F-Droid), iOS, Ubuntu Touch (via Waydroid + Android wallet), web (Chrome/Firefox/Opera on taler.net).  
2. **One QR / link** — live `taler://withdraw/…` from the **community-shared** bank pool (`explorer`).  
   Minted via `GET /intro/demo-withdraw.json` (or fallback `withdraw.uri`).  
   Auto-confirm finishes the bank side so coins reach the wallet.  
3. **Surprising balance note** — shows live `balance_explorer` from `stats.json`  
   (“pool holds GOA:… (shared, but also yours)”).

QR encoding matches merchant shop popup (**QR_Taler**): animated `#0042B3` conic
ring, off-DOM PNG, center `qr-logo.svg` (`shop-pay.css` classes). Label text:
**Scan in wallet**.

### Optional: personal bank account (auto-created)

In the optional fold:

1. User clicks **Create my bank account**.  
2. Browser calls `GET /intro/auto-account.json` (no form fields).  
3. Server registers a new libeufin user via public `POST /accounts`.  
4. UI shows **username / password** once (“created for you”;  
   not stored for recovery).  
5. Username shape: **`goa-account-<random>`** (shown to the user).  
   Password embeds **`pleasechangeme`** with random chars around it.
6. QR_Taler + link open `/webui/` so the user can log in with those credentials.

Balance **starts at zero** — not the shared `explorer` pool.

#### Deploy status — own bank account (**missing** until verified)

The **own bank account** path is **not complete** unless all of the following
are live on `taler-hacktivism-bank`. Treat this as **missing** until checked:

| Check | Expected |
|-------|----------|
| Landing HTML | Optional fold has **Create my bank account** |
| API process | `python3 /usr/local/bin/demo-withdraw-api.py` on **127.0.0.1:19096** |
| Nginx | `location = /intro/auto-account.json` → `:19096` |
| Public | `curl -sS https://bank.hacktivism.ch/intro/auto-account.json` returns JSON with `username` / `password` / `balance: "GOA:0"` |
| WebUI login | Credentials work at `https://bank.hacktivism.ch/webui/` |

Install helper: `scripts/taler-bank/install-demo-withdraw-api.sh`  
(or manual copy of `demo-withdraw-api.py` + `nginx-landing.conf` + restart API).

### How the shared pool works

1. Shared account `explorer` is pre-funded with GOA.  
2. Each demo withdraw is a **one-shot** op from that pool (not a personal login).  
3. Wallet accepts the URI; **auto-confirm only for `explorer`** (see  
   `auto-confirm-withdrawals.sh`).  
4. Pool balance is communal; amounts you withdraw are yours in the wallet.

### Helpers

| Script | Role |
|--------|------|
| `scripts/taler-bank/demo-withdraw-api.py` | Loopback HTTP **:19096** — `/demo-withdraw.json` + `/auto-account.json` |
| `scripts/taler-bank/install-demo-withdraw-api.sh` | Copy into bank container, nginx locations, start API + auto-confirm loop |
| `scripts/taler-bank/auto-confirm-withdrawals.sh` | Confirm **explorer-only** withdrawals when status=`selected` |
| `scripts/taler-bank/refresh-demo-withdraw.sh` | Refresh static `withdraw.uri` (no python) |
| `configs/bank-landing/nginx-landing.conf` | `/intro/demo-withdraw.json`, `/intro/auto-account.json` proxies |

Needs **python3** in the bank container for `demo-withdraw-api.py`.

### CLI sketch (fresh wallet DB)

```bash
# equivalent one-step in CLI terms:
taler-wallet-cli exchanges add https://exchange.hacktivism.ch/
taler-wallet-cli exchanges accept-tos https://exchange.hacktivism.ch/
# then withdraw from the exchange (amount chosen in wallet UI)
```

---

## Live stats (central host collector · hernani systemd)

**Primary:** user **hernani** on koopa runs `taler-landing-stats.timer`
(`scripts/taler-landing/`). That process full-scans **all bank accounts**
(except `exchange` double-count), paginates every ledger, writes
`stats.json` with **GOA alt units** (`amount_alt` / Kilo-GOA / Mega-GOA / …),
and refreshes exchange + merchant stats the same way.

```bash
# on koopa as hernani
./scripts/taler-landing/install-landing-stats-host.sh
sudo loginctl enable-linger hernani
systemctl --user start taler-landing-stats.service
```

See **`scripts/taler-landing/README.md`**.

**Fallback:** in-container `landing-stats.sh` (below) if the host timer is down.
It now also scans admin + all users (skips only `exchange`).

## Live stats (in bank container · fallback)

Stats are **not** computed in the browser. A small script can still run
**inside the bank container**, query libeufin (all accounts), and write a
public JSON file next to the landing assets.

| Piece | Path / role |
|-------|-------------|
| Generator (in-container) | `/usr/local/bin/landing-stats.sh` |
| Source in admin-log | `scripts/taler-bank/landing-stats.sh` |
| Host installer | `scripts/taler-bank/landing-stats-install.sh` |
| Output | `/var/www/bank-landing/stats.json` → `https://bank.hacktivism.ch/intro/stats.json` |
| Landing UI | `index.html` section **GOA flow · live** |

### What the page shows

- **Bank accounts** — registered accounts (libeufin `GET /accounts`, admin)
- **Wallets involved** — unique reserve pubs from Taler withdrawal debits
- **Withdraws / bank credits** — flow amounts (exchange mirror skipped where noted)
- **Last withdraws** — recent list with amount, time (**CEST** / `Europe/Zurich`)
- **Last 24h / 7d** — amount
- **`balance_explorer`** — live shared-pool balance (shown on withdraw step)
- Performance probes + container RSS (bottom of page)

### Scan depth (must stay deep enough)

| Env | Default | Meaning |
|-----|---------|---------|
| `TX_DELTA` | `-200000` | per-account ledger window (`GET …/transactions?delta=`) |
| `ACCOUNTS_DELTA` | `-10000` | account-list window (`GET /accounts?delta=`) |
| `MAX_SCAN_ACCOUNTS` | `0` (unlimited) | max usernames to scan; `0` = all |

Older defaults (`TX_DELTA=-100`, `MAX_SCAN_ACCOUNTS=80`) **undercounted** credits/withdraws
and account totals on this stack. Empty accounts often return **HTTP 204** (no body) —
that is normal (e.g. fresh auto-accounts), not a stats failure.

### `stats.json` shape (abridged)

```json
{
  "ok": true,
  "currency": "GOA",
  "generated_at": "2026-07-10T20:35+02:00",
  "balance_explorer": "GOA:960",
  "bank_accounts": { "total": 56, "users": 55 },
  "wallets": { "unique_reserves": 51 },
  "withdraws": {
    "count": 51,
    "last_24h": { "amount": "GOA:…" },
    "last_7d": { "amount": "GOA:…" }
  },
  "flow": {
    "incoming": { "amount": "GOA:…", "count": 68 },
    "withdraw": { "amount": "GOA:…", "count": 51 }
  },
  "source": "in-container landing-stats.sh"
}
```

### Install + refresh (on koopa host)

```bash
# from laptop: copy scripts, then on koopa:
cd /path/to/koopa-admin-log/scripts/taler-bank

# copy into container + one-shot run
sudo ./landing-stats-install.sh

# every minute inside the container (* * * * *)
sudo ./landing-stats-install.sh --cron
# optional: LANDING_STATS_CRON='*/5 * * * *' ./landing-stats-install.sh --cron

# later: only re-run
sudo ./landing-stats-install.sh --run-only
```

Requirements **inside** the container:

- `curl`, `awk` (mawk OK), `sed`, `date` — **no python**
- libeufin-bank listening on loopback (script auto-detects `PORT` / 9012 / 8080)
- `/root/bank-explorer-password.txt` (or `BANK_PASS=…` on the `podman exec`)
- writable `/var/www/bank-landing/` (same tree nginx uses for `/intro/`)

`make-demo-withdraw-qr.sh` also calls `landing-stats.sh` after refreshing the demo URI,
when the binary is already installed at `/usr/local/bin/landing-stats.sh`.

### Manual one-liner (debug)

```bash
podman exec -e LANDING_DIR=/var/www/bank-landing taler-hacktivism-bank \
  /usr/local/bin/landing-stats.sh
curl -sS https://bank.hacktivism.ch/intro/stats.json | python3 -m json.tool
```

---

## Host / container files

Landing root (nginx in bank container), typically under something like
`/var/www/bank-landing/` or the container path mapped for **9013**:

| File | Role |
|------|------|
| `index.html` | landing (wallet, shared withdraw, folds, stats) + Open Graph meta |
| `og-goa-shop.png` | **link preview** image (1200×630) for chats / social (OG + Twitter) |
| `shop-pay.css` / `shop-pay.js` | GOA shop popup + **QR_Taler** frame styles |
| `qr-logo.svg` | center logo on QR_Taler frames |
| `qrcode.min.js` | QR encode helper |
| `stats.json` | **live stats** (from in-container script) |
| `nginx-landing.conf` | bank container nginx (**9013**) |
| `withdraw.uri` | current demo `taler://withdraw/…` (optional) |
| `withdraw.amount` | e.g. `GOA:10` (optional) |
| `withdraw-watch.ids` | ids for auto-confirm helper (optional) |

## Deploy landing HTML + helper API

```bash
# landing assets
podman cp configs/bank-landing/index.html \
  taler-hacktivism-bank:/var/www/bank-landing/index.html
podman cp configs/bank-landing/og-goa-shop.png \
  taler-hacktivism-bank:/var/www/bank-landing/og-goa-shop.png
podman cp configs/bank-landing/shop-pay.js \
  taler-hacktivism-bank:/var/www/bank-landing/shop-pay.js
# … shop-pay.css, qr-logo.svg, qrcode.min.js as needed

# demo-withdraw + auto-account API (python3 in container)
./scripts/taler-bank/install-demo-withdraw-api.sh
# or manually:
podman cp scripts/taler-bank/demo-withdraw-api.py \
  taler-hacktivism-bank:/usr/local/bin/demo-withdraw-api.py
podman cp configs/bank-landing/nginx-landing.conf \
  taler-hacktivism-bank:/etc/nginx/sites-available/bank-landing
# restart: python3 /usr/local/bin/demo-withdraw-api.py on 127.0.0.1:19096
#          nginx -s reload; auto-confirm-withdrawals.sh --loop
```

### Demo QR refresh (in container, no python)

```bash
podman exec taler-hacktivism-bank /usr/local/bin/refresh-demo-withdraw.sh
# source: scripts/taler-bank/refresh-demo-withdraw.sh
```

Older host script (needs python3): `scripts/taler-bank/make-demo-withdraw-qr.sh`
