Skip to content
Ballo Innovations2026 — present
In buildaiarchitecturemobile

Penda

A money app whose AI assistant can act on your finances but cannot quietly rewrite them

A personal finance app with a chat assistant that reads and acts on a user's own ledger. A model saying something wrong isn't the risk that matters; users discount that easily enough. The risk is a model being confidently wrong about a number and quietly overwriting history, because the whole value of a finance tool is that you believe the balance.

Penda interface
Built for
Ballo Innovations
My role
Architecture and implementation of the assistant's tool layer, the confirmation and staging model, and the row-level security boundary it all runs inside.
When
2026 — present
  • Deno
  • Supabase Edge Functions
  • PostgreSQL
  • Row Level Security
  • Gemini
  • Groq
  • TypeScript
Write-up

The longer version

The problem

An assistant that can only answer questions about your money is a search box. An assistant that can act on your money is genuinely useful, and also one bad turn away from destroying data the user can't reconstruct. Creates are recoverable. A spurious row shows up in a list and you delete it. Updates and deletes are where a confused agent overwrites a number that now exists nowhere else, and the user might not notice for a month.

What I built

The confinement model: two functions rather than one, with the model-facing function structurally incapable of a destructive write. Everything else follows from that. The staging table, the snapshots for undo, the graduated trust flag that lets a consistently-correct user stop confirming small creates. All of it comes from wanting the guarantee to be a property of the code, not of how the model happens to behave on a given day.

What I would not claim

There's no prompt-injection defence here. What exists is capability confinement, which is a different property and in some ways a stronger one, but it isn't the same claim. Nothing tags untrusted text as untrusted. The argument is only that the model's reachable actions are too narrow for an injected instruction to do much with.

The allowlist is also enforced at more than one layer, across two runtimes. Each copy is deliberate defence in depth; keeping them in agreement is not free, and it is the next thing to fix.

01Look & feel

Screens and demos

What it looks like in use, not just what it was designed to do.

  • Penda's launch screen, a wallet mark above the name and the line "AI-first money tracking"
    Launch
  • Penda home screen with a morning greeting, an install-to-home-screen prompt and a finish-setup card
    Home: installable, and it works offline
  • Chat thread where a spend of K80 on a ride is logged in plain language and the assistant ties it back to a debt goal
    The assistant logging a spend from a sentence. A create, which is the only kind of write it can perform unstaged
  • Persona picker for the assistant, offering a hustler, a balanced coach, an analyst, an exasperated parent, a drill sergeant and a comedian
    Who the assistant is in chat: tone only, and it changes no permission
  • Profile screen with plan tier and a mode selector for individual, couple, family or business wallets
    Profile: the mode reframes the advice over the same money
  • Insights screen with spend by category, a daily spending heatmap for the month, and a weekly AI digest card
    Insights: spend by category and a daily heatmap
02The engineering

What made it hard

The problems worth reading about. Everything else in this system was ordinary work.

  1. 01

    The assistant can create a transaction but can't edit or delete one. Not because the prompt says so, but because the function holding the model contains no code that performs an update. A prompt is a suggestion a model can misread. It was never going to be a security boundary.

  2. 02

    Destructive intents get written to a staging table and executed by a different function that only runs when the user taps confirm. Tiering is by blast radius rather than by SQL verb, so a large or multi-field create gets staged too.

  3. 03

    The assistant runs under the user's own credentials, so what bounds its reach is database row-level security rather than application code.

03Numbers

What it measures

Each figure says where it came from, so you can judge how much weight to give it. Some are measurements and some are chosen thresholds; the basis line tells you which.

0
Tools that can reach an update or delete

Structural, and the whole point of the design: the chat function's tool dispatch has no case that performs an update or a delete. You verify it by reading the dispatch, not by sampling behaviour.

2
Independent allowlists a write must satisfy

One at staging time in the chat function, one at execution time in the confirm function. A third mirror exists client-side for undo.

40 s
Turn budget

Chosen after working out the unbounded case. A twelve-second model timeout across two providers over four tool iterations stacked up to roughly 160 seconds. The wall-clock budget caps that directly.

Decision records

The close calls, written up

Where a choice was genuinely arguable, I wrote it up the way a team records a design decision internally: the constraint, the options I turned down and why, and what it would cost to reverse.

  • ADR-004acceptedAI Safety30 Jul 2026

    Confining what the assistant can write

    The chat assistant in Penda can create a transaction, but it cannot edit or delete one. Not because the prompt tells it not to, but because the function holding the model has no code that performs an update. Destructive intents are written to a staging table and executed by a different function that only runs when the user taps confirm.

    4 options evaluated