PayrollTracker
Real Illinois payroll, on the desktop.
A Windows desktop payroll application for a real Illinois employer. A Tauri 2 and React shell drives a tested .NET 8 payroll engine running as a local JSON-RPC sidecar, with everything stored in SQLite on the machine. It is intended to pay real people, so correctness leads the roadmap.
- Problem
- Run real Illinois payroll from a Windows desktop application.
- Constraint
- Statutory figures change by year, and a plausible stale value can pay people incorrectly.
- Decision
- Keep payroll arithmetic in a tested .NET domain engine, verify each supported tax year against IRS, SSA, and Illinois DOR sources, and throw for every other year.
- Evidence
- 173 tests across 18 files; 2024–2026 statutory figures are compiled with primary-source citations.
What it is
This is not a demo. It is intended to pay real people, which sets the order of everything else: statutory tax figures are verified against IRS, SSA, and Illinois Department of Revenue primary sources and cited in the code, and the engine throws rather than guess for any year it has not verified.
The user interface is a Tauri 2 and React desktop app. All payroll math runs in a tested .NET 8 engine that the frontend drives as a local JSON-RPC sidecar. Data is stored locally in SQLite under the per-user app-data directory — never in the install directory.
Architecture
- React + TypeScript
apps/desktop, rendered in WebView2invoke("backend_command") - Rust shell
apps/desktop/src-taurinewline-delimited JSON-RPC over stdio payroll-backend.exe.NET 8, self-contained single filePayrollManager.Domainthe authoritative payroll engineEF Core- SQLite
%LOCALAPPDATA%, DPAPI-encrypted SSNs
The payroll engine
All calculations live in PayrollManager.Domain, covered by 173 tests across 18 test files.
- Federal withholding
- IRS Publication 15-T percentage method for automated payroll systems (Worksheet 1A), with full Form W-4 (2020+) support: filing status, the Step 2(c) multiple-jobs checkbox, dependents and other credits, other income, deductions, and extra per-period withholding.
- Illinois withholding
- Flat 4.95% with the per-allowance exemption from Form IL-W-4, per Booklet IL-700-T.
- FICA
- Social Security at 6.2% up to the annual wage base ($184,500 for 2026), Medicare at 1.45%, and Additional Medicare at 0.9% above $200,000 — withheld without regard to filing status, which the employee reconciles on Form 8959.
- Employer taxes
- FUTA at 0.6% net on the first $7,000, and Illinois SUI, whose rate and wage base are employer-specific and therefore entered in Settings rather than hardcoded. A zero surfaces a warning instead of a silently wrong liability.
- 401(k)
- Pre-tax deferrals capped at the IRC §402(g) elective-deferral limit, with the age-50-and-over catch-up.
- Overtime
- FLSA overtime for hourly employees.
Every amount is a decimal, rounded to the cent half away from zero — matching IRS worksheet instructions rather than banker's rounding. Each line is rounded as it is produced, and every total is the sum of already-rounded lines, so a pay stub's totals always match the lines printed beneath them.
Statutory figures for 2024, 2025, and 2026 are compiled in with primary-source citations. Running payroll for a year with no verified figures throws TaxRulesNotAvailableException instead of reusing another year's numbers.
Pay-run lifecycle
Pay runs move Draft → Calculated → Posted, with Voided as the only exit from Posted. A posted run is immutable: money has been committed, so corrections are made by voiding and issuing an adjustment run, never by editing history.
Posting recomputes the amounts and compares them against a hash of what the user reviewed, so a run can never post numbers different from the ones that were approved. Every mutation is written to an append-only audit log.
Security posture
- No shell access from page code. The sidecar is spawned from Rust with
std::process, not through Tauri's shell plugin. The only bridge isbackend_command, which forwards a method name the sidecar either recognises or rejects — page code cannot name an executable, pass process arguments, or express SQL. - Money is display-only in the frontend. Amounts arrive already computed and are rendered as-is; the app never re-derives or sums money in JavaScript, where float drift would disagree with the backend's decimal math.
- One request in flight at a time. The Rust client serialises calls behind a mutex, so a response line always belongs to the request just sent. A pre-send failure is retried once on a fresh process; a post-send failure, where the command may already have committed, is never silently replayed.
- SSNs are encrypted at rest with Windows DPAPI and never sent to the frontend. Only the last four digits are kept in plaintext, for masked display.
What it deliberately does not do
The payroll math is correct, but this is not yet a compliance solution, and the preview label is meant literally.
- No employment-tax form generation (941, 940, W-2), no deposits, and no e-file.
- Illinois only. No other states, and no multi-state employees.
- Installers are unsigned, so SmartScreen warns on first launch. This is expected.
- It is not tax or legal advice. The employer remains responsible for deposit and filing obligations.