Sign inSign up

darktrees/ynab-assistant

By darktrees

•Updated 9 days ago

YNAB Assistant: self-hosted OFX import, local categorisation and a yearly budget reality check

Image
0

138

darktrees/ynab-assistant repository overview

⁠YNAB Assistant

A self-hosted assistant for YNAB⁠. It imports the OFX or QFX file your bank exports, categorised and reconciled, and it checks your budget against a year of real spending.

An import (OFX, QFX or QIF, so a bank whose only exports are "MS Money" or "Quicken" works too) arrives with every line already categorised and reconciled against what's in your budget. Pending lines wait until they settle.

YNAB's own file import gives you a list of new transactions to categorise one by one, and it duplicates anything you'd already typed in by hand unless the amount matches to the cent. This app learns your categories from your own YNAB history and works out which lines are already in the budget, so a review is mostly confirming what it guessed. Categorisation runs on a small local model; no transaction is sent to any AI or third-party service.

It is not affiliated with or endorsed by YNAB.

⁠What it does

  • Categories learned from your history. One sync trains a per-user model on your past YNAB transactions: a rule per merchant where you've been consistent, and a text classifier for the rest. Every row shows how sure it is, with its top three guesses at the head of the category list.
  • Recognises what's already there. A line you entered by hand, with a typo'd amount, a bill that went up, or a scheduled transaction YNAB dated on a different day, is corrected and cleared in place instead of imported a second time. Whatever is in YNAB for that period but not in the file is listed, so a missed entry is visible.
  • Payees reused. Lines are linked to the payees you already have, and a new one is flagged before it's created.
  • Transfers and schedules. A transfer between your own accounts is imported as a YNAB transfer, not an expense, including a BPAY payment to your own credit card at another bank. A line that matches a scheduled transaction takes its payee, category and split, and any row can be tagged recurring to create a new schedule.
  • New categories on the spot. Create a category, and its group, from the review screen without leaving it.
  • A dashboard. Ready to Assign, which accounts are behind on imports, a 90-day cash-flow forecast of your everyday account (scheduled transactions, plus income and bills that repeat in your history, plus your usual day-to-day spending), the big expenses due in the next 60 days and how much is still short, and this month's underfunded targets.
  • A cash plan. Every bank account added up and projected day by day: scheduled bills and income, each card's balance (charges reach the bank only when it's paid on its due day, in full or a fixed amount, and nothing goes past the credit limit you set), day-to-day spending at your budget, and the what-ifs you add (a loan with its repayments, paying a card down or off, a big payment, a bill that changes from a date), plus what your yearly and irregular targets will actually spend. If you'd run out, it says when and exactly which payments couldn't be made, and how much you'd need and by when. Save scenarios and compare them side by side: cash over time, what you owe, and the interest each one costs. Saved with your account; it never changes YNAB.
  • A reality check on the budget. For every category, twelve months of what it really cost against its target (or what you actually assigned), how far off the plan is, and the target it should have: how much, monthly, quarterly or yearly, and when it's due (presents by 1 November, rates every three months). Edit a target and save it to YNAB from the same row, and split a catch-all category by moving one payee's transactions, history included, into a new or existing category. Read live from YNAB each time; nothing is stored.
  • It keeps learning. Every category you confirm on import goes back into the model.
  • Several people, one install. Each person signs in with an emailed link, connects their own budget, and gets their own history and model.

⁠Privacy

  • Your transactions go to YNAB and nowhere else. Categorising, matching and training all happen inside the container. There is no AI service, no analytics and no telemetry.
  • The bank file isn't kept. It is parsed into the review screen and then discarded. Only the lines you import are added to your history, so the model can learn from them.
  • What is stored, and how. Each person's history (bank description, payee, category, amount and date), their model and their saved cash plan sit in their own folder under /app/data. Email addresses and YNAB tokens are encrypted with AES-256-GCM. Sessions and sign-in links are stored only as hashes. The history itself isn't encrypted, so protect the volume as you would the budget.
  • Delete everything in one step. Settings → Delete all my data erases your history, your model, your saved cash plan, your stored token and every session. Nobody else's data is touched, and the next sync rebuilds the history from YNAB.

⁠Running it

services:
  ynab-assistant:
    image: darktrees/ynab-assistant:latest
    env_file: .env
    volumes:
      - ./data:/app/data
    ports:
      - "5000:5000"
    restart: unless-stopped

The container runs as uid 1000 and keeps everything in /app/data: a shared sign-in database, and each person's transaction history and model. Create the folder and hand it over before the first start:

mkdir -p data && sudo chown 1000:1000 data

Then sign in at /login, connect a budget by pasting a YNAB personal access token⁠, and press Sync history from YNAB once to train. Back up ./data with the keys below: a token stored there can only be read with ENCRYPTION_KEY.

Exposing it. There are no passwords. Sign-in is a single-use link emailed only to the addresses in ALLOWLIST, the session is a server-side record behind an httpOnly cookie, sign-in is rate-limited, and every form checks its origin. Put it behind a reverse proxy with a trusted certificate and set SITE_URL to that https address; the cookie is marked Secure when it is. It holds a token that can change your budget, so a private network is still the safer place for it.

⁠Configuration

Put these in .env beside the compose file. Never put them in the compose file itself. Generate each key separately with openssl rand -hex 32.

VariableRequiredWhat it's for
SECRET_KEYyesSigns Flask's cookie. Without a fixed value, each worker makes its own and sessions break between them
ENCRYPTION_KEYyesAES-256-GCM key for email addresses and YNAB tokens at rest. Changing it strands what's stored
EMAIL_HASH_KEYyesPepper for looking people up by email. Changing it means nobody can sign in again
ALLOWLISTyesComma-separated addresses that may sign in. Empty lets nobody in
SITE_URLyesThe public address, used to build sign-in links, e.g. https://ynab.example.com
SMTP2GO_API_KEYyesSends the sign-in emails
EMAIL_FROMyesThe sender of those emails, at a domain your SMTP2GO account has verified
UPDATE_CHECKnooff stops the check for a newer image on Docker Hub
UPDATE_CHECK_IMAGEnoThe image that check looks at. Defaults to darktrees/ynab-assistant

YNAB tokens are not configuration: each person connects their own from inside the app, and it is stored encrypted.

The dashboard shows the build the container is running, and a banner when a newer image has been published. Update with docker compose pull && docker compose up -d.

⁠Data and attribution

This project's licence covers its own code. Whoever runs the image brings their own accounts and agrees to each provider's terms themselves. No financial data ships in the image, and none leaves your server except to YNAB.

SourceUsed forTerms
YNAB API⁠Reading your budget's history, accounts, payees, schedules, targets and monthly totals; writing the import, category targets, and the moves you confirm on the reality checkYour own YNAB account and the YNAB API Terms of Service⁠. Called at runtime with each person's own token; nothing ships
Your bank's OFX, QFX or QIF exportThe transactions to importYour own data. The file itself is not stored; the lines you import join your history in ./data to train on, and go nowhere but YNAB
SMTP2GO⁠Sending sign-in emailsThe operator's own account and SMTP2GO's terms. Nothing ships
Docker Hub⁠Checking whether a newer image existsA public tag lookup, nothing about you is sent. Off with UPDATE_CHECK=off

The image includes Flask⁠, scikit-learn⁠, joblib⁠ and Requests⁠ (BSD-3-Clause / Apache-2.0), ofxparse⁠ (MIT), cryptography⁠ (Apache-2.0 / BSD) and gunicorn⁠ (MIT), each under its own licence.

⁠Licence

YNAB Assistant is source-available under the PolyForm Noncommercial License 1.0.0⁠. Personal, hobby, research, educational and charity use are all welcome. Commercial use is not permitted without a separate licence. The full terms are at /app/LICENSE in the image.

⁠Source

The code lives on a private forge. It's available on request for non-commercial use; get in touch through the contact link on the darktrees Docker Hub profile⁠. To ask about commercial use, use the same link.

Tag summary

Content type

Image

Digest

sha256:792ee5cc9…

Size

133.4 MB

Last updated

9 days ago

docker pull darktrees/ynab-assistant