<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Henil Sarang</title>
    <description>The latest articles on DEV Community by Henil Sarang (@henil_sarang_8f2a1ff6e391).</description>
    <link>https://dev.to/henil_sarang_8f2a1ff6e391</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4165961%2F9b4dfb71-3821-435b-8b9f-cb5a9fdd92ec.jpg</url>
      <title>DEV Community: Henil Sarang</title>
      <link>https://dev.to/henil_sarang_8f2a1ff6e391</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/henil_sarang_8f2a1ff6e391"/>
    <language>en</language>
    <item>
      <title>Why my budgeting app will never ask for your bank password</title>
      <dc:creator>Henil Sarang</dc:creator>
      <pubDate>Tue, 06 Oct 2026 09:35:57 +0000</pubDate>
      <link>https://dev.to/henil_sarang_8f2a1ff6e391/import-only-by-design-building-a-self-hosted-finance-app-for-a-country-without-open-banking-3b0h</link>
      <guid>https://dev.to/henil_sarang_8f2a1ff6e391/import-only-by-design-building-a-self-hosted-finance-app-for-a-country-without-open-banking-3b0h</guid>
      <description>&lt;p&gt;I didn't want to build another password honeypot.&lt;/p&gt;

&lt;p&gt;Canada still doesn't have working open banking for consumers. So almost every budgeting app that "connects to your bank" does it the same way: you hand over your online banking password. The same password your bank's terms tell you never to share with anyone.&lt;/p&gt;

&lt;p&gt;I didn't want to be one more place that password lives. So FinVault, the household finance app I've been building and running on my Raspberry Pi, doesn't connect to banks at all. You download your statements and import them. That's the only way data gets in.&lt;/p&gt;

&lt;p&gt;It felt limiting at first. Then it ended up shaping almost every decision after it.&lt;/p&gt;

&lt;p&gt;If import is the only way in, import has to be really good. FinVault reads QFX, OFX, QBO, QIF, CSV and text PDFs, with presets for RBC, TD, CIBC, BMO, Scotia, Tangerine, Simplii, Wealthsimple and a few others. Import the same month twice or overlap your date ranges, and it skips the duplicates. Every import can be undone, because one bad import that scrambles your data is all it takes for someone to stop trusting the app.&lt;/p&gt;

&lt;p&gt;For Canadian accounts, I didn't just hide bank sync. I blocked it. If an account is Canadian (by country, currency, or institution), it's import-only, and there's no setting to change that. Accounts outside Canada can use optional sync through GoCardless, Pluggy or SimpleFIN, but it stays off until an admin turns it on. I've learned that if the safe option is only the default, someone eventually flips the switch. It's easier not to have the switch.&lt;/p&gt;

&lt;p&gt;Sorting is where I spent most of my time. Anything imported without a category lands in an amber "to sort" tray. Rules and merchants it has seen before suggest a category, and one click accepts it. My monthly routine is download, import, clear the tray. About two minutes per account. If that routine is annoying, nobody sticks with it, so that's where the effort went.&lt;/p&gt;

&lt;p&gt;It's built for how money works here, not just translated. It tracks TFSA, RRSP and FHSA contribution room. Tax-time summaries cover the categories an accountant actually asks about: medical, childcare, donations, moving, home office. The debt planner uses semi-annual compounding, the way Canadian fixed-rate mortgages work. And the French version is Canadian French, default categories included.&lt;/p&gt;

&lt;p&gt;Anything optional starts off: OCR, exchange-rate fetching, AI categorization, bank sync. If an exchange rate is missing, totals show a warning instead of quietly guessing. Backups are encrypted nightly (AES-256-GCM), since this is financial data sitting on your own hardware.&lt;/p&gt;

&lt;p&gt;Under the hood: one Docker container, SQLite by default (Postgres if you want it), no cloud anything. MIT licensed.&lt;/p&gt;

&lt;p&gt;What I keep relearning is that a good constraint does a lot of the design work for you. "It never touches your bank" is something anyone gets in one sentence, and it came straight from refusing to handle passwords.&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/finvaultapp/finvault" rel="noopener noreferrer"&gt;https://github.com/finvaultapp/finvault&lt;/a&gt;&lt;br&gt;
Try it: &lt;a href="https://finvaultapp.github.io/finvault" rel="noopener noreferrer"&gt;https://finvaultapp.github.io/finvault&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>docker</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
