Appearance
Bulma Banking Stack
type: reference
owner: bulma + trunks + jimmy-neutron
first_touched: 2026-05-24
last_touched: 2026-06-11
Dual-Provider OAuth Architecture
source: documentation/activities/reference_bulma-banking-stack.md
Rule: Bulma uses BOTH TrueLayer AND Monzo OAuth in parallel, not one or the other.
Why: Monzo's own API gives the cleanest data (categorised transactions, pots, merchant enrichment) but only covers Monzo accounts. TrueLayer covers everything else (Natwest, savings, credit cards). Both needed for net-worth visibility.
How to apply
CT109
bulmaschema: tables includesource ENUM('monzo','truelayer')+providercolumnCredential storage: separate token files per provider in
/mnt/data/hinata/data/bulma/(Z2 bind mount). Vaultwarden (CT103, 192.168.1.250) is the canonical credential store.CT109 systemd timers:
bulma-poll-monzo+bulma-poll-truelayer, each every 15 min (*:0/15)Never recommend "just use one provider" — deliberate design choice for data coverage
Status (2026-06-11)
Both providers live on CT109 bulma-finance (192.168.1.214). Monzo migration completed 2026-06-11. TrueLayer (NatWest credit) polling on CT109 since 2026-06-11. Mac is reauth surface only for both providers.
Five-Tab Rebuild — Producer-Iframe + React-Comparative Fusion
source: documentation/activities/bulma-tab-rebuild.md
Tab structure
TabSourceRole
Homeiframe(bulma-report-home.html)Full producer report minus Tactical Intelligence block
WeeklyReact comparative panel + iframe(bulma-report-weekly.html)WTD vs Last Week + 7-day strip + BUDGETS table + producer HTML beneath
MonthlyReact comparative + iframe(bulma-report-monthly.html)MTD vs Last Month + 3 KPI cards + BUDGETS ledger + producer HTML beneath
YearlyReact-onlyYTD vs Last Year (producer has no yearly report)
Behaviouriframe(bulma-report-behaviour.html)Full #ml-insights block
Architecture decision — splitter, not producer rewrite
report.py is 7,403 lines. Adopted: split-bulma-report.py post-processor reads producer output and injects tab-mask overlays. Producer is untouched.
Pipeline order
refresh-and-deploy-data.sh
Step 0 build-tokens.py
Step 1a tasks-to-{memory,lanes}
Step 1b refresh-{tasks,session-burn}
Step 1c sync producer report*.html → public/data/bulma-report*.html
Step 1c run split-bulma-report.py
Step 1d refresh-bulma-comparative.py → bulma-comparative.json
Step 2 md5 hash, deploy if changedReact comparative panel features
7-day spend strip — mini bar chart, daily totals, today highlighted in
var(--accent)BUDGETS table — per-category target vs actual, status chip (OK/WARN/OVER), delta %
Pace projection (Monthly only) —
pace = MTD / proRata3 KPI card row (Monthly) — MTD spend + full-month pace + vs previous month
Failures + lessons
1. CSS `@import` order bug — import must precede every other rule or browsers silently drop it
2. Squidward palette as accent reverted — "palette" was ambiguous; base stayed (slate), accent reverted to Hinata orange
3. Dashboard.tsx mid-session strip + restore — strip operations need git tags or backup comments
4. Pipeline step ordering — splitter must run AFTER syncing weekly/monthly HTMLs
5. Producer hex categorisation drift — some Monzo transfers tagged general not transfer; accepted
Open
* Producer report.py 7,400 inline hexes still in warm-orange/indigo palette — migration backlogged
* report_monthly.html last regenerated April — stale until June 1st
* ~50 `var(--accent, #4a9eff)` fallback hexes in MusicMastery/SolfegeView are dormant violations; sweep deferred
Monzo Live Transactions
source: documentation/activities/bulma-monzo-slice1.md
Monzo polling runs on CT109 bulma-finance (192.168.1.214) as systemd timer bulma-poll-monzo, every 15 minutes (OnCalendar=*:0/15). Migration completed 2026-06-11. First CT109 poll: 2 accounts, 40 transactions.
Data plane
Z2 host /mnt/data/hinata/data/bulma bind-mounted into CT109 at /root/data/bulma. Tokens live in the bind mount; Z2 is the sole token writer. CT109's refresh rotates the chain.
Collector endpoint
http://192.168.1.153:8090/bulma (LAN). Host Tailscale IP unreachable from LXC; poll-monzo.py uses LAN only.
Mac role
Reauth surface only. reauth-monzo.py --force writes to ~/Sandpit/hinata-sandpit/data/bulma/, then push-monzo-tokens.sh copies to Z2. Mac tokens_monzo.json deleted after first CT109 poll; monzo_credentials.json stays for next reauth.
Monzo SCA gate
After browser OAuth, API calls return HTTP 403 until approval is tapped in the Monzo app. Not a token failure. Poll succeeds on next fire (within 15 min) once approved.
Credential discipline
- Never read or print token values — only lengths, expiry epochs, and "refresh ok" event types
Slice 2 (deferred)
UI wire-up — Bulma.tsx live-transactions panel reading /data/bulma-monzo.json.
Fees & Interest Panel Shipped
source: documentation/activities/bulma-fees-panel.md
#840044 — Natwest-style accumulated fees + interest panel on Bulma Monthly tab. 2026-05-26 evening.
Files
* `src/lib/feeInterestDetector.ts` — pure 220-line detector with word-boundary regex + category whitelist
* `src/components/Bulma.tsx` — new `FeesInterestPanel` wired into Monthly tab top
* `public/data/bulma-monzo-fixture.json` — 24 synthetic txs for DEV mode fallback
Verification logic
* Word-boundary regex on fee patterns (FEE, INTEREST, CHARGE, OVERDRAFT, PENALTY, LATE PAYMENT, etc.)
* Category whitelist: charges, fees, interest, overdraft
* Only debits (amount < 0) count as fees-paid
* Multi-account merge by source field
Pre-req status
Monzo polling live on CT109 (2026-06-11)
TrueLayer polling live on CT109 (2026-06-11)
◆ hinata · bulma-banking-stack · folded from documentation/activities/ phase-19