Metadata-Version: 2.4
Name: rbcli
Version: 0.1.0
Summary: Swiss Open Banking CLI via SIX bLink API and OpenWealth API — optimized for LLM agents
Author: Roman Bischoff, Claude Opus 4.6 (Anthropic)
License: MIT
Project-URL: Homepage, https://rbcli.gbits.io
Project-URL: Documentation, https://github.com/gbits-io/rbcli/tree/main/docs
Project-URL: Repository, https://github.com/gbits-io/rbcli
Project-URL: Issues, https://https://github.com/gbits-io/rbcli/issues
Keywords: banking,open-banking,blink,switzerland,cli,llm,open wealth,API banking
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.1
Requires-Dist: flask>=3.0
Requires-Dist: requests>=2.31
Requires-Dist: tabulate>=0.9
Requires-Dist: cryptography>=42.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Dynamic: license-file

# rbcli

**Swiss Open Banking from your terminal.** Payments, accounts, statements, product comparison, infrastructure monitoring, cryptographic signing, an independent git-backed ledger, and LLM safety guardrails — powered by [SIX bLink](https://blink.six-group.com/), designed for autonomous LLM agents.

```
$ rbcli pay send --from CH9300762011623852957 --to CH6589144414396878645 \
    --amount 250.00 --currency CHF --reference "Invoice 2026-003" \
    --force --sign --assert-min-balance 10000

✓ Payment executed (SCA bypassed) [signed]
  Payment Id:      pay-348264a1
  Uetr:            a3c8f921-71b4-4e2a-9d05-83bf2e1c6a47
  Status:          EXECUTED
  Amount:          CHF 250.00
  From Balance:    12450.00 → 12200.00
  Signed:          True
  Signature:       d4f89cc1786de61f1aa5bc29...
```

## Why?

Swiss banks have had bLink since 2020. Top managers are conservative — they limit what's possible through APIs because they fear losing the bank–client relationship. `rbcli` demonstrates what's already technically feasible.

```
  ┌──────────────────────────────────────────────────────────────┐
  │                    What banks fear                           │
  │                                                              │
  │   Today:    Client ──── Bank App ──── Bank                   │
  │                          (lock-in)                           │
  │                                                              │
  │   rbcli: Client ──── Terminal/LLM ──── bLink ──── Bank   │
  │                          (open)            (exists!)         │
  │                                                              │
  │   The app becomes optional. The client becomes sovereign.    │
  └──────────────────────────────────────────────────────────────┘
```

The `--force` flag is the politically interesting part: it shows that SCA via a proprietary mobile app is a business decision, not a technical limitation. Add `--sign` and you have *stronger* authentication than any banking app offers.

### The Twint proof

The same Swiss banks operate both bLink and Twint. One is open, the other is closed. This isn't an accident — it's a choice:

```
  bLink (SIX)                          Twint
  ────────────────────────────         ────────────────────────────
  Open standard                        Closed ecosystem
  Any third party can connect          Merchant contract required
  Documented REST APIs                 No public API for P2P
  Client chooses the interface         Twint IS the interface
  rbcli works ✓                     rbcli can't work ✗

  Same banks. Same country. Opposite philosophy.
```

Twint P2P payments (person-to-person via phone number) are completely locked inside the Twint app. There is no programmatic way to send a Twint P2P payment — no URL scheme, no open API, no webhook. The banks chose this. They could have built Twint on open rails. They didn't, because lock-in was the point.

rbcli exists because bLink exists. The infrastructure for open banking is already there. What's missing is the will to use it.

## Quick start

```bash
# Install
brew install pipx && pipx ensurepath
pipx install .

# Start the simulator (3 demo banks, pre-seeded accounts)
rbcli sim start

# Get the full picture in one call
rbcli wealth summary

# Make a signed payment with safety guardrails
rbcli keys init
rbcli pay send --from CH9300762011623852957 --to CH6589144414396878645 \
  --amount 100.00 --force --sign --assert-min-balance 10000

# Keep your own records
rbcli ledger init
rbcli ledger sync

# Stop
rbcli sim stop
```

## Architecture

```
┌───────────────────────────────────────────────────────────┐
│              LLM Agent / Terminal                          │
│  Claude Code · Cowork · OpenClaw · bash                    │
│                                                            │
│  "Show me everything I own. Optimize for deposit           │
│   protection and yield across cash and securities."        │
└───────────────────────┬───────────────────────────────────┘
                        │ shell (structured JSON)
┌───────────────────────▼───────────────────────────────────┐
│                      rbcli                              │
│                                                            │
│  accounts · statements · pay · transfer · products         │
│  banks · standing-orders · monitor · notifications         │
│  keys · ledger · wealth · portfolio · customers            │
│                                                            │
│  ┌───────────┐  ┌──────────┐  ┌────────────────────────┐ │
│  │ Signing   │  │  Ledger  │  │  Channel Tracking      │ │
│  │ Ed25519   │  │  Git repo│  │  blink/ebanking/...    │ │
│  └───────────┘  └──────────┘  └────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐│
│  │ Safety: --dry-run · --idempotency-key · --assert-min  ││
│  └───────────────────────────────────────────────────────┘│
└───────────────────────┬───────────────────────────────────┘
                        │ HTTP REST
┌───────────────────────▼───────────────────────────────────┐
│            rbcli sim (Flask) / bLink / OpenWealth       │
│                                                            │
│  ┌─ AIS + PSS ────────────────────────────────────────┐  │
│  │  Big Bank AG  ·  KaBe Bank  ·  Small Bank AG       │  │
│  └────────────────────────────────────────────────────┘  │
│  ┌─ OpenWealth Custody ───────────────────────────────┐  │
│  │  Positions · Transactions · Portfolios · Customers  │  │
│  └────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────┘
```

## Client sovereignty stack

Three features that shift the power from the bank to the client:

```
┌─────────────────────────────────────────────────────────────────┐
│                                                                  │
│  ┌──────────────────┐   You see which channel initiated each    │
│  │ 1. Channel        │   transaction. "Show me debits I didn't  │
│  │    Visibility     │   initiate through rbcli" = fraud     │
│  └────────┬─────────┘   detection with one command.             │
│           │                                                      │
│  ┌────────▼─────────┐   Every statement, payment, and balance   │
│  │ 2. Git Ledger    │   is committed to a local git repo.       │
│  │    (independent   │   Cryptographic hashes. Immutable.       │
│  │     record)       │   "Verify my records against the bank."  │
│  └────────┬─────────┘                                            │
│           │                                                      │
│  ┌────────▼─────────┐   Ed25519 signatures on payment           │
│  │ 3. Cryptographic │   instructions. Non-repudiable proof.     │
│  │    Signing        │   Stronger than any mobile app OTP.       │
│  └──────────────────┘                                            │
│                                                                  │
│  The bank is no longer the sole record-keeper.                   │
│  The client becomes a peer, not a dependent.                     │
└─────────────────────────────────────────────────────────────────┘
```

## LLM safety guardrails

Three features that make autonomous LLM agents safe for banking:

```
┌─────────────────────────────────────────────────────────────────┐
│                                                                  │
│  --dry-run             Check consequences without executing.     │
│                        "What happens if I move CHF 100k?"        │
│                        Shows before/after balances for both      │
│                        sides. No money moves.                    │
│                                                                  │
│  --assert-min-balance  Hard guardrail. Payment is rejected if    │
│                        remaining balance would fall below N.     │
│                        Prevents an LLM from draining an account. │
│                                                                  │
│  --idempotency-key     Same key = same result on retry.          │
│                        If the LLM retries a failed call, the     │
│                        payment isn't executed twice.              │
│                        Response includes deduplicated: true.     │
│                                                                  │
│  Safety first. Efficiency second. Optimization third.            │
└─────────────────────────────────────────────────────────────────┘
```

```bash
# Check consequences before acting
$ rbcli pay send --from CH93... --to CH65... --amount 100000 --force --dry-run
✓ Dry run — no payment executed
  From Balance Before: 500000.0
  From Balance After:  400000.0
  To Balance Before:   12450.0
  To Balance After:    112450.0

# Guardrail: abort if balance would drop too low
$ rbcli pay send --from CH93... --to CH65... --amount 5000 --force --assert-min-balance 10000
✗ Payment would leave balance at CHF 5450.00, below required minimum of CHF 10000.00.

# Prevent double payments on LLM retry
$ rbcli pay send --from CH93... --to CH65... --amount 100 --force --idempotency-key "rent-2026-03"
✓ Payment executed (SCA bypassed)
$ rbcli pay send --from CH93... --to CH65... --amount 100 --force --idempotency-key "rent-2026-03"
✓ Payment executed (SCA bypassed) (deduplicated)
  Deduplicated: True    ← no money moved
```

## Wealth summary: cash + securities

One command for the full picture — cash across all banks plus securities across all custody accounts:

```
$ rbcli wealth summary

Wealth Summary

  Total Wealth:                CHF 1,784,900.25

  Cash          34.8%  CHF   621,750.25  █████████████
  Securities    65.2%  CHF 1,163,150.00  ██████████████████████████

  Cash Positions

  Cash Total:                  CHF 621,750.25
  Weighted Avg Yield:          0.112%
  Est. Annual Interest:        CHF 672.84

  esisuisse Covered:           CHF 203,130.25 (33%)
  esisuisse Uncovered:         CHF 418,620.00

  ✓ Big Bank AG        CHF   100,650.00   AA
  ⚠ KaBe Bank          CHF   515,420.00   A
      ↳ CHF 400,000.00 exceeds esisuisse limit
  ✓ Small Bank AG      CHF     5,680.25   BBB+

  Securities Portfolio

  Market Value:                CHF 1,163,150.00
  Unrealized P&L:              CHF +81,400.00 (+7.52%)
  Positions:                   5

  fund          44.1%  CHF   512,400.00  █████████████████
  equity        38.8%  CHF   450,750.00  ███████████████
  bond          17.2%  CHF   200,000.00  ██████

  UBS ETF SMI (CHF)        CHF   491,000.00  +31,000.00  kabe-bank
  Nestlé SA                CHF   270,750.00  +28,500.00  big-bank
  Conf. Bond 1.5% 2028     CHF   200,000.00  +2,000.00   big-bank
  Swiss Re AG              CHF   180,000.00  +18,000.00  big-bank
  iShares Core MSCI World  CHF    21,400.00  +1,900.00   kabe-bank

  Warnings

  ⚠ exceeds_deposit_protection — KaBe Bank +CHF 400k
  ⚠ concentration_risk — 83% of cash at KaBe Bank
  ⚠ total_bank_exposure — CHF 1M at KaBe Bank (58% of wealth)
  ⚠ low_credit_rating — Small Bank BBB+
  ℹ zero_yield — CHF 12,450 earning 0%
```

In JSON mode, the LLM gets a `portfolio` object nested within the response — total market value, cost basis, unrealized P&L, positions with ISINs, asset class breakdown, and per-bank distribution. One call, complete picture.

## Demo banks

```
┌──────────────────────────────────────────────────────────────────┐
│  Big Bank AG          KaBe Bank            Small Bank AG         │
│  (big-bank)           (kabe-bank)          (small-bank)          │
│                                                                   │
│  Rating:  AA          Rating:  A           Rating:  BBB+         │
│  Savings: 0.20%       Savings: 0.50%       Savings: 0.20%        │
│  esisuisse: 100k      esisuisse: 100k      esisuisse: 100k       │
│                                                                   │
│  Accounts:            Accounts:            Accounts:              │
│  · CHF current 12.4k  · CHF current 500k   · CHF current 5.7k   │
│  · CHF savings 85k    · CHF savings 0       · CHF savings 0      │
│  · EUR current 3.2k   · USD trading 15.4k                        │
└──────────────────────────────────────────────────────────────────┘
```

KaBe Bank holds CHF 500'000 — set up for the Einlagensicherung demo scenario.

## Commands

### Core banking

| Command | Description |
|---------|-------------|
| `accounts list` | List accounts at a bank |
| `accounts balance <IBAN>` | Current balance (auto-committed to ledger) |
| `statements fetch <IBAN>` | Transaction history with `--channel` filter (auto-committed) |
| `statements export <IBAN>` | Export as CSV, JSON, or MT940 |
| `pay send --from --to --amount` | Payment with `--force`, `--sign`, `--dry-run`, `--assert-min-balance`, `--idempotency-key` |
| `pay confirm <id> --otp <code>` | SCA confirmation |
| `pay verify <id>` | Verify cryptographic signature |
| `pay list` | Recent payments with UETR and channel |
| `transfer send --from --to --amount` | Internal transfer with `--dry-run`, `--assert-min-balance`, `--idempotency-key` |
| `standing-orders list/create/cancel` | Recurring payments |

### Discovery, comparison, and aggregation

| Command | Description |
|---------|-------------|
| `banks list` | All banks with credit ratings, rates, deposit protection |
| `banks info <alias>` | Full detail: rates, esisuisse, capabilities |
| `products list` | Products across all banks |
| `products compare --type savings` | Ranked comparison, best rate first |
| `wealth summary` | Unified view: cash + securities + warnings (one call) |

### Securities portfolio (OpenWealth Custody)

| Command | Description |
|---------|-------------|
| `portfolio positions` | All securities across all custody accounts |
| `portfolio positions --portfolio-id <id>` | Positions for one portfolio |
| `portfolio transactions` | Security transaction history (buys, sells, dividends) |
| `portfolio transactions --isin <ISIN>` | Transactions for one security |
| `portfolio summary` | Asset allocation, currency exposure, P&L, warnings |

### Customer management (OpenWealth)

| Command | Description |
|---------|-------------|
| `customers list` | All managed customers with linked accounts |
| `customers info <id>` | Customer detail: profile, risk, linked custody accounts |

### Signing and ledger

| Command | Description |
|---------|-------------|
| `keys init` | Generate Ed25519 keypair |
| `keys show` | Display public key |
| `keys export` | Export public key (PEM or hex) for bank registration |
| `ledger init` | Initialize git-backed ledger |
| `ledger sync` | Fetch & commit all accounts across all banks |
| `ledger verify <IBAN>` | Diff local records against bank's current data |
| `ledger status` | What's tracked, last sync dates |
| `ledger log` | Git history of all interactions |

### Monitoring (Koivu-style)

| Command | Description |
|---------|-------------|
| `monitor ping --from --to` | Pennytest: send CHF 0.01, measure latency |
| `monitor run --config routes.toml` | Continuous monitoring loop |
| `monitor status` | Recent ping results |
| `monitor report` | Uptime %, avg latency, failures |

### Infrastructure

| Command | Description |
|---------|-------------|
| `sim start/stop/reset/status` | Manage the mock bLink server |
| `config init/show/list-banks/set-default` | Configuration |
| `notifications list` | Payment notifications (camt.054) with channel |
| `--help-llm` | Structured JSON manifest for LLM agents |

## LLM agent usage

`rbcli` is designed so LLM agents can use it autonomously:

- **Auto-detection**: JSON when piped, tables when interactive
- **ANSI colors**: semantic coloring in TTY mode (amounts, channels, ratings, statuses), zero ANSI in JSON/CSV
- **`--help-llm`**: complete capability manifest with parameter schemas
- **Exit codes**: 0=success, 2=auth, 4=insufficient funds, 5=pending SCA, 6=unreachable, 7=timeout, 8=rejected
- **Error suggestions**: every error tells the agent how to fix it
- **Safety**: `--dry-run`, `--assert-min-balance`, `--idempotency-key` prevent catastrophic mistakes
- **`--diff`**: automatic before/after balances on every executed payment

### What Claude Code does with rbcli

You say: *"I have CHF 500'000 at KaBe Bank. Distribute it so everything is covered by Einlagensicherung, and maximize yield."*

```
Claude Code reasoning:

1. rbcli wealth summary
   → Sees CHF 500k at KaBe, 33% esisuisse coverage, 3 warnings

2. Thinks: "3 banks × 100k = 300k max coverage. Keep 300k at KaBe
   (best rate 0.50%), move 100k to Big Bank (AA), 100k to Small Bank.
   Trade-off: CHF 600/year less interest for 200k more protection."

3. rbcli pay send --from CH65... --to CH93... --amount 100000 \
     --force --sign --assert-min-balance 200000 --dry-run
   → Dry run OK. From: 500k → 400k. To: 12.4k → 112.4k.

4. rbcli pay send --from CH65... --to CH93... --amount 100000 \
     --force --sign --assert-min-balance 200000 \
     --idempotency-key "esisuisse-bigbank-2026-03-25"
   → Executed. Signed. Diff: 500k → 400k.

5. rbcli pay send --from CH65... --to CH36... --amount 100000 \
     --force --sign --assert-min-balance 200000 \
     --idempotency-key "esisuisse-smallbank-2026-03-25"
   → Executed. Signed. Diff: 400k → 300k.

6. rbcli wealth summary
   → Confirms: 3 banks, esisuisse coverage now 49%, warnings reduced.
```

Two calls for the full picture (`wealth summary`), two dry-run-validated + signed + idempotent payments, one verification. No banking app does this.

## Channel visibility

Every transaction shows which channel initiated it:

```
$ rbcli statements fetch CH9300762011623852957

DATE        AMOUNT   COUNTERPARTY          REFERENCE               CHANNEL
----------  -------  --------------------  ----------------------  ---------
2026-03-24  -1500    Immobilien Müller AG  Miete März 2026         ebanking
2026-03-22   8500    Arbeitgeber GmbH      Lohn Februar 2026       ebics
2026-03-20    -89.9  Swisscom AG           Mobile Abo Februar      mobile
2026-03-18   -245.5  Migros Zürich         Einkauf KK-1234         card
2026-03-13   -350    CSS Versicherung      Krankenkasse März 2026  ebanking
2026-03-10    -42    SBB AG                GA Monatsrate           mobile
```

Filter by channel: `rbcli statements fetch CH93... --channel ebanking`

Channels: `blink`, `ebanking`, `mobile`, `ebics`, `card`, `system`

## Cryptographic signing

```bash
# Generate keypair
rbcli keys init
rbcli keys export > public.pem    # Register with bank

# Sign a payment
rbcli pay send --from CH93... --to CH65... --amount 100 --force --sign

# Verify later
rbcli pay verify pay-348264a1
✓ Signature VALID for pay-348264a1
  algorithm:       Ed25519
  local_key_match: True
```

What gets signed: a canonical JSON of `from_iban`, `to_iban`, `amount`, `currency`, `reference`, `uetr`, and `timestamp`. The signature is stored in the payment record and auto-committed to the git ledger.

The argument: if a payment is signed with Ed25519, the bank has *stronger* proof of authorization than a mobile app OTP. The proprietary app becomes not just unnecessary but inferior.

## Git-backed ledger

```bash
# Initialize
rbcli ledger init

# Sync all banks (fetches statements + balances, commits to git)
rbcli ledger sync

# Your audit trail
rbcli ledger log
COMMIT        TIMESTAMP                  MESSAGE
53500392c6e2  2026-03-25 11:52:37 +0000  rbcli: transfer xfr-3e553101
2df774f15993  2026-03-25 11:52:37 +0000  rbcli: payment pay-a0d6cd19 EXECUTED
f0e9401d2699  2026-03-25 11:52:36 +0000  rbcli: balance CH3608387... at small-bank
d8b269acbbf4  2026-03-25 11:52:36 +0000  rbcli: statements CH3608387... (2 txns)

# Audit the bank
rbcli ledger verify CH9300762011623852957
✓ Records match. No discrepancies found.
```

Every interaction is a git commit. If the bank ever changes or removes a transaction, `ledger verify` shows the discrepancy — because your local repo has the cryptographic proof of what they told you before.

```
~/.config/rbcli/ledger/          ← git repo
├── big-bank/
│   └── CH9300762011623852957/
│       ├── statements/2026-03-25.json
│       ├── balances/2026-03-25T103000.json
│       ├── payments/pay-348264a1.json    ← includes signature
│       └── transfers/xfr-3e553101.json
├── kabe-bank/
│   └── ...
└── small-bank/
    └── ...
```

## ISO 20022 messages

| Message | Purpose | CLI command |
|---------|---------|-------------|
| pain.001 | Payment initiation | `pay send` |
| camt.052 | Intraday balance/transactions | `statements fetch --intraday` |
| camt.053 | End-of-day statements | `statements fetch` |
| camt.054 | Payment notifications | `notifications list` |

All payments include a UETR (Unique End-to-End Transaction Reference) — UUID v4, same format as SWIFT gpi.

## How rbcli compares

```
                   Comparis.ch  Banking App  Fintech/bLink  Twint        rbcli + LLM
                   ───────────  ───────────  ─────────────  ─────────    ──────────────
Compare products   ✓ static     own only     pre-built UI   ✗            ✓ structured
See all balances   ✗            one bank     ✓ multibank    ✗            ✓ one command
Full wealth view   ✗            ✗            ✗              ✗            ✓ wealth summary
Execute payments   ✗            ✓ with app   ✓ redirect     ✓ app only   ✓ --force
Custom reasoning   ✗            ✗            pre-coded      ✗            ✓ any question
Sign payments      ✗            ✗            ✗              ✗            ✓ Ed25519
Independent record ✗            ✗            ✗              ✗            ✓ git ledger
Channel audit      ✗            ✗            partial        ✗            ✓ full
Dry-run / preview  ✗            ✗            ✗              ✗            ✓ --dry-run
Idempotency        ✗            ✗            ✗              ✗            ✓ --idempotency-key
Balance guardrail  ✗            ✗            ✗              ✗            ✓ --assert-min-balance
Risk warnings      ✗            ✗            some           ✗            ✓ structured
Open API           n/a          ✗            ✓ bLink        ✗ closed     ✓ bLink
New feature speed  weeks        months       weeks          months       seconds
Lock-in            none         bank app     fintech app    Twint app    none
```

## Configuration

```
~/.config/rbcli/
├── config.toml       # Bank connections and defaults
├── signing.key       # Ed25519 private key (chmod 600)
├── signing.pub       # Ed25519 public key
├── ledger/           # Git repo with banking records
└── sim.pid           # Simulator PID file
```

## Monitoring (Koivu-style)

Inspired by [Koivu Network](https://www.koivu.network/) — monitoring banking infrastructure uptime using real pennytest payments:

```bash
rbcli monitor ping --from CH93... --to CH65... --timeout 10
✓ Ping OK: 6ms (big-bank→big-bank)
```

Continuous monitoring with a routes config:

```toml
# routes.toml
[[routes]]
name = "intra-bank-bigbank"
from = "CH9300762011623852957"
to = "CH2800762011623852958"
bank = "big-bank"
amount = 0.01
currency = "CHF"

[[routes]]
name = "cross-bank-to-kabe"
from = "CH9300762011623852957"
to = "CH6589144414396878645"
bank = "big-bank"
amount = 0.01
currency = "CHF"
```

```bash
rbcli monitor run --config routes.toml --interval 60
```

## Roadmap

- **bankcli** — thin orchestrator routing to protocol-specific CLIs
- **fixcli** — FIX protocol for trading and securities
- **ebicscli** — EBICS for corporate banking
- **OpenWealth** — portfolio management, custody accounts
- **MCP server** — expose as Model Context Protocol server for direct LLM tool use
- **Real bLink** — drop-in replacement for the simulator
- **Alpensign / SwissSign** — qualified electronic signatures (ZertES)

## Man page

```bash
man ./man/rbcli.1
```

## License

MIT
