Skip to content

Current Release

The documented application version is 0.37.0.

This page tracks user-visible changes. For the full chronological changelog with implementation detail and security notes, read HISTORY.md in the BillTracker source checkout. For architecture and operational decisions, read the Engineering Log. The page is grouped by feature area so a user can jump straight to the workflow that changed for them.

v0.37.0

This release extends trust, traceability, and visual polish across the tracker, payments, and bank-sync workflows, and adds a token-protected private calendar subscription feed.

Tracker

  • Tracker table sorting — Clickable column headers and a compact filter-bar selector sort the monthly tracker by bill name, due date, expected amount, last-month paid, paid amount, remaining amount, paid date, and status. The sort is URL-backed and pauses drag-reorder automatically. Status sorting uses the tracker lifecycle order (missed, late, due_soon, upcoming, autodraft, paid, skipped) rather than alphabetical labels. See How do I sort the Tracker?.
  • Trend sparkline on amount column — A 44×16 px inline SVG polyline to the right of the expected amount shows the last six months of actual payment totals, oldest-to-newest, normalized to the row's min/max range. The sparkline is computed server-side in a single batched query and rendered as a <polyline> on the client. It appears only when two or more months of data exist. See What does the sparkline on a Tracker row mean?.
  • "Changed" badge on drifted bills — When the drift service detects a month-over-month amount change above the configured threshold, the amount column shows an amber "Changed" badge with a trending-up icon alongside the sparkline. Reuses the existing drift report — no extra API call. See What does the "Changed" badge mean?.
  • Animated page, modal, and tracker reorder transitions — A shared PageTransition wrapper and framer-motion layout animations now handle user/admin route content, dialog entry, and tracker bucket row movement, so sorted and reordered bill groups move smoothly instead of snapping.
  • Bank payments override provisional manual tracker payments — Manual payments entered from Tracker count immediately. When a matching bank-backed payment clears, it becomes the accounting source of truth and the manual payment is preserved as history with override metadata. Overridden manual payments are excluded consistently from Tracker, Summary, Calendar, Analytics, Categories, starting amount summaries, drift checks, notifications, status counts, bank pending deductions, trends, overdue checks, and debt balance deltas.

Bills and Payments

  • Autopay trust indicator — Autopay is no longer a binary flag. The AP badge in tracker rows shows a confidence score derived from the last 12 months of payment history (✓ 12/12 on a healthy bill, ⚠ 11/12 in amber when at least one payment was marked as an autopay failure). A clock nudge appears when autopay has not been manually confirmed in over 90 days. Hovering the badge reveals a tooltip with the success rate, last failure date and notes, and verification status. A new autopay trust panel inside BillModal surfaces the same data and offers a Mark verified button that calls POST /api/bills/:id/verify-autopay. See How do I trust an autopay bill?.
  • Cancellation tracker — The deactivate-bill confirmation dialog now includes an optional reason dropdown: Moved to spouse, Switched providers, Paid off, Cancelled, or Other. The selected reason is stored in bills.inactive_reason alongside bills.inactivated_at. See How do I deactivate a bill and keep the reason?.
  • Autopay failure flag on payments — The PaymentModal edit dialog now shows an "Autopay failed — paid manually" checkbox on autopay- enabled bills or when the payment method is set to autopay, enabling retroactive flagging of past failures. See How do I record a payment?.
  • Subscription recommendations narrowed to bank-backed known services — Recommendations now require a high-confidence match that resolves to a known subscription catalog entry, and break confidence into identity, amount, and cadence evidence with user-visible evidence chips. See Browse the Service Catalog.

Subscriptions

  • Service Catalog page — The known-service catalog moved to its own dedicated route at /subscriptions/catalog and now acts as an advanced matching tool: tracked entries appear under a "Tracking" header with price-drift indicators, Re-link swaps catalog links via a searchable dialog, and Custom bank descriptors add exact payee strings to improve future matching. The Subscriptions page now shows a compact "Improve Matching" card linking to the catalog. See Browse the Service Catalog.
  • Subscription catalog: bank descriptors + pricing — A 200-service researched dataset now ships with the catalog along with bank statement descriptor strings and slang. lookupCatalog checks descriptors first (score 2000+) before falling back to name/domain fuzzy matching (1000/500).

Bank Sync and Matching

  • Auto-match review panel — Merchant-rule auto-matches in the last seven days appear in a collapsible "Auto-matched — review" panel on the Data → Bank Sync page. Each entry shows the payee, date, amount, and the bill it was matched to; an Undo button reverses the match, restores the bill balance, and reverts the transaction to unmatched. See How do I undo an auto-matched payment?.
  • Auto-learn merchant rules from manual matches — Explicitly confirming a transaction match now creates a normalized merchant rule so future synced transactions from the same payee auto-match without manual review. Background auto-matching never creates rules. Generic financial tokens (ach, atm, transfer, fee) are filtered out so a rule like "ACH Payment" cannot become a catch-all. See What are merchant rules?.
  • Ambiguous merchant-rule matching protection — When the most-specific tier of matching rules maps to more than one bill (two bills both named "Amazon" with matching rules), the transaction is left for manual review rather than silently attributed to the wrong bill.
  • Improved unmatch flow — choice dialog + bulk deselect — Clicking Unmatch now opens a two-option dialog. Option 1 unmatch that one payment. Option 2 fetches all linked transactions for the bill whose payee normalizes to the same prefix and opens a checklist dialog where each similar match is pre-checked; users can deselect individual transactions, use All/None quick-selects, and optionally remove the merchant rule. New endpoint POST /api/transactions/unmatch-bulk handles mixed provider_sync and transaction_match entries in a single database transaction.

Profile and Calendar

  • Profile display-name persistence — Profile name updates save to users.display_name and are returned consistently as display_name, displayName, and name aliases. The Sidebar and Profile page display the saved name reliably across reloads and new sessions.
  • Private calendar subscription feed — A token-protected feed.ics subscription flow for Apple Calendar, Google Calendar, Outlook, and generic ICS clients. The Settings page gains a Calendar Feed card with create, copy, regenerate, revoke, and preview actions, plus platform-specific guidance. The Calendar page includes an "Add All" entry point that routes users to the subscription setup. The public feed endpoint does not require session cookies, uses stable per-bill-cycle event UIDs, and emits all-day DATE events with CRLF/no-BOM/no-VTIMEZONE ICS output. See Subscribe to the Calendar Feed.

UX Polish

  • Remembered collapsible search/filter panels — A shared SearchFilterPanel and useSearchPanelPreference hook back the Tracker, Bills, and Subscriptions search/filter areas with a per-user search_bars_collapsed setting. The expanded/collapsed preference is remembered across pages and sessions.
  • Mobile tracker view polish — The mobile tracker now mirrors desktop drift context (Changed badge, amount sparkline), prevents duplicate quick-pay taps while a save is in progress, and lets bucket headers wrap cleanly on narrow screens.
  • Empty state polish — All dashed-border centered empty states across the app have been replaced with filled card treatments and explicit CTAs.
  • Bank balance freshness timestamp — The Tracker bank budget card's pulsing indicator is renamed to "Live Sync" and now shows "as of [date, time]" sourced from bank_tracking.last_updated, matching the Calendar page.

Security

  • Session tokens hashed in the databasesessions.id now stores SHA-256(token). The raw token stays only in the cookie. All existing sessions were invalidated on first startup after this version (one-time re-login). The migration is v0.94. See Why did everyone have to log in once after v0.37?.
  • IP geolocation made opt-in, disabled by default — The ip-api.com lookup on new-device logins is now guarded by each user's geolocation_enabled Profile setting (default: false). When disabled, no outbound request is made for that user and the location_* columns in user_login_history stay null for new entries. See What can I control from Profile?.
  • Rate limiting on sync/backfill endpointsPOST /:id/sync, POST /sync-all, and POST /:id/backfill now share a syncLimiter (10 requests per 15 minutes per authenticated user). See Security.
  • WebAuthn / FIDO2 hardware security key 2FA — Migration v0.92 adds webauthn_credentials and webauthn_challenges tables, a webauthnService for the full registration/authentication lifecycle, and six new endpoints in routes/auth.js. RP ID and origin are configurable via WEBAUTHN_RP_ID and WEBAUTHN_ORIGIN env vars. See How do I enable WebAuthn hardware-key 2FA?.
  • Encryption key separation — TOKEN_ENCRYPTION_KEY restored — When set, new encryptions use the env key (prefix e2:); when absent, the DB key is used (prefix v2:). On startup, if the env key is set, all DB-key-encrypted secrets are automatically re-encrypted in a single transaction. The Admin → Bank Sync card now shows which key source is active. See How are secrets encrypted at rest?.

Operations

  • SimpleFIN transaction deduplication stable across disconnect/reconnectprovider_transaction_id no longer includes data_source_id. Migration v0.93 rewrites all keys, deduplicates any rows that are now identical, and replaces the unique index.
  • Sync lookback window — single source of truthbankSyncConfigService is the only source for the seed, routine, and max sync-day constants. Stale 90/30 UI fallbacks were corrected.
  • SimpleFIN fetch retries transient failures with backoff — Up to 3 attempts with 1 s / 2 s delays; 403 and other 4xx fail immediately.
  • SimpleFIN requests now time out after 30 seconds — Both claimSetupToken and fetchAccountsAndTransactions use AbortSignal.timeout(30000).
  • Manual sync and auto-sync now produce identical match resultsrunSync in bankSyncService calls autoMatchForUser, applyMerchantRules, and applySpendingCategoryRules together.
  • Match suggestion rejections now expire correctly — The two broken created_at queries were corrected to rejected_at, so rejected suggestions resurface after 90 days and old rows are pruned by cleanup.
  • Interest on debt bills charged once per calendar monthcomputeBalanceDelta now checks bills.interest_accrued_month and skips the interest component when it matches the current month. interest_delta is stored on each payment row.
  • Worker health improvements — A batched autopay-marking query, persisted worker state in the settings table, batched notification deduplication, the correct backup_schedule_enabled key, and a toIso() helper that fixes the "Next Check" timezone bug.

Previous Releases

v0.36.0

  • Login history: encrypted at rest + geolocation + new device alerts + failed attempt tracking + session detection + 10 records — IP and user agent are encrypted; new-device logins trigger push notifications via the user's configured channel.
  • Login history for single-user mode — A bt_single_session presence cookie enables history recording in single-user mode.
  • TOTP / Authenticator App 2FA for local logintotpService.js with QR-code setup, 8 one-time recovery codes, and a second-step login flow.
  • No Login mode — admin UI redesign — A clean radio-group selector for Require Login versus No Login — Single User, with conditional auth-methods card visibility.
  • Spending page — bank transaction categorization and budgets — Eight default spending categories, a /spending sidebar page, a per-category monthly budget editor, and merchant-rule auto-categorize.
  • SimpleFIN matching pipeline fixesnormalizeMerchant() now strips & correctly, name scoring uses word-boundary regex matching, longer merchant rules win on collisions, and late attribution honors the configured window.
  • Database performance: composite indexes — Four new composite indexes on (user_id, deleted_at)-shaped queries.
  • Worker health improvements — Batched autopay and notification queries, persisted worker state, and the correct backup_schedule_enabled key.

v0.35.0

  • TOKEN_ENCRYPTION_KEY deployment guidance — A one-time console.warn fires when the auto-generated DB key is in use, pointing operators to the env var.
  • HKDF key derivation with automatic migrationv2: ciphertext uses HKDF-SHA-256; legacy ciphertexts coexist transparently.
  • CSRF token moved out of readable cookie — The CSRF cookie is httpOnly: true by default. The SPA fetches the token once from GET /api/auth/csrf-token and caches it in memory.

v0.34.0

  • Overdue Command Center — A collapsible panel with per-bill Pay Now, Skip, and Snooze (⅓/7 days) actions. Snoozed bills hide with a count hint in the header.
  • Sidebar overdue badge — A live overdue count appears on the Tracker menu pill and the mobile drawer.
  • Snooze persistencesnoozed_until on monthly_bill_state (migration v0.70).
  • Overdue count endpointGET /api/tracker/overdue-count with client-side polling.
  • Instant bill search and filters — Bills and Tracker gain search inputs and structured filters.
  • Command palette bill lookup — A global Ctrl+K palette finds bills by name, category, notes, due details, or amount.
  • Partial payment tracking — Paid-versus-expected progress and a payment ledger for installment payments.
  • Tracker overpayment remaining math — Paying more than the due amount no longer produces reversed progress bars.

v0.33.8.7

Tracker layout experiments from 0.33.8.5 and 0.33.8.6 were reverted:

  • Restored the single-column tracker bucket layout
  • Restored the 1500px maximum layout width
  • Restored the original tracker table sizing and columns

The release keeps:

  • Compact S subscription badges
  • Per-bucket remaining balance and Done labels
  • The generated MkDocs site directory outside the application repository

v0.33.8.4

  • Added SimpleFIN 90-day backfill controls with an effective 89-day request
  • Added initial connection seed sync
  • Redesigned the admin Status page
  • Corrected SimpleFIN error-state reporting

v0.33.8.3

  • Added subscription badges
  • Added SimpleFIN status reporting
  • Started the daily worker during server boot
  • Corrected tracker overdue counts

v0.33.8.0 - v0.33.8.2

  • Added advisory non-bill filters
  • Expanded the subscription catalog
  • Improved GeorgiaDigits font scoping for numeric inputs

What changed for the user?

The Tracker gains three new column-level signals (sparkline, "Changed" badge, autopay trust score) and URL-backed sorting with a lifecycle status order. The Bills page asks for a cancellation reason when you deactivate a bill and exposes a Mark verified button for autopay. The Data page gains a collapsible Auto-matched — review panel with per-row Undo, and the Service Catalog at /subscriptions/catalog is now the dedicated matching workspace with custom bank descriptors. A token-protected feed.ics calendar subscription feed keeps your existing calendar app in sync without manual entry. See the linked guide pages throughout this changelog for the user-facing workflow.

For the full application changelog, read HISTORY.md in the BillTracker source checkout.

See also