UNIFIED FINANCE — link the accounts, feed the fleet safely (Proposal v2 · draft for weekend review)

For: Byron · Host: Mia · Nature: read-only aggregation · Sensitivity: maximum (bank data + bearer tokens + LLM exposure)

Scope assumption (correct me): one read-only view of balances, transactions, holdings, net worth — that you and the fleet can query, and that drives budget cards/alerts. No money movement. Transfers/bill-pay = a different, higher-risk proposal.


Executive summary

What. Link your institutions once, aggregate balances + transactions + holdings into an encrypted store on Mia, and expose it to the fleet through a read-only, aggregates-only interface plus a tailnet-only dashboard — so "travel spend this month," "net-worth trend," "flag charges over $X" become one query, wired as real actions.

Two things a hard review changed, and they're load-bearing:

  1. Plaid may be the wrong tool. Plaid production access requires a company/app review, privacy policy, and security questionnaire — "one person's Mac Mini fleet" isn't a sanctioned use case, and getting shut off would brick the project. SimpleFIN Bridge is built for exactly personal, read-only aggregation: clean ToS for individuals, no production charade, and a far smaller attack surface (no Plaid keys, no webhook infra). Phase 0's first job is Plaid-vs-SimpleFIN for your actual institutions — coverage is the only axis where Plaid clearly wins.

  2. The real risk isn't strangers — it's the agents. The original draft secured tokens from outsiders and then handed the data to the least predictable actors in the system (LLMs) with no controls. Financial PII flowing into a cloud LLM context would ship your finances to a third-party inference provider on every budgeting question — silently breaking "private." And transaction memos are attacker-controllable strings (anyone who can cause a charge writes text your agents read) → a prompt-injection/exfiltration path. So this v2 is built around token isolation, an aggregates-only agent interface, an explicit LLM data-flow policy, and per-agent authorization — not just "encrypt and tailnet."

Decision sought. Approve the direction (read-only, provider TBD, safety-first) and Phase 0, which resolves the four gating questions below before any account is linked.

The four Phase-0 gating questions (must answer before Phase 1)

  1. Provider: SimpleFIN vs Plaid for your real banks/cards/brokerage — coverage + ToS + cost, measured.
  2. LLM data-flow policy: which models may ever see finance data. Default stance: raw rows never leave Mia; only aggregates may reach a model, and only a local model unless you opt in. The MCP tool returns summaries, not transaction dumps.
  3. Token isolation boundary: how bearer tokens are made architecturally unreachable from the agent runtime (below).
  4. Webhook vs poll: default poll-only — a webhook needs a public endpoint, which contradicts tailnet-only. Poll keeps zero public surface.

Architecture (safety-first shape)

Link (one-time, you) → tokens held ONLY by a dedicated `finance-sync` OS user
   finance-sync: talks to provider (read-only products), writes finance.db
        │   (agents CANNOT read tokens or the provider API — different user, no access)
        ▼
finance.db  — SQLCipher-encrypted; key in the sync-user's keychain, not on disk plaintext
        │   ingest FILTER: account masks only, no /auth, no full numbers, field allowlist
        ▼
finance-api (read-only socket) — returns AGGREGATES ONLY (never raw rows to agents)
        │   per-agent allowlist · tiered access · rate limit · full audit log · freshness stamp
        ▼
finance.arnao.ai (TAILNET-ONLY dashboard, raw view for YOU only)  ·  minimized alerts

The agent runtime and the token vault are separate OS users on the same box — an agent RCE or MCP bug is one privilege boundary away from tokens, not zero.

Security posture (the crux — corrected)

  1. Read-only products only — no payment/transfer scopes. Can see, never move.
  2. Token isolation — a finance-sync user owns tokens + provider API; agents reach only a socket serving derived data. Tokens are unreachable from the MCP layer by construction.
  3. Do NOT back up tokens — they're cheap to recreate (re-link ~10 min). Backing them up copies bank credentials into every backup location. Back up finance.db (encrypted) only; token loss is a drilled re-link runbook, a non-event. (Corrects v1, which had this backwards.)
  4. LLM data-flow policy — raw transactions never enter model context; agents get aggregates; cloud models only with explicit opt-in per query class; a local model is the default for anything touching finance.
  5. Transaction text is untrusted — treated as data, not instructions; the finance tool's outputs are constrained; no single agent turn combines finance-read with unconstrained outbound tools (kills the memo-injection → exfil path).
  6. Per-agent authz + tiered access + audit + rate limit — most agents get "budget summary"; almost none get raw; every query logged (who/what/when); anomalous-query alerting is the compromise detector.
  7. finance.db encryption specified — SQLCipher (not FileVault-only, which protects only a powered-off Mac).
  8. Minimized alerts — Telegram is not E2E; alerts say "large charge detected — check dashboard," never the merchant/amount. No PII in the notification layer.
  9. Item health is a first-class citizen — items rot constantly (ITEM_LOGIN_REQUIRED, OAuth re-consent, MFA changes). Every agent answer carries a freshness stamp; a stale feed silently answering "runway" with month-old data is worse than none. Staleness → re-link alert.
  10. Transaction lifecycle handled — pending→posted mutation, removals, sync-cursor, dedup — or "spend this month" is simply wrong.
  11. Exit plan + retention — documented provider-abandonment/export path; a transaction-history retention/aging policy, not indefinite accumulation.

What it enables (wired actions, per your rule — not passive links)

Phases

Phase Scope Effort
0 Gating decisions: provider (SimpleFIN vs Plaid, tested on your institutions) · LLM data-flow policy · token-isolation design · poll-vs-webhook · pricing confirm · encryption mechanism 1 day
1 Link accounts (you, once) · isolated token store · SQLCipher finance.db · ingest filter · initial pull 1–2 days
2 Sync (poll) w/ txn-lifecycle handling · aggregates-only finance-api · per-agent authz + audit + rate limit · minimized alert rules 2–3 days
3 Tailnet dashboard (raw for you) + action-board budget cards · freshness everywhere 2 days

What I need from you

  1. Confirm read-only aggregation scope (or say if transfers are wanted — different tier).
  2. Which institutions (banks/cards/brokerage) — so Phase 0 tests provider coverage.
  3. Your call on the LLM policy: local-model-only for finance, or allow aggregates to a cloud model on specific query classes?
  4. Agree finance.db is tailnet-only + in the backup rotation (tokens are NOT backed up).

Self-critique — the Fable adversarial pass

Draft 1 was run through Claude Fable 5 as a hostile fintech-security review; it called v1 "a security vibe, not a security design." The load-bearing catches, all now fixed:

Fable's verdict on v1: "do not approve Phase 1 as written." v2 answers all seven MUST-fixes and reframes around the agent-data-flow risk it flagged as the biggest gap. This is a draft for your weekend review — the four Phase-0 gating questions are where your input decides the shape.