Docs Developers

Development setup

For SlowBooks Pro v2.31.4 · Download this guide (one HTML file) · Source on GitHub

Tech stack, project layout, and contributor flow. For install instructions see Installing SlowBooks Pro; for the public security policy and disclosure process see SECURITY.md; for internal hardening notes see Security hardening.


Tech Stack

ComponentTechnology
BackendPython 3.13 + FastAPI (89 routers, 735 API operations on 588 paths in 2.31.4)
DatabaseSQLite company files (desktop, Server Edition) or PostgreSQL 17 + SQLAlchemy 2.0
MigrationsAlembic; every table and column change needs one (see below)
FrontendVanilla HTML/CSS/JS (no framework) + self-hosted Chart.js 4.4.6 for analytics
PDFWeasyPrint 70.0 + Jinja2
Bank feeds and importsSimpleFIN, Mercury; ofxparse (OFX/QFX), CSV
PaymentsStripe Checkout, PayPal and Square (hosted pages), one abstraction in app/services/payments/
AuthNamed users in four roles, sessions, scoped API tokens
Port3001

Project Structure

SlowBooks-Pro-2026/
โ”œโ”€โ”€ .env.example              # Environment config template
โ”œโ”€โ”€ requirements.txt          # Python dependencies (production)
โ”œโ”€โ”€ requirements-dev.txt      # Test + lint deps
โ”œโ”€โ”€ run.py                    # Uvicorn entry point (port 3001)
โ”œโ”€โ”€ alembic.ini               # Alembic config
โ”œโ”€โ”€ migrations/               # Alembic database migrations (script_location in alembic.ini)
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ main.py               # FastAPI app + the routers, role policy, startup checks
โ”‚   โ”œโ”€โ”€ config.py             # Environment-based settings (CORS, origins)
โ”‚   โ”œโ”€โ”€ database.py           # SQLAlchemy engine + session + table auto-creation
โ”‚   โ”œโ”€โ”€ models/               # 60 model modules (105 tables)
โ”‚   โ”‚   โ”œโ”€โ”€ accounts.py       # Chart of Accounts (self-referencing)
โ”‚   โ”‚   โ”œโ”€โ”€ contacts.py       # Customers + Vendors
โ”‚   โ”‚   โ”œโ”€โ”€ items.py          # Products, services, materials, labor
โ”‚   โ”‚   โ”œโ”€โ”€ invoices.py       # Invoices + line items
โ”‚   โ”‚   โ”œโ”€โ”€ estimates.py      # Estimates + line items
โ”‚   โ”‚   โ”œโ”€โ”€ payments.py       # Payments + allocations
โ”‚   โ”‚   โ”œโ”€โ”€ transactions.py   # Journal entries (double-entry core)
โ”‚   โ”‚   โ”œโ”€โ”€ banking.py        # Bank accounts, transactions, reconciliations
โ”‚   โ”‚   โ”œโ”€โ”€ settings.py       # Key-value company settings
โ”‚   โ”‚   โ”œโ”€โ”€ audit.py          # Audit log
โ”‚   โ”‚   โ”œโ”€โ”€ purchase_orders.py # Purchase orders + lines
โ”‚   โ”‚   โ”œโ”€โ”€ bills.py          # Bills + lines + payments + allocations
โ”‚   โ”‚   โ”œโ”€โ”€ credit_memos.py   # Credit memos + lines + applications
โ”‚   โ”‚   โ”œโ”€โ”€ recurring.py      # Recurring invoices + lines
โ”‚   โ”‚   โ”œโ”€โ”€ email_log.py      # Email delivery log
โ”‚   โ”‚   โ”œโ”€โ”€ tax.py            # Tax category mappings
โ”‚   โ”‚   โ”œโ”€โ”€ backups.py        # Backup records
โ”‚   โ”‚   โ”œโ”€โ”€ companies.py      # Multi-company records
โ”‚   โ”‚   โ”œโ”€โ”€ payroll.py        # Employees, pay runs, pay stubs, bank accounts
โ”‚   โ”‚   โ”œโ”€โ”€ hr.py             # HR module: onboarding, time entries, PTO, deductions
โ”‚   โ”‚   โ”œโ”€โ”€ pto.py            # PTO policies, requests, accruals
โ”‚   โ”‚   โ”œโ”€โ”€ time_entries.py   # Time entry tracking with approval workflow
โ”‚   โ”‚   โ”œโ”€โ”€ deductions.py     # Deduction types, employee deductions, garnishments
โ”‚   โ”‚   โ”œโ”€โ”€ qbo_mapping.py    # what an earlier QuickBooks Online import left behind
โ”‚   โ”‚   โ”œโ”€โ”€ attachments.py    # File attachments
โ”‚   โ”‚   โ”œโ”€โ”€ bank_rules.py     # Bank transaction categorization rules
โ”‚   โ”‚   โ”œโ”€โ”€ budgets.py        # Budget tracking by account/period
โ”‚   โ”‚   โ”œโ”€โ”€ document_audit.py # a SHA-256 hash per generated tax-form PDF
โ”‚   โ”‚   โ”œโ”€โ”€ portal_access.py  # Portal access audit log
โ”‚   โ”‚   โ”œโ”€โ”€ reseller_permit.py # Per-entity reseller permits
โ”‚   โ”‚   โ””โ”€โ”€ email_templates.py # Customizable email templates
โ”‚   โ”œโ”€โ”€ schemas/              # Pydantic request/response models
โ”‚   โ”œโ”€โ”€ routes/               # FastAPI routers (89; payroll and reports are packages)
โ”‚   โ”œโ”€โ”€ services/
โ”‚   โ”‚   โ”œโ”€โ”€ accounting.py     # Double-entry journal entry engine
โ”‚   โ”‚   โ”œโ”€โ”€ analytics.py      # Business intelligence aggregates (8 methods)
โ”‚   โ”‚   โ”œโ”€โ”€ audit.py          # SQLAlchemy after_flush audit hooks
โ”‚   โ”‚   โ”œโ”€โ”€ closing_date.py   # Closing date enforcement guard
โ”‚   โ”‚   โ”œโ”€โ”€ payroll_service.py # Withholding calculations
โ”‚   โ”‚   โ”œโ”€โ”€ recurring_service.py # Recurring invoice generation
โ”‚   โ”‚   โ”œโ”€โ”€ email_service.py  # SMTP email delivery
โ”‚   โ”‚   โ”œโ”€โ”€ csv_export.py     # CSV export (5 entity types)
โ”‚   โ”‚   โ”œโ”€โ”€ csv_import.py     # CSV import with error handling
โ”‚   โ”‚   โ”œโ”€โ”€ ofx_import.py     # OFX/QFX bank feed parser
โ”‚   โ”‚   โ”œโ”€โ”€ tax_export.py     # Schedule C data + CSV export
โ”‚   โ”‚   โ”œโ”€โ”€ backup_service.py # backups: pg_dump on PostgreSQL, a file copy on SQLite
โ”‚   โ”‚   โ”œโ”€โ”€ company_service.py # Multi-company DB management
โ”‚   โ”‚   โ”œโ”€โ”€ iif_export.py     # IIF export (8 export functions)
โ”‚   โ”‚   โ”œโ”€โ”€ iif_import.py     # IIF parser + import + validation
โ”‚   โ”‚   โ”œโ”€โ”€ pdf_service.py    # WeasyPrint PDF generation
โ”‚   โ”‚   โ”œโ”€โ”€ payments/         # stripe, paypal, square, recorder, clearing
โ”‚   โ”‚   โ”œโ”€โ”€ qbo_export_import.py # QuickBooks Online's Export data zip โ†’ Migrate Data
โ”‚   โ”‚   โ”œโ”€โ”€ auth.py           # Users, roles, sessions, API tokens
โ”‚   โ”‚   โ”œโ”€โ”€ rate_limit.py     # slowapi rate limiting
โ”‚   โ”‚   โ”œโ”€โ”€ settings_service.py # Settings CRUD with sensitive key filtering
โ”‚   โ”‚   โ”œโ”€โ”€ encryption.py     # Fernet encryption + versioned ciphertext + rewrap CLI
โ”‚   โ”‚   โ””โ”€โ”€ crypto.py         # Master key resolution
โ”‚   โ”œโ”€โ”€ templates/            # Jinja2 templates (PDF, email, checks, collection letters)
โ”‚   โ”œโ”€โ”€ seed/                 # Chart of Accounts seed data
โ”‚   โ””โ”€โ”€ static/
โ”‚       โ”œโ”€โ”€ css/
โ”‚       โ”‚   โ”œโ”€โ”€ style.css     # QB2003 "Default Blue" skin
โ”‚       โ”‚   โ””โ”€โ”€ dark.css      # Dark mode CSS overrides
โ”‚       โ””โ”€โ”€ js/               # SPA router, API wrapper, 40+ page modules
โ”‚           โ”œโ”€โ”€ app.js              # Main SPA router (76 routes)
โ”‚           โ”œโ”€โ”€ api.js              # HTTP wrapper (API.get/post/put/del)
โ”‚           โ”œโ”€โ”€ employees.js        # Employee CRUD + Details modal
โ”‚           โ”œโ”€โ”€ onboarding.js       # Onboarding checklists + e-signature
โ”‚           โ”œโ”€โ”€ time_entries.js     # Time tracking + approve/reject workflow
โ”‚           โ”œโ”€โ”€ pto.js              # PTO policies + request workflow
โ”‚           โ”œโ”€โ”€ deductions.js       # Deduction types, employee deductions, garnishments
โ”‚           โ”œโ”€โ”€ tax_forms.js        # W-2, W-3, 940, 941 form generation UI
โ”‚           โ””โ”€โ”€ [30+ more pages]    # Invoices, customers, reports, analytics, etc.
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ seed_database.py      # Seed the Chart of Accounts
โ”‚   โ”œโ”€โ”€ seed_irs_mock_data.py # IRS Pub 583 mock data
โ”‚   โ”œโ”€โ”€ run_recurring.py      # Cron script for recurring invoices + auto-enter memorized transactions
โ”‚   โ””โ”€โ”€ backup.sh             # PostgreSQL backup with rotation
โ”œโ”€โ”€ screenshots/              # README + docs images
โ”œโ”€โ”€ cloudflare/               # Self-hosted Cloudflare Worker AI gateway
โ”‚   โ”œโ”€โ”€ worker.js             # Hardened proxy (model allowlist, rate limiting, security headers)
โ”‚   โ”œโ”€โ”€ wrangler.toml         # Deployment config
โ”‚   โ””โ”€โ”€ README.md             # Setup guide
โ”œโ”€โ”€ tests/                    # ~7,200 pytest tests (auth, security, posting, reporting, imports, payroll, HR, browser tests under Playwright, the wiring, schema, autoescape, rounding, race-condition, N+1, closing-date, secret-redaction, void-symmetry, IIF round-trip and shell-injection audits)
โ””โ”€โ”€ index.html                # SPA shell

Where things go when you add code

  • New HTTP endpoint โ€” drop into app/routes/<domain>.py. Define an APIRouter(prefix="/api/...", tags=[...]), attach @router.get / @router.post / etc., then app.include_router(router) in app/main.py. Group by domain โ€” add to the smallest existing router file, or create a new one for a new feature area.
  • New DB table or column โ€” model goes in app/models/<name>.py and an Alembic migration in migrations/versions/ (alembic revision -m "...", then alembic upgrade head). Both are required: the app refuses to start against a database behind the migration head, and tests/test_gate_290_fixes.py fails on a table only create_all() makes.
  • New Pydantic shape โ€” app/schemas/<domain>.py. Beware the date: date field-shadows-type collision โ€” see CONTRIBUTING.md โ†’ Schema conventions.
  • New SPA page โ€” app/static/js/<name>.js exporting a module object with a render() method; register the hash route in app/static/js/app.js. Talk to the backend through API.get/post/ put/del (the del spelling matters โ€” not delete).
  • Tests โ€” tests/test_<area>.py. The wiring audit (tests/test_wiring.py) will fail CI if your new endpoint has no SPA caller AND isn't on the _INTENTIONAL_BACKEND_ONLY allowlist.

Running tests

pip install -r requirements.txt -r requirements-dev.txt
pytest                       # the full suite, about 7,200 tests; 40 minutes on one machine
pytest tests/test_wiring.py  # under a second: the JS <-> backend wiring audit
pytest -k "audit or portal"  # subset by keyword

The browser tests need Playwright's Chromium (playwright install chromium) and skip without it; the PostgreSQL-only tests run when SLOWBOOKS_TEST_POSTGRES_URL names a database. To run the app while you work: cp .env.example .env, set APP_DEBUG=true, APP_HOST=127.0.0.1 and a PAYROLL_ENCRYPTION_SECRET, then python run.py and open http://localhost:3001 (INSTALL.md, Option 2, has the whole setup). The desktop window is python desktop_launcher.py.

The wiring audit is also a boot-time tripwire in docker-entrypoint.sh: a container that has pytest installed (the Dockerfile installs requirements.txt only, so add requirements-dev.txt in a debug image) fails to start if any JS API call points at a removed route. Production images skip the boot check silently โ€” CI already gates the same condition before the image is built.

Manual end-to-end smoke test

scripts/integration_test_frontend.py is a live-HTTP integration script (deliberately not under tests/ so the pytest collector skips it). It covers the HR and payroll pages against a fresh database at 127.0.0.1:8000, confirming the pages and their API calls answer over real HTTP. Run it against a server started for it when you've touched those pages:

uvicorn app.main:app --port 8000 &
python scripts/integration_test_frontend.py

Lint + format

pip install "black>=24.8.0" "ruff>=0.6.0"
black --check app/ tests/ scripts/   # what CI runs
ruff check app/ tests/ scripts/

CI lints the whole tree (.github/workflows/ci.yml); both pass on every release tag.


Contributor flow

See CONTRIBUTING.md for the full guide. The short version:

  1. Branch off main
  2. Make changes; add/update tests
  3. pytest green locally
  4. Open a PR โ€” CI runs lint, the full suite on Linux and Windows, pip-audit, CodeQL and the Docker build; the installer and DMG build on their own workflows
  5. Reviewer merges once green

The PR template prompts for: summary, test plan, security/perf notes, and any docs updates.