The bank list
This is the file the app fetches when you turn bank list updates on. It describes how to read each bank's alert emails. It is the same file for everyone and contains nothing about anybody.
The files
patterns.json— the list itself, as readable text.patterns.json.sig— its signature.
Both are plain text on purpose. You should be able to read what your phone accepted without needing a tool for it.
What is in it
For each bank: the email address its alerts come from, the subject line to look for, and the patterns that pull out the amount, the card and the shop. That is the whole file. There are no user accounts in it, no addresses, no transaction data — it is a description of email formats.
How the signature works
The signature covers the exact bytes of patterns.json. The app
carries the matching public key and checks the signature before it
reads the file at all. A list that has been altered, substituted, or served by
somebody else does not verify, and the app keeps the list it already had.
Three more things the app checks, because a valid signature alone is not enough:
- The version must be newer. A signature proves who wrote a file, not that they still stand behind it — so an older list, replayed by anyone who kept a copy, is refused.
- The app must be new enough to understand the format, or it keeps what it has rather than misreading the new one.
- A list that would remove a bank you are using is refused. Banks can be retired, but they are delivered switched off so you can see it, never silently dropped.
Checking it yourself
The signature is Ed25519 over the file's bytes. The public key is published here, because a key you have to go and find is a check nobody makes:
key_id 6071f4a8 public Lcc1ZWO/D2wVG3o7Y8yPNZ2s/7uRDS6M+c9djfTLOfw=
The same key id appears on the app's About screen, so you can confirm the phone is checking against the key published here and not something else.
curl -sO https://budget-tracker.sahadeosolutions.ca/patterns.json
curl -sO https://budget-tracker.sahadeosolutions.ca/patterns.json.sig
python3 - <<'PY'
import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
key = Ed25519PublicKey.from_public_bytes(
base64.b64decode("Lcc1ZWO/D2wVG3o7Y8yPNZ2s/7uRDS6M+c9djfTLOfw="))
sig = [l.split(":", 1)[1].strip()
for l in open("patterns.json.sig") if l.startswith("signature")][0]
key.verify(base64.b64decode(sig), open("patterns.json", "rb").read())
print("verified")
PY
Two keys can be trusted by the app at once, so one can be replaced without every phone having to update on the same day. If the key above ever changes, the old one keeps working until the app that trusts only the new one has shipped.
Because the signature covers the exact bytes, reformatting the file — even adding a newline — will make it stop verifying. That is deliberate: it means there is no gap between the thing that was checked and the thing the app reads.