getbud

Documentation

Everything you need to run and use getbud. For the code and issues, see GitHub — architecture, configuration, data-model, and deployment references also live in the repo as markdown under docs/.

Quick start

The repo ships a compose file with the app and Postgres:

git clone https://github.com/ctrl-research/getbud
cd getbud
cp .env.example .env      # set POSTGRES_PASSWORD at minimum
docker compose up -d

The app listens on :8081 (the container listens on 8080; compose maps it to host 8081). Prebuilt multi-arch images (amd64/arm64) are on GHCR if you'd rather point your own deployment at them:

docker pull ghcr.io/ctrl-research/getbud:latest   # or a specific X.Y.Z

Database migrations run automatically at startup. The first account to sign in becomes admin and every new user starts with a sensible set of income and expense categories.

The compose file enables local email/password auth for a quick first look — create a user with:

docker compose exec app getbud seed    # [email protected] / getbud-dev

For a real deployment, configure Google or OIDC sign-in and set GETBUD_LOCAL_AUTH=false.

Configuration

Everything is configured with GETBUD_* environment variables (compose reads them from .env):

VariableDefaultWhat it does
GETBUD_DATABASE_URLRequired. Postgres connection string.
GETBUD_ADDR:8080Listen address.
GETBUD_BASE_URLhttp://localhost:8080Public URL of the instance. OAuth redirect URIs derive from it; session cookies are Secure when it's https. No trailing slash.
GETBUD_GOOGLE_CLIENT_ID / GETBUD_GOOGLE_CLIENT_SECRETEnable Sign in with Google (set both together).
GETBUD_OIDC_ISSUER_URL + _CLIENT_ID / _CLIENT_SECRETGeneric OIDC provider (Authentik, Keycloak, …); redirect URI $BASE_URL/auth/oidc/callback. GETBUD_OIDC_NAME labels the login button.
GETBUD_LOCAL_AUTHfalseEmail/password accounts, intended for dev/testing. Create one with the seed subcommand.
GETBUD_ALLOWED_EMAILSComma-separated emails allowed to sign up after the first user. Empty = closed instance.

Sign-in options

Google

  1. In the Google Cloud Console, create a project and configure the OAuth consent screen (External; Testing mode is fine — add the accounts that will sign in as test users).
  2. Create an OAuth client ID of type Web application with the authorized redirect URI $GETBUD_BASE_URL/auth/google/callback.
  3. Set GETBUD_GOOGLE_CLIENT_ID and GETBUD_GOOGLE_CLIENT_SECRET and restart. The login page shows the Google button automatically.

Generic OIDC

Any OIDC provider with discovery works (Authentik, Keycloak, Pocket ID, …). Set the issuer URL exactly as the provider publishes it (trailing slash included), the client id/secret, and the redirect URI $GETBUD_BASE_URL/auth/oidc/callback.

In both cases: sign in yourself first — the first account becomes admin and bypasses the allowlist. After that, only GETBUD_ALLOWED_EMAILS addresses can register; existing accounts always keep signing in. Each user's data is completely private.

Accounts & balances

Accounts come in two flavours:

Accounts with transactions can't be deleted — archive them instead and they drop out of pickers while their history stays intact.

Transactions & transfers

Amounts are signed: money in is positive, money out is negative (the UI's income/expense toggle handles the sign for you). Every transaction can carry a payee, notes, and a category; categories are flat, per-user, and manageable in Settings (rename, recolor, archive, or delete with reassignment).

Transfers move money between two of your own accounts — paying a credit card, funding a TFSA. Both legs are created together, deleted together, and excluded from every income/expense report. A transfer into a registered account is what drives contribution tracking.

CSV import

Import → pick the account → drop the export from your bank. getbud sniffs the delimiter (comma, semicolon, tab), strips BOMs, detects the header row, and guesses the column mapping — date (with format detection, including day/month disambiguation), payee, and amount as either one signed column or split debit/credit columns. Credit-card exports that report purchases as positive numbers have a one-click "positive means expense" toggle.

The review step shows every row before anything is saved:

Duplicate detection is a soft warning, never a silent drop. Each import is recorded as a batch — the history at the bottom of the Import page reverts any batch atomically, and the mapping you used is remembered per account for next time.

Contribution room

The Contributions page tracks RRSP, TFSA, and FHSA room per calendar year. Room is yours to enter — TFSA room is cumulative and personal, RRSP room comes from your notice of assessment — so getbud shows the published annual limits only as placeholder hints (check CRA MyAccount for your real number).

Contributions and withdrawals are derived from transactions in your registered accounts: deposits count as contributions, withdrawals as withdrawals, and transfers between two accounts of the same type (TFSA → TFSA) are correctly ignored — moving money within a type isn't new room used. Cross-type transfers (chequing → TFSA) count.

Known simplification: contributions attribute to the calendar year they occur in; the RRSP first-60-days rule is not modelled.

Reports

All reports honour the shared date-range picker (this month, last month, year to date, last 12 months, or custom) and always exclude transfers. Reports assume a single currency; mixed-currency data is not converted.

Releases & images

Every merge to main bumps the patch version, tags the repo, and publishes a multi-arch image (amd64/arm64) to ghcr.io/ctrl-research/getbud as X.Y.Z and latest ([minor]/[major] in the PR title bump higher). Curated GitHub Releases with notes — optionally with an extra image tag like stable — are cut from the Release workflow in the Actions tab. The running version is logged at startup.

Data & backups

All state lives in the Postgres database — accounts, transactions, snapshots, room, users, sessions. A pg_dump is a complete backup. Migrations run automatically on upgrade, so restoring into a newer version just works.

Development

Requirements: Go and Node as pinned in .tool-versions (asdf/mise), and Docker for Postgres.

make db        # postgres via docker compose (host port 5433)
make seed      # create the local dev user
make run       # Go API on :8081 with local auth
make web       # Vite dev server on :5173, proxying /api and /auth

Open http://localhost:5173 and sign in with [email protected] / getbud-dev. After editing SQL under internal/store/queries/, run make generate; make test-db runs the Postgres-backed store tests. See ARCHITECTURE.md for the full tour.