Money trace service — fund origin trees and bank mail ingestion
Go to file
Kavi e4e8f20555 Twemoji category icons as logo fallback
For transactions with no brand match, show the spending category emoji
(Twitter Twemoji SVG via jsDelivr) instead of a bare initial:
  Groceries 🛒  Food 🍔  Fuel   Health 💊  Subscriptions 💻
  Shopping 🛍  Utilities 💡  Cash 🏧  Debt 💳  Wallets 📱  Fees 🏦
Income/transfer rows use flow-type icons (💰 income, 🔁 self-transfer).
Resolution chain: Brandfetch brand logo -> category Twemoji -> coloured
initial. Brand logo onerror also falls back to the category emoji.
Call sites pass {category} (merchants/breakdown) or {flowType} (ledger).
2026-06-02 14:44:20 -04:00
config chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
data chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
docs chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
parsers Fix BancoEstado parser for wrapped-header CuentaRUT layout 2026-06-02 14:23:44 -04:00
scripts Add Brandfetch merchant logos to dashboard 2026-06-02 14:41:09 -04:00
src Add dashboard.html and static file serving to server 2026-06-01 22:38:51 -04:00
test chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
web Twemoji category icons as logo fallback 2026-06-02 14:44:20 -04:00
.gitignore Add 2019-2024 backfill ledger (separate) + ?ledger= dashboard param 2026-06-02 07:32:21 -04:00
.kua-vault.json chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
README.md chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
debug-labels.js chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
kua.json chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
list-labels.js chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
package-lock.json chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
package.json chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00
schema.sql chore: restore kua-money-trace from Bruno (source was on Bruno, Gal copy was lost) 2026-05-01 03:00:31 -04:00

README.md

kua-money-trace

Servicio separado para reconstruir el arbol de origen y destino del dinero.

No reemplaza contabilidad ni aprobacion humana. Guarda hechos, propone enlaces y permite ver de donde vino la plata que termino financiando cada gasto.

Primer MVP

  • Ledger en JSON local.
  • Motor de grafo con enlaces explicitos y enlaces FIFO por cuenta.
  • Trazabilidad hacia atras desde cualquier movimiento, documento o evento economico.
  • API HTTP minima sin dependencias externas.
  • CLI para resumen y pruebas rapidas.

Ejecutar

npm test
npm run summarize
npm run demo
npm run serve

Luego:

curl http://localhost:3910/health
curl http://localhost:3910/nodes/event:black-spa-10804/origin-tree

Descargar correos

Primera cuenta configurada:

vjoati-gmail -> vjoati@gmail.com
kdoi-email -> kdoi@email.com

La clave no se guarda en el repo. Para Gmail se debe usar una app password/OAuth y exponerla solo en el entorno:

export VJOATI_GMAIL_APP_PASSWORD='...'
npm run mail:dry-run
npm run mail:download -- --limit 25 --since 2025-01-01

Gmail API OAuth

El camino recomendado para Gmail es OAuth con permiso solo lectura. Necesita un OAuth Client de Google Cloud con redirect URI http://127.0.0.1:3912/oauth2callback y la Gmail API habilitada.

export GOOGLE_OAUTH_CLIENT_ID='...apps.googleusercontent.com'
export GOOGLE_OAUTH_CLIENT_SECRET='...'
npm run mail:gmail-oauth

El comando imprime una URL, espera el callback local y guarda el refresh token en:

data/mail-oauth/vjoati-gmail.token.json

Luego descarga desde Google, no desde Apple Mail:

npm run mail:gmail-download -- --limit 100 --since 2025-01-01

El archivo queda en:

data/mail-archive/vjoati-gmail/
  raw-eml/yyyy/mm/*.eml
  attachments/yyyy/mm/*
  manifests/emails.ndjson

Para probar sin tocar Gmail:

npm run mail:fixture

Si Gmail no permite app password, hay dos caminos ya soportados:

Apple Mail local

Lista cuentas/carpetas locales de Mail.app:

npm run mail:list-apple

Importa una carpeta .mbox local de Apple Mail:

node src/mailCli.js import-apple-mail \
  --account vjoati-gmail \
  --source '/Users/kavi/Library/Mail/V10/.../INBOX.mbox' \
  --limit 25

Para cuentas ya configuradas en macOS, es mejor usar el indice de Mail. Esto resuelve la cuenta desde ~/Library/Accounts/Accounts4.sqlite, lee Envelope Index, y archiva los .emlx locales por id de mensaje:

node src/mailCli.js import-apple-mail-index \
  --account vjoati-gmail \
  --mailbox all \
  --since 2025-01-01 \
  --limit 100

En Gmail, --mailbox all usa [Gmail]/Todos cuando existe y si no cae a INBOX. Para la cuenta kdoi-email, el importador detecta la IMAP directa con mensajes y usa INBOX.

Atajos:

npm run mail:import-apple-index -- --limit 100 --since 2025-01-01
npm run mail:import-kdoi -- --limit 100 --since 2025-01-01

Google Takeout / MBOX

Exporta Gmail desde Google Takeout como archivo .mbox, luego:

node src/mailCli.js import-mbox \
  --account vjoati-gmail \
  --file '/ruta/al/archivo.mbox' \
  --limit 1000

Idea central

Un pago con tarjeta no es el origen final. El arbol correcto puede ser:

DTE / gasto Muralla
└─ cargo Visa Darwin
   └─ pago Visa desde cuenta corriente Darwin
      └─ ingreso puro Darwin

Y para cuentas europeas:

Gasto con debito Revolut
└─ saldo Revolut EUR
   └─ carga Revolut con Visa Chile
      └─ pago Visa desde cuenta corriente Chile
         └─ ingreso puro

Proximos pasos

  • Importadores para cartolas Banco de Chile, Santander, Revolut, Mercado Pago y SII RCV.
  • Archivo de correos y adjuntos en Storagebox.
  • Extraccion IA de PDFs/correos con estado propuesto, nunca aprobado automaticamente.
  • Persistencia Postgres con auditoria de decisiones.