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
PageTransitionwrapper 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/12on a healthy bill,⚠ 11/12in 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 callsPOST /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_reasonalongsidebills.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/catalogand 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.
lookupCatalogchecks 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-bulkhandles mixedprovider_syncandtransaction_matchentries in a single database transaction.
Profile and Calendar¶
- Profile display-name persistence — Profile name updates save
to
users.display_nameand are returned consistently asdisplay_name,displayName, andnamealiases. The Sidebar and Profile page display the saved name reliably across reloads and new sessions. - Private calendar subscription feed — A token-protected
feed.icssubscription 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
SearchFilterPanelanduseSearchPanelPreferencehook back the Tracker, Bills, and Subscriptions search/filter areas with a per-usersearch_bars_collapsedsetting. 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 database —
sessions.idnow storesSHA-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 isv0.94. See Why did everyone have to log in once after v0.37?. - IP geolocation made opt-in, disabled by default — The
ip-api.comlookup on new-device logins is now guarded by each user'sgeolocation_enabledProfile setting (default:false). When disabled, no outbound request is made for that user and thelocation_*columns inuser_login_historystay null for new entries. See What can I control from Profile?. - Rate limiting on sync/backfill endpoints —
POST /:id/sync,POST /sync-all, andPOST /:id/backfillnow share asyncLimiter(10 requests per 15 minutes per authenticated user). See Security. - WebAuthn / FIDO2 hardware security key 2FA — Migration
v0.92addswebauthn_credentialsandwebauthn_challengestables, awebauthnServicefor the full registration/authentication lifecycle, and six new endpoints inroutes/auth.js. RP ID and origin are configurable viaWEBAUTHN_RP_IDandWEBAUTHN_ORIGINenv vars. See How do I enable WebAuthn hardware-key 2FA?. - Encryption key separation —
TOKEN_ENCRYPTION_KEYrestored — When set, new encryptions use the env key (prefixe2:); when absent, the DB key is used (prefixv2:). 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/reconnect —
provider_transaction_idno longer includesdata_source_id. Migrationv0.93rewrites all keys, deduplicates any rows that are now identical, and replaces the unique index. - Sync lookback window — single source of truth —
bankSyncConfigServiceis the only source for the seed, routine, and max sync-day constants. Stale90/30UI 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
claimSetupTokenandfetchAccountsAndTransactionsuseAbortSignal.timeout(30000). - Manual sync and auto-sync now produce identical match results
—
runSyncinbankSyncServicecallsautoMatchForUser,applyMerchantRules, andapplySpendingCategoryRulestogether. - Match suggestion rejections now expire correctly — The two
broken
created_atqueries were corrected torejected_at, so rejected suggestions resurface after 90 days and old rows are pruned by cleanup. - Interest on debt bills charged once per calendar month —
computeBalanceDeltanow checksbills.interest_accrued_monthand skips the interest component when it matches the current month.interest_deltais 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_enabledkey, and atoIso()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_sessionpresence cookie enables history recording in single-user mode. - TOTP / Authenticator App 2FA for local login —
totpService.jswith 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
/spendingsidebar page, a per-category monthly budget editor, and merchant-rule auto-categorize. - SimpleFIN matching pipeline fixes —
normalizeMerchant()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_enabledkey.
v0.35.0¶
TOKEN_ENCRYPTION_KEYdeployment guidance — A one-timeconsole.warnfires when the auto-generated DB key is in use, pointing operators to the env var.- HKDF key derivation with automatic migration —
v2:ciphertext uses HKDF-SHA-256; legacy ciphertexts coexist transparently. - CSRF token moved out of readable cookie — The CSRF cookie is
httpOnly: trueby default. The SPA fetches the token once fromGET /api/auth/csrf-tokenand 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 persistence —
snoozed_untilonmonthly_bill_state(migrationv0.70). - Overdue count endpoint —
GET /api/tracker/overdue-countwith client-side polling. - Instant bill search and filters — Bills and Tracker gain search inputs and structured filters.
- Command palette bill lookup — A global
Ctrl+Kpalette 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
1500pxmaximum layout width - Restored the original tracker table sizing and columns
The release keeps:
- Compact
Ssubscription 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.