BillTracker is a self-hosted app for tracking recurring bills, monthly payments, due dates, categories, and personal bill history. It runs as a Node/Express server with a React frontend and stores data in SQLite. This product was produced with the assistance of AI. https://t1.scheller.ltd/
Go to file
null 7c3cfd1715 docs: update README.md, ERM, FUTURE.md, HISTORY.md
README.md updates:
- Added billing cycles (weekly/biweekly/quarterly/annual), history ranges,
  monthly income/starting amounts, migration rollback, audit logging,
  auth-mode/OIDC config, CSRF protection details
- Added INIT_REGULAR_USER/PASS and SESSION_CLEANUP_INTERVAL_MS env vars
- Added CSRF env vars (CSRF_HTTP_ONLY, CSRF_SAME_SITE, CSRF_SECURE,
  CSRF_COOKIE_NAME)
- Noted export limitation: cycle_type, cycle_day, history_ranges omitted
- Fixed: CSP is now implemented with per-request nonces (was 'deferred')
- Added: default admin restricted from tracker routes, session rotation
  on password change, audit logging
- Cleaned up demo server formatting, project structure listing, scripts
- Removed authLogin.js from project structure (file was deleted in v0.23.2)

Engineering_Reference_Manual.md:
- Removed stale authLogin.js duplicate route note (file no longer exists)
- Removed 401/403 error detail from login endpoint (simplified)
- Updated version to 0.23.2

FUTURE.md:
- Marked notification privacy leak (CRITICAL) as FIXED v0.23.2
- Marked duplicate login route (LOW) as FIXED v0.23.2
- Updated current version to v0.23.2

HISTORY.md:
- Added v0.23.2 entry with security fix and route consolidation details
2026-05-10 12:42:45 -05:00
client fix: notification privacy leak — per-user bills no longer sent to all recipients (v0.23.2) 2026-05-10 12:34:53 -05:00
db v0.23.1: migration rollback capability 2026-05-10 10:44:39 -05:00
docs docs: update README.md, ERM, FUTURE.md, HISTORY.md 2026-05-10 12:42:45 -05:00
img correct 2026-05-04 13:51:38 -05:00
legacy push 2026-05-09 13:03:36 -05:00
middleware v0.20.6: Audit logging for critical operations 2026-05-10 00:03:12 -05:00
public push 2026-05-09 13:03:36 -05:00
routes fix: remove duplicate login route (authLogin.js), consolidate into auth.js 2026-05-10 12:20:50 -05:00
scripts push 2026-05-09 13:03:36 -05:00
services fix: notification privacy leak — per-user bills no longer sent to all recipients (v0.23.2) 2026-05-10 12:34:53 -05:00
setup v0.22.3: fix ENV-seeded users skip first-login flow, add audit logging 2026-05-10 04:24:51 -05:00
utils push 2026-05-09 13:03:36 -05:00
workers initial commit 2026-05-03 19:51:57 -05:00
.dockerignore push 2026-05-09 13:03:36 -05:00
.env.example push 2026-05-09 13:03:36 -05:00
.gitignore initial commit 2026-05-03 19:51:57 -05:00
.rsyncignore initial commit 2026-05-03 19:51:57 -05:00
DEVELOPMENT_LOG.md fix: notification privacy leak — per-user bills no longer sent to all recipients (v0.23.2) 2026-05-10 12:34:53 -05:00
Dockerfile push 2026-05-09 13:03:36 -05:00
FUTURE.md docs: update README.md, ERM, FUTURE.md, HISTORY.md 2026-05-10 12:42:45 -05:00
HISTORY.md docs: update README.md, ERM, FUTURE.md, HISTORY.md 2026-05-10 12:42:45 -05:00
NOTES.md push 2026-05-09 13:03:36 -05:00
README.md docs: update README.md, ERM, FUTURE.md, HISTORY.md 2026-05-10 12:42:45 -05:00
REVIEW.md push 2026-05-09 13:03:36 -05:00
SECURITY_AUDIT.md v0.19.2: fix legacy DB migration login failure + security hardening 2026-05-09 18:25:25 -05:00
STRUCTURE.md v0.20.0: admin dashboard with roadmap and activity log 2026-05-09 21:14:21 -05:00
bill-tracker.code-workspace calendar 2026-05-04 13:14:32 -05:00
components.json initial commit 2026-05-03 19:51:57 -05:00
deploy.sh initial commit 2026-05-03 19:51:57 -05:00
docker-compose.yml push 2026-05-09 13:03:36 -05:00
docker-entrypoint.sh initial commit 2026-05-03 19:51:57 -05:00
index.html logo 2026-05-03 22:33:21 -05:00
jsconfig.json initial commit 2026-05-03 19:51:57 -05:00
package-lock.json v0.22.0: React Query Migration 2026-05-10 03:10:43 -05:00
package.json fix: notification privacy leak — per-user bills no longer sent to all recipients (v0.23.2) 2026-05-10 12:34:53 -05:00
postcss.config.js initial commit 2026-05-03 19:51:57 -05:00
run-functional-test.js push 2026-05-09 13:03:36 -05:00
server.js fix: remove duplicate login route (authLogin.js), consolidate into auth.js 2026-05-10 12:20:50 -05:00
tailwind.config.js initial commit 2026-05-03 19:51:57 -05:00
test-functional.js push 2026-05-09 13:03:36 -05:00
vite.config.js logo 2026-05-03 22:33:21 -05:00

README.md

BillTracker

Tracker logo

BillTracker is a self-hosted app for tracking recurring bills, monthly payments, due dates, categories, and personal bill history. It runs as a Node/Express server with a React frontend and stores data in SQLite. This product was produced with the assistance of AI.

Demo Server: https://t1.scheller.ltd/
Username: guest · Password: guest123

Screenshots

Analytics screenshot

Analytics screenshot

Analytics screenshot

Calendar screenshot

What Is BillTracker?

BillTracker helps a household or small self-hosted setup keep bill data in one place:

  • recurring bill records with due day, expected amount, category, notes, autopay details, optional APR, and flexible billing cycles (monthly, weekly, biweekly, quarterly, annual)
  • bill history ranges for tracking which months a bill was active
  • monthly tracker with payments, skipped bills, actual monthly amounts, and notes
  • monthly income tracking and starting cash amounts (1st/15th/other)
  • calendar view for due dates and payments
  • analytics for monthly spending, expected vs actual totals, category spend, and payment history
  • categories, profile, display name, notification preferences, password changes, and data tools
  • admin user management, authentication settings, auth-mode/OIDC configuration, backups, scheduled backups, cleanup, migration rollback, audit logging, and status checks

Features

  • Tracker for month-by-month bill status, payment entry, notes, skipped bills, overdue totals, and month navigation
  • Bills page for creating, editing, deactivating, reactivating, deleting, and controlling inactive bill history
  • Calendar page with a monthly grid, bill due dates, payments, and progress summary
  • Analytics page with date range, category/bill filters, charts, heatmap, and print output
  • User-owned categories
  • Settings for theme, currency, date format, and grace period
  • Profile page with display name, notification preferences, password change, imports, exports, and import history
  • User exports to Excel workbook or BillTracker user SQLite export (note: exports currently omit cycle_type, cycle_day, and bill_history_ranges)
  • XLSX spreadsheet import with preview and import decisions
  • User SQLite import from exports created by this app
  • Admin users, role management, password resets, full database backups/restores, scheduled backups, cleanup, auth-mode/OIDC configuration, migration rollback, audit logging, and status page
  • Local username/password login and optional authentik/OIDC login
  • CSRF protection using double-submit cookie pattern with per-request nonces

Quick Start

Install dependencies:

npm install

Run the API and Vite frontend for development:

npm run dev

Build the frontend:

npm run build

Start the production server:

npm start

The production server serves dist/ and listens on PORT, defaulting to 3000.

Useful scripts present in this repo:

npm run dev:api
npm run dev:ui
npm run build
npm start
node scripts/test-import.js
node scripts/test-oidc-smoke.js
node scripts/test-cookie-options.js

Docker

Docker files are included. The compose file runs the published image on host port 3030 and stores app data under /data in the container.

docker compose up -d

On first start without an existing database, create the admin account with:

INIT_ADMIN_USER=admin
INIT_ADMIN_PASS=change-this-password

To also seed a regular (non-admin) user:

INIT_REGULAR_USER=regularuser
INIT_REGULAR_PASS=changeme123

The regular user password must be at least 8 characters. Seeded users skip the first-login and password-change gates.

Remove or change those first-run values after the initial accounts exist.

Configuration

Most app settings are configured in the web UI. User-facing settings live under Settings/Profile. Server-wide settings such as users, backups, cleanup, and authentication methods live in Admin.

Real environment variables used by the app:

PORT=3000
NODE_ENV=production
DB_PATH=/path/to/bills.db
BACKUP_PATH=/path/to/backups
INIT_ADMIN_USER=admin
INIT_ADMIN_PASS=change-this-password
INIT_REGULAR_USER=regularuser
INIT_REGULAR_PASS=changeme123
SESSION_CLEANUP_INTERVAL_MS=86400000
HTTPS=true
COOKIE_SECURE=true
CORS_ORIGIN=https://bills.example.com
CSRF_HTTP_ONLY=true
CSRF_SAME_SITE=strict
CSRF_SECURE=true
CSRF_COOKIE_NAME=bt_csrf_token

OIDC environment fallback variables are supported when the matching Admin database setting is blank:

OIDC_PROVIDER_NAME=authentik
OIDC_ISSUER_URL=https://yourURL.com/application/o/bills/.well-known/openid-configuration
OIDC_CLIENT_ID=<client-id>
OIDC_CLIENT_SECRET=<client-secret>
OIDC_TOKEN_AUTH_METHOD=client_secret_basic
OIDC_REDIRECT_URI=https://bills.example.com/api/auth/oidc/callback
OIDC_SCOPES="openid email profile groups"
OIDC_ADMIN_GROUP=bill-tracker-admins
OIDC_AUTO_PROVISION=true

Database-backed Admin settings take precedence over environment fallback values.

Documentation

For detailed technical documentation, see the docs/ directory:

  • CSRF-SPA-Setup.md: CSRF protection implementation for Single Page Applications, including the double-submit cookie pattern and environment configuration
  • Authentik-Integration.md: Complete guide for Authentik OIDC integration, including setup, security features, and troubleshooting

Authentication

BillTracker supports local username/password login by default. Admins can create users, reset user passwords, promote/demote users, and configure login methods.

Optional authentik/OIDC login can be enabled in Admin. OIDC uses authorization code flow with PKCE, state and nonce validation, and openid-client token validation. OIDC users can be auto-provisioned when enabled.

Admin role is never granted by default through OIDC. Set an authentik admin group in BillTracker; only users whose OIDC groups claim includes that configured group become app admins.

BillTracker enforces lockout checks: local login cannot be disabled unless OIDC is configured, enabled, and mapped to an admin group. This prevents accidental lockout where no login method is available.

The default admin account (created by INIT_ADMIN_USER/INIT_ADMIN_PASS) is restricted to admin routes only. It cannot access user tracker routes (bills, payments, calendar, etc.). Regular users and promoted admins have full access.

authentik Setup

See Authentik-Integration.md for comprehensive setup instructions, including:

  • Detailed Authentik provider/application configuration steps
  • PKCE and state parameter security
  • ID token verification details
  • User provisioning and group-to-role mapping
  • Troubleshooting guide

In authentik, create an OAuth2/OpenID provider/application for BillTracker:

  • Client type: confidential
  • Redirect URI: https://bills.example.com/api/auth/oidc/callback
  • Scopes: openid email profile groups
  • Groups claim: make sure authentik sends groups
  • Admin group: create or choose the authentik group that should become BillTracker admins

In BillTracker, go to Admin -> Authentication Methods and set:

  • Provider name: authentik
  • Issuer/discovery URL: https://yourURL.com/application/o/bills/.well-known/openid-configuration
  • Client ID and client secret from authentik
  • Redirect URI matching the authentik allowed redirect URI
  • Scopes: openid email profile groups
  • Admin group: the exact authentik group name for BillTracker admins
  • Auto-provision users: enabled if you want valid authentik users created on first login

The backend accepts either the provider issuer base URL or the full discovery URL. For authentik, the full discovery URL example is:

https://yourURL.com/application/o/bills/.well-known/openid-configuration

Keep local login enabled until you have tested authentik login with an admin-group user.

Data, Imports, Exports, And Backups

BillTracker stores data in SQLite. By default the database is db/bills.db; set DB_PATH for a different location. In Docker, the image sets DB_PATH=/data/db/bills.db and BACKUP_PATH=/data/backups.

User data is scoped to the signed-in user. User exports include bills, categories, payments, monthly bill state, notes, and export metadata. They do not include password hashes, sessions, admin settings, SMTP credentials, backup files, server paths, or other users' data.

Data tools:

  • XLSX spreadsheet import with preview before apply
  • user SQLite import from BillTracker user exports
  • user SQLite export
  • Excel workbook export
  • import history
  • admin full database backup, import, download, restore, delete, scheduled backups, and retention

Backups and exports contain sensitive financial data. The code writes SQLite backup files with restrictive file permissions, but backup/export encryption is not implemented. Protect downloaded files and backup storage yourself.

Security Notes

  • Auth is required for user data routes.
  • Admin routes require an admin session.
  • The default admin account cannot access user tracker routes.
  • User-owned bill, category, payment, import, and export routes derive ownership from the authenticated session (req.user.id in SQL).
  • CSRF protection uses a double-submit cookie pattern: a bt_csrf_token cookie is set on responses, and mutating requests must include a matching x-csrf-token header. Defaults are httpOnly, sameSite=strict, and secure (overridable via env vars).
  • Local login, password change, import, export, admin actions, and OIDC routes have per-IP in-memory rate limits.
  • CORS is disabled unless CORS_ORIGIN is set.
  • Security headers include Content-Security-Policy with per-request nonces, plus standard hardening headers. HSTS is sent only when HTTPS=true.
  • Session cookies are httpOnly, sameSite=strict, and marked secure when COOKIE_SECURE=true, HTTPS=true, or the request appears to be HTTPS.
  • Password changes rotate the current session ID and invalidate all other sessions.
  • Audit logging records security-sensitive events: login, logout, password changes, role changes, CSRF failures, and migration operations.
  • OIDC validation is handled through openid-client using discovered provider metadata and JWKS.
  • Protect database files, backups, and exports as sensitive financial records.

Reverse Proxy And HTTPS

Run BillTracker behind HTTPS for normal use. If TLS terminates at a reverse proxy, forward X-Forwarded-Proto: https so secure-cookie detection can work. You can also set HTTPS=true or COOKIE_SECURE=true.

Set CORS_ORIGIN only when the frontend and backend are served from different origins. For the normal same-origin deployment, leave it unset.

Project Structure

client/      React app, pages, layout, UI components
db/          SQLite connection, schema, startup migrations
middleware/  auth checks, CSRF, rate limits, security headers
routes/      Express API routes
services/   auth, OIDC, backups, scheduler, imports, cleanup, status, notifications, audit
workers/    daily background tasks (notifications, cleanup)
setup/      first-run admin setup
scripts/    DB migrations, seed data, and smoke/import tests
public/     legacy static assets
img/        app-runtime images and source screenshots
docs/       Engineering Reference Manual, CSRF guide, Authentik integration

Upgrading

For a direct Node install:

git pull
npm install
npm run build
npm start

Restart your process manager after building. The app initializes the SQLite schema and runs additive migrations on startup; the Docker entrypoint also runs scripts/migrate-db.js before starting unless RUN_DB_MIGRATIONS=false.

For Docker, pull/rebuild the image, recreate the container, and keep the /data volume mounted.

Known Limitations

  • Admin backups and user exports are not encrypted by the app.
  • OIDC single logout is not implemented.
  • User exports (Excel and SQLite) currently omit cycle_type, cycle_day, and bill_history_ranges data.
  • Rate limiting is in-memory, so counters reset on restart and are not shared across multiple app instances.
  • Authentik live login must be tested in your deployment with your authentik provider.
  • The XLSX parser dependency has known upstream security advisories; the import route is authenticated, file-size limited, and parses cells as data.

License

License: Not specified.