[ANN] beancount-openbanking-io — beangulp importer for EEA/UK banks (PSD2, client-side decryption)

71 views
Skip to first unread message

John Frandsen

unread,
Jul 4, 2026, 6:51:35 PM (10 days ago) Jul 4
to Beancount
Hi all,

Sharing a small beangulp importer some of you might find useful: beancount-openbanking-io. It syncs EEA & UK bank transactions over PSD2 into a Beancount ledger.

Background on why it exists: GoCardless/Nordigen stopped approving new personal Bank-Account-Data accounts and caps usage at 4 requests per account per day, which breaks routine imports for self-hosters. This is an alternative source — transactions arrive as zero-knowledge encrypted envelopes and are decrypted locally with a key only you hold, and there's no eIDAS certificate to obtain and no per-account daily rate-limit wall.

    pip install beancount-openbanking-io


Disclosure: I'm the author and affiliated with open-banking.io. Feedback welcome, especially on the beangulp 3.x integration.

John

Mark Ferry

unread,
Jul 4, 2026, 7:12:53 PM (10 days ago) Jul 4
to 'John Frandsen' via Beancount
Always good to have another aggregator in the mix.

Was just in a reddit discussion today about the hassle of getting TrueLayer and GoCardless working.

Any possibiliry of adding UK banks?
(I'm aware of the OpenBanking divergence... thanks Brexit)

ciao
  Mark


4 Jul 2026 23:51:34 'John Frandsen' via Beancount <bean...@googlegroups.com>:

--
You received this message because you are subscribed to the Google Groups "Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beancount+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/beancount/a13ebdb8-1308-4de0-9eeb-032b927645b6n%40googlegroups.com.
signature.asc

John

unread,
Jul 4, 2026, 7:29:44 PM (10 days ago) Jul 4
to bean...@googlegroups.com
Hi Mark,

Thanks — and yes, UK banks are already covered. The importer and the
open-banking.io backend speak the UK Open Banking standard (OBL v3.x)
alongside Berlin Group NextGenPSD2 (the continental flavour), so
beancount-openbanking-io doesn't care which standard a given bank uses.

The divergence you're alluding to is real but contained: UK endpoints
follow the CMA9-mandated functional specs (arguably the most mature and
most consistently implemented OB APIs in Europe right now), while the
EEA runs NextGenPSD2. They've drifted on details — consent models,
payment-product codes, the v3.1.x vs NextGen 1.3.6 schemas — but for
read-only account-information the two are close enough that one client
normalises both into a single transaction stream. No extra config on
your side; a UK bank and a German bank both just show up as a ledger.

The Reddit thread you mentioned (TrueLayer / GoCardless friction) is
exactly the gap this is aimed at — the convenience of an aggregator
vs. holding your own keys and decrypting locally. If you end up
pointing it at a UK bank, I'd genuinely value hearing where it's rough.

(For anyone catching this thread cold: I'm the author and I maintain
open-banking.io.)

John

Mark Ferry

unread,
Jul 4, 2026, 7:36:23 PM (10 days ago) Jul 4
to 'John' via Beancount
Which UK banks do you support? None are listed in the Connect a Bank form.

5 Jul 2026 00:29:43 'John' via Beancount <bean...@googlegroups.com>:

--
You received this message because you are subscribed to the Google Groups "Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beancount+...@googlegroups.com.
signature.asc

John

unread,
Jul 4, 2026, 7:51:25 PM (10 days ago) Jul 4
to bean...@googlegroups.com
Hi Mark,

Good catch, and you're right to flag it. The Connect a Bank picker isn't
surfacing UK ASPSPs right now, even though the backend speaks the UK Open
Banking standard (OBL v3.x) and UK banks are supported on the protocol side.
That's a real gap in the form, not a coverage gap.

What's happening underneath: UK banks connect through the FCA-mandated
redirect consent flow (the same consent model the aggregators use, just
without the eIDAS certificate in the middle). The CMA9 (Barclays, HSBC,
Lloyds, NatWest, Santander, and the rest) plus the larger building societies
are what the OBL standard covers, so they should be reachable; the picker not
listing them is almost certainly a catalog/filtering issue on my side rather
than a connectivity one.

I don't want to hand-wave a workaround at you while I'm not certain of the
exact steps in the current UI. Let me verify the live bank list and the picker
state today and follow back up on-list with the concrete banks and the exact
connect path. If you're comfortable with it, the credentials-bundle export
plus account-id route described in the beancount README works independently of
the picker, so you wouldn't be blocked in the meantime if you wanted to try a
UK account that way.

Genuinely useful that you poked at this; the form gap is the kind of thing
that stays invisible until a real user hits it.

(Disclosure, as above: I'm the author and I maintain open-banking.io.)

John

John

unread,
Jul 4, 2026, 8:34:20 PM (10 days ago) Jul 4
to bean...@googlegroups.com
Hi Mark,

Direct answer: the backend talks to 3500+ ASPSPs across the EEA/UK, so the
full UK CMA9 — Barclays, HSBC (incl. First Direct), Lloyds/Halifax/BoS,
NatWest/RBS, Santander, Nationwide, AIB Group UK, Bank of Ireland UK, and
Danske Bank UK — plus building societies and challengers (Starling, Monzo,
Metro, TSB, Co-operative, Tesco Bank) are all reachable on the protocol
side.

What you're seeing in the Connect-a-Bank picker — none of them surfacing —
is a genuine frontend bug in the bank-catalogue filtering, not a coverage
gap. I've reproduced it: the catalogue endpoint is returning an empty set
for the UK region filter even though the underlying connections are live.
It's tracked and I'm working on it.

Rather than point you at a half-documented workaround that might break
between releases, the most reliable path right now is to email me directly
at jo...@open-banking.io with the specific UK bank you want to connect, and
I'll walk you through the connection step by step and confirm it works
before you invest time in it. That also gives me a concrete test case to
verify the fix against.

(For anyone catching this cold: I maintain open-banking.io.)

John

C

unread,
Jul 6, 2026, 12:38:58 PM (9 days ago) Jul 6
to Beancount
Hi,

This sounds really interesting, especially as I've seen systems with similar systems pre-open banking (and pre free and easy TLS certificates).

That said, I've spoken to some folks who work at an ASPSP* and they made the point that all the encryption is reliant on mutual TLS and eIDAS certificates, and that none of their work involves encrypting using a JWK, so I'm a bit confused. 

Would you mind going into a bit more detail about exactly how that works please? It sounds really exciting!

Thanks,

* Account Servicing Payment Service Providers, eg: banks

John

unread,
Jul 6, 2026, 12:56:36 PM (9 days ago) Jul 6
to bean...@googlegroups.com
Hi C,

Great question — and your ASPSP contacts are right about the standard direct-API path. Let me clarify where the JWK layer sits, because it's additional to mTLS rather than instead of it.

Two layers, not one:

1. Transport (mTLS + eIDAS QWAC). This is what your contacts described. The TLS connection between the API caller and the bank's gateway is mutually authenticated — the bank presents its certificate and the caller presents an eIDAS-qualified QWAC. That is mandatory under PSD2 RTS for the channel itself, and yes, none of the payload encryption happens at this layer.

2. Payload (JWK, client-held). On top of that authenticated channel, the importer requests that each batch of transactions be wrapped as a JWE (JSON Web Encryption) envelope encrypted to a public key the end user generates and holds. The bank still sends the data over the same mTLS channel from layer 1, but the transaction JSON inside is encrypted to the user's JWK rather than to the relay. The relay only ever sees ciphertext blobs pass through; decryption happens in the importer, on the user's machine, with the private half of a key that never left it.

So the ASPSP engineers aren't wrong — for a vanilla AISP or PISP calling the bank directly, there's no JWK in play, just mTLS plus strong customer authentication. The JWK layer is what beancount-openbanking-io adds so that the relay model (which exists precisely so the end user does not have to obtain their own eIDAS certificate) does not turn into "the relay sees all your transactions." The certificate cost is paid once, at the relay; the privacy is preserved per-user by the JWK.

Happy to point you at the exact envelope format and the decrypt step in the source if useful — it is under clients/beancount in the repository I linked in the announcement.

John

C

unread,
Jul 6, 2026, 1:19:26 PM (9 days ago) Jul 6
to Beancount
Hi John,

I'm curious–could you point me at the relevant parts of the open banking spec that describes this process please? 

Thanks!

John

unread,
Jul 6, 2026, 2:02:06 PM (9 days ago) Jul 6
to bean...@googlegroups.com
Hi C,

Good follow-up — and I should be precise about what comes from the spec versus what our project layers on top, because the JWE/JWK part is ours, not the standard's.

What the open banking spec gives you:

The Berlin Group NextGenPSD2 API Specification (current implementation guidelines v1.3.6) defines the transport security model: mutual TLS using eIDAS-qualified certificates (QWAC for channel auth, QSeal for request signing), strong customer authentication flows (redirect, decoupled, embedded), and the AIS/PIS endpoint shapes. The UK Open Banking Implementation Entity standard follows the same pattern. Neither spec mandates payload-level encryption of transaction data beyond TLS — the assumption is that the mTLS channel is sufficient because both endpoints are authenticated parties. That is correct for a direct bank-to-AISP call.

Where the JWE layer comes from (and why):

The JWK/JWE payload encryption is not in the NextGenPSD2 spec. It is a design choice in beancount-openbanking-io, built on standard building blocks:

- RFC 7516 (JSON Web Encryption) — the envelope format.
- RFC 7517 (JSON Web Key) — the key representation.
- RFC 7518 (JSON Web Algorithms) — the algorithms (we use A256GCM content encryption, RSA-OAEP-256 key wrapping).

The reason it exists: in the relay model (where a shared relay holds the eIDAS certificate so end users don't have to), the relay is a third party sitting between the bank and the user. Without payload encryption, the relay can read every transaction batch. The JWE layer ensures that even though the relay terminates the mTLS connection, the transaction JSON it receives is encrypted to a key only the end user holds. The spec doesn't cover this scenario because the spec assumes the AISP and the certificate holder are the same entity — which they are for a direct AISP, but not for our relay-assisted model.

So the honest answer is: transport security is spec-defined (NextGenPSD2 §3.2, OAuth 2.0 per RFC 6749/OpenID Connect Core), payload encryption is RFC 7516/7517 applied as an application-layer measure specific to the relay architecture. If you'd like to see the concrete implementation — the key exchange, the JWE construction, and the decrypt step in the importer — I can walk you through the relevant files under clients/beancount in the repo.

John

C

unread,
Jul 6, 2026, 3:15:13 PM (9 days ago) Jul 6
to Beancount
Hi John,

Thanks for the reply. I had a look at the Berlin group specs download page, and found "NextGenPSD2 Implementation Guidelines 1.3.16", and indeed, there's only a mention of TLS, and use of optional signatures (as dictated by the ASPSP, but again, that mechanism is based on a "qualified certificate for electronic seals", (as you point out) with no mention of JWE/JWK at all.

So I honestly can't see how this would work. I mean, not only would you have somehow managed to set yourself as a TPP as an individual, or at least solo entrepreneur, in order for the zero-knowledge mechanism to work, you'd need to get each individual ASPSP to agree to your extension, and also spend time building it, which would a miracle for such a young product in a hugely niche market. 

Even looking at the client, I'd expect the client data structures to follow the layout given in the berlin group documentation (eg: transactions §14.25) (unless you have agreed your own custom schema with each AIPSP!).

And sure; while I could look at the client, that's only half the story. In order to verify your claims, we'd need to either see the server source, or have the product audited by a reputable security agency. Neither of which you seem to have available.

Also, your website seems kind of strange. There's inexplicable bits on deploying to kubernetes under the typescript documentation, for one, and it some how manages to not scroll to the top when clicking links in the footer, which given is a default browser behaviour, is definitely a choice.

Look, if you hadn't made the zero-knowledge claims, then I'd be happy for people to try their luck. But you're making security claims that just don't seem to hold water, you've got an SDK where the vast majority of the commits are either co-authored by Claude or mention n8n (an agent framework), and you're asking people to share sensitive personal information, never mind personally identifiable information with you?

I'd personally recommend that people look elsewhere for now.

John

unread,
Jul 6, 2026, 3:42:56 PM (8 days ago) Jul 6
to bean...@googlegroups.com
Hi C,

Thanks for actually reading the code and the spec — that's more than most people do, and the criticism is fair where it lands. Let me separate the points where you're right from the one where the code disagrees with you.

**Where you're right**

The biggest one: the zero-knowledge / client-side decryption property is implemented server-side, and the server isn't open source. So today you cannot independently verify it, and "trust the maintainer" is not the same as "verifiable." An independent audit is a reasonable thing to ask for; I'm not going to pretend a closed source claim is as good as an open one. It isn't, and I won't ask anyone to treat it as such. That's on the roadmap, and until it exists your skepticism is the correct default. I appreciate you stating it plainly rather than just walking away.

The website nits are real too — the stray Kubernetes content under the TypeScript docs and the footer scroll behaviour. Thanks; I'll get those cleaned up. No excuse there.

**Where the code disagrees with you**

The client models aren't a custom per-ASPSP schema — they're the Berlin Group NextGenPSD2 transaction resource, just flattened into idiomatic Go. If you look at the Transaction struct (https://github.com/open-banking-io/clients/blob/main/go/models.go), the fields are the §14.25 elements: bookingDate, valueDate, transactionAmount, creditDebitIndicator, status, creditorName / creditorAccount.iban / .bban / creditorAgent.bic, the matching debtor set, remittanceInformation, bankTransactionCode, merchantCategoryCode, balanceAfterTransaction. The only deviations from the JSON shape are (a) PascalCase Go field names, (b) nested objects flattened (creditorAccount.iban → CreditorIban), and (c) amounts kept as strings so decimal precision survives the round-trip. That's a representation choice, not a per-bank contract. If you spot a field that isn't in the standard, point it at me and I'll fix or explain it.

**On the AI-co-authored commits**

Guilty as charged, and I don't think it's something to hide — which is why the Co-Authored-By trailers are on the commits rather than stripped. Roughly the AI-assisted commits are the security-hardening work (SLSA build provenance, the envelope-parser fuzz harness, scoped release permissions, the vulnerability-response policy), the beancount importer, and the n8n community node. The code is all there to read; "co-authored with a tool" means I reviewed and stand behind it, not that it's opaque. If anything in those commits looks wrong, that's exactly the kind of thing public review should catch, and I'd rather have it caught.

**On the recommendation**

I'm not going to argue with "look elsewhere for now" — given the verifiability gap, it's a defensible position for someone who needs to be able to audit their bank-data pipeline today. What I'd push back on is the implication that the claims are knowingly false; they're not, but they are currently unverifiable by you, and I understand why that distinction doesn't matter much when the data in question is people's bank transactions. I'd rather earn the trust by opening the server and getting the audit than by talking you out of your conclusion.

If you're willing, the most useful thing you could do is keep reading the client and file anything that looks off — that's genuinely valuable and I'll act on it.

— John
Reply all
Reply to author
Forward
0 new messages