← Beau Sumile

Mahi Kala

Reconciled financial truth for complex households.

Private beta Honolulu · Tax Accountability Services, LLC Web app · US households · No data resale

Most personal finance apps stop at "the aggregator says your balance is $X." That's a feed, not a ledger. When the aggregator drops a statement, miscategorizes a transfer, or fails silently for a week, the user is the last to know — and there's no second source to verify against.

Mahi Kala treats every fact in the ledger as a claim that needs evidence. Bank-side data flows in through one or more aggregators. Statement-side data flows in through user-uploaded PDFs, CSVs, and email attachments — extracted, validated, and reconciled before it touches the ledger. The two sides have to agree, and when they don't, the user sees exactly which account, which period, and which side disagrees.

01

Reconciled, not aggregated

Aggregator data is checked against statement evidence on every close period. Gaps surface as actionable exceptions, not silently wrong totals.

02

Honest freshness

Five distinct dates per source — attempt, success, data-as-of, next-expected, user-acknowledged — drive a single derived status that doesn't lie about how current a number is.

03

Verifiable privacy

"Delete my data" leaves zero residue across database, disk, and aggregator-side connections. The wipe is tested with an assertion that every household-scoped table comes back empty.

The system on one page

Four kinds of source feed one reconciled ledger. The ledger powers the user views — net worth, reconciliation report, freshness dashboard. Every workflow below is a slice of this picture.

graph TB subgraph SRC["Data sources"] direction LR P[Plaid] T[Teller] D[Documents] M[Manual] end SRC --> EXTRACT[Extract & normalize] EXTRACT --> VALIDATE{Validate} VALIDATE -->|valid| RECON[Reconciliation engine] VALIDATE -->|invalid| EXCEPTION[Exception queue] RECON --> LEDGER[(Ledger)] LEDGER --> NW[Net worth] LEDGER --> REPORT[Reconciliation report] LEDGER --> HEALTH[Source health] LEDGER --> AI[AI context]

Core workflows

01

Connect a bank

Users see one "Connect a bank" affordance. Mahi Kala routes the link to the aggregator with better coverage and pricing for that institution — Teller's per-enrollment economics for major US banks, Plaid for the long tail.

Every enrollment is signed by the aggregator with Ed25519 over a server-issued nonce. Replay attacks where a stolen token gets POSTed to plant a ghost connection are rejected before they reach the ledger.

sequenceDiagram participant U as User participant B as Backend participant A as Aggregator U->>B: GET link-config B->>B: Mint nonce B-->>U: nonce + config U->>A: Connect widget A->>U: token + signatures U->>B: POST enrollment B->>B: Verify Ed25519 B->>B: Consume nonce B->>B: Write linked_item B-->>U: ok
02

Sync, with honest freshness

A scheduler runs sync per source on its expected cadence. last_attempt_at stamps every run; last_success_at and data_as_of only move forward when real data arrived — a webhook firing doesn't qualify.

The distinction drives one derived freshness_status: healthy, waiting, missing-statement, stale, needs-reauth, failed-transient, failed-permanent, manual, paused, or setup-needed. Every account carries the status as a colored pill.

flowchart TB S[Scheduler] --> AT[record_attempt] AT --> C[Fetch source] C --> R{Data?} R -->|advanced| OK[record_success] R -->|no data| NO[Attempt only] R -->|error| FA[record_failure] OK --> D[Derive status] NO --> D FA --> D D --> B[Badge + dashboard]
03

Statement ingestion

The wedge. For institutions where the aggregator is unreliable or unsupported, the user uploads a statement — PDF, CSV, OFX, or screenshot. Deterministic parsers handle structured files; vision and document AI handle the rest.

The extractor produces a candidate statement, never a ledger write. Candidates go through hard validation (totals match, period exists, opening + flows = closing) and reconciliation against the existing ledger. The user reviews before any row hits production. No LLM output writes directly to financial data.

flowchart TB U[Upload] --> CL{Classify} CL --> EX[Extract candidate] EX --> V{Validate} V -->|fail| Q[Review queue] V -->|pass| RE[Reconcile vs ledger] RE --> PV[Preview to user] PV --> C{Confirm?} C -->|yes| L[(Atomic ledger write)] C -->|no| RJ[Reject, keep doc]
04

Reconciliation report

The single screen that answers: do my balances tie to source evidence, and where don't they? Per-account balance tie-out, transfer-matching state, missing-statement recommendations, source health, and open exceptions — all in one scorecard with a "fully reconciled" boolean for the headline.

This is the surface that justifies a premium price tag — no aggregator-only app gives it.

flowchart LR A[Accounts] --> B[Balance ties] R[Events] --> T[Transfer state] REC[Recs] --> M[Missing statements] EV[Action req'd] --> X[Open exceptions] SR[Sources] --> H[Health] B --> S[Scorecard] T --> S M --> S X --> S H --> S S --> UI[Reconciliation tab]
05

Verifiable privacy wipe

Financial data is sensitive by definition. Three deletion paths ship: reset (clear data, keep account), delete-household, and delete-account. Each runs leaf-first through every household-scoped table, removes uploaded documents from disk, and revokes aggregator-side connections so the bank stops sharing.

The wipe is tested with an assertion that verify_zero_residue() returns an empty dict — every table, every file. The only thing retained is a single audit row saying "this household wiped on date X."

flowchart TB U[User type-confirms] --> RV[Revoke aggregators] RV --> F[Unlink raw docs] F --> D[Leaf-first DELETE] D --> CA[Cascade FK] CA --> V{verify_zero_residue} V -->|empty| AU[Audit row only] V -->|residue| FL[Refuse done] AU --> R[Show counts cleared]

Architecture

Four kinds of data source share one ledger. The connector layer normalizes each provider's shape into the same internal rows — Plaid items, Teller enrollments, document-fed sources, and manual entries all hit the same write path. A source can have multiple roles per account over time and the system tracks each independently.

flowchart TB subgraph CONN["Connector layer"] PA[providers/plaid] TA[providers/teller] DA[extractors:
deterministic · vision · openai] MA[manual endpoints] end subgraph SRC["Source registry"] DS["data_sources
kind · cadence ·
freshness timestamps"] SA["source_accounts
account ↔ source ↔ role"] end subgraph LED["Ledger"] AC[accounts] TX[transactions] BA[balances] IN[investments] AS[assets] RE[reconciliation events] end subgraph VIEW["User-facing"] OV[Overview] REP[Reconciliation] SH[Source health] AT[Assets] end CONN --> DS DS --> SA CONN --> AC CONN --> TX CONN --> BA CONN --> IN CONN --> AS LED --> VIEW SA --> VIEW

What's different

Most personal finance apps optimize for aggregator coverage breadth. Mahi Kala optimizes for whether the user can trust the number on screen. That's a different bet.

Capability Aggregator-first apps Mahi Kala
Aggregator coverageBroader (13k+)Major US + statement-fed long tail
Statement reconciliationNot supportedPer-account tie-out vs evidence
Document ingestionCSV only, if at allPDF · CSV · OFX · vision
Explicit freshness stateOne timestampFive dates · derived enum
Transfer matching stateHeuristic onlyMatched · pending · inferred · external · review
Verifiable data deletionUnclear scopeZero-residue + revoke + unlink
AI-native contextRule-basedAnthropic + OpenAI across extraction · categorization · queries

Status

Mahi Kala is in private beta. The architecture above is shipped: multi-aggregator linking, statement ingestion, reconciliation report, freshness layer, assets registry, and the verifiable-wipe data-deletion paths all run in production today. Onboarding is founder-led; the design partner cohort is small and intentional.

Built in Honolulu under Tax Accountability Services, LLC. Household-scoped, web app, US-only, no data resale.