Skip to main content

Swiss Open Banking CLI via SIX bLink API and OpenWealth API — optimized for LLM agents

Project description

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, 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

# 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.            │
└─────────────────────────────────────────────────────────────────┘
# 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

# 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

# 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 — monitoring banking infrastructure uptime using real pennytest payments:

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

Continuous monitoring with a routes config:

# 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"
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

man ./man/rbcli.1

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rbcli-0.1.0.tar.gz (75.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rbcli-0.1.0-py3-none-any.whl (80.0 kB view details)

Uploaded Python 3

File details

Details for the file rbcli-0.1.0.tar.gz.

File metadata

  • Download URL: rbcli-0.1.0.tar.gz
  • Upload date:
  • Size: 75.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rbcli-0.1.0.tar.gz
Algorithm Hash digest
SHA256 092e57ff63fe4ca78bdff219831163db1d4514db7941bd957ea1bc5b48deb60a
MD5 20853412b8d96350e32ea570d5985403
BLAKE2b-256 dc012641c7d1b03f2304e3e2231382c3a63d4c54ee345351dfa9ee9a6a6bd205

See more details on using hashes here.

Provenance

The following attestation bundles were made for rbcli-0.1.0.tar.gz:

Publisher: publish.yml on gbits-io/rbcli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rbcli-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rbcli-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 80.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rbcli-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5524067008e19c842b0c8848cde6fcc72c23097ed68f4be7b22de2907f659813
MD5 246d655f65b07453dd1b41721ed27f9b
BLAKE2b-256 a3eee366a558c039d6cafea71405eec2a928426583558678cd9aca9503f51b56

See more details on using hashes here.

Provenance

The following attestation bundles were made for rbcli-0.1.0-py3-none-any.whl:

Publisher: publish.yml on gbits-io/rbcli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page