ZachZimm/quicker

★ 0Forks 0PythonGitHub ↗Compare

README

Quicker

A local web application for turning invoice photos, paid tax stubs, and credit card statements into reviewed transactions for Quicken. The browser is the main workspace; the Windows companion uploads a folder and synchronizes originals to a permanent archive.

The Windows companion refreshes Quicken and enters approved transactions. It exports the complete reference before entry, atomically rechecks approvals, then verifies attempted transactions in another fresh export. Interrupted or ambiguous outcomes remain unresolved until reconciliation.

Run on Linux

Requires Python 3.12+, Node.js 22+, and a running vision-model endpoint. From the repository root:

uv sync --extra dev
npm --prefix web ci
npm --prefix web run build
cp .env.example .env
uv run quicker setup --username admin
uv run quicker import-qif /path/to/quicker-reference.QIF
uv run quicker serve --host 0.0.0.0 --port 8999

In another terminal, start the durable extraction worker:

uv run python -m quicker.worker

Open http://localhost:8999 on this computer, or use the Linux machine's LAN address from another device. The development server uses HTTP. For the planned HTTPS deployment, use the reverse-proxy configuration below.

New installations default to the local Bonsai service on port 9090, base path /v1, protocol chat-completions, and model bonsai-2-27b. Set the private server host and API key in Settings. Existing saved settings are retained. The default response budget is 32,768 tokens, configurable up to 65,536; the model context must also fit the input images, prompt, and any reasoning. The tested Bonsai setup uses a 65,536-token context and its vision projector. Protocol, transport, host, port, base path, model, optional key, timeout, concurrency, output limit, and image size are editable in Settings. A blank key on a new connection sends no Authorization header; the explicit Remove key checkbox clears an existing key. The vision check uses a small synthetic image. Save settings before testing them.

The app reads environment variables from its process. For custom .env values, use uv run --env-file .env quicker … and uv run --env-file .env python -m quicker.worker, or export them in the shell. The systemd units read .env automatically. QUICKER_DATA_DIR defaults to ./data; keep it on a local disk. QUICKER_WEB_DIST can override the path to the built browser files.

If a shared account was provisioned during development, its generated credentials are in the ignored .local/first-login.txt. quicker setup resets the shared password and invalidates browser sessions. It does not delete documents.

Staying signed in

Browser logins persist across browser and server restarts, with no server-side session timeout. Quicker renews its 400-day HttpOnly cookie on successful authenticated requests. Regular use keeps the login active. Clearing browser cookies, private browsing, or browser retention limits can still require a login.

Log out to revoke that browser's session. Running quicker setup to reset the shared password revokes every browser session. Existing unexpired logins upgrade automatically on their next authenticated request; expired logins require one new sign-in. Restart the server after installing this change.

Use the workspace

  1. Import an all-accounts QIF export with Transactions, Account List, Category List, and Memorized payees in Settings. Verify the transaction count and date range for each account in the coverage table. Blank-payee entries and original QIF records are retained. Importing never writes to Quicken.
  2. Review the property directory and year/account mappings. Known aliases share a property identity; units remain separate labels. Exact Quicken account names are retained. Automatic assignments use the property's latest mapped year, including when the payment date is missing; auto insurance uses the exact R&K Properties account. All assignments remain editable.
  3. Choose Upload & analyze for JPEG, PNG, or HEIC images. Analysis starts automatically and detects the document type. Choose whether files are separate documents or ordered pages of one document. Originals are retained; smaller JPEG copies are used for extraction and preview. Limits are 20 images, 50 MB per image, and 150 MB per upload batch.
  4. Keep the worker running. Documents show Waiting for analysis, Analyzing, or the completed/failed result. The analysis panel shows worker availability, queue counts, and the saved model connection's status. Worker heartbeats expire after 15 seconds; the model-server check is cached for 30 seconds and sends no images or inference requests. The vision check in Settings also tests image support. Restart both the server and worker after updating; startup applies database migrations. Temporary model outages leave documents queued and resume automatically, without spending document-analysis attempts. Invalid output has up to three attempts; invalid request settings require intervention. Analyze again retries a completed or failed document using current settings, or updates a waiting job with older settings. Waiting documents show retry timing, worker availability, or a busy queue. A job uses the model settings revision saved when it was queued. Group only as many pages as fit the configured model's context. A truncated response is rejected in full and cannot create partial transactions.
  5. Edit fields directly in the review register. Click a cell or press Enter/F2 to edit, Tab to move between fields, and Escape to cancel a cell edit. Enter saves the row; leaving a row saves automatically. Property, unit, category, account, and expense tag offer searchable choices. Enable Tag and memo for optional columns. Details opens the source page, full form, duplicate matches, and change history. Dates are required before approval. Failed saves keep the draft; Save row retries and Discard edits / reload fetches the current saved transaction. Background refreshes preserve drafts. Card purchases use purchase dates, refunds are positive, and payments, fees, interest, and visibly crossed-out items are ignored. Card properties are left for manual review except the business rules below. Generic insurer names alone do not establish auto coverage. Other handwriting is ignored except tax payment confirmations and dates. Underlines, check marks, and adjacent notes alone do not exclude a transaction.
  6. Select rows for bulk property, account, category, or date assignment and batch approval. Matching Quicken history shows the account, date, amount, payee, category and reason for the suggestion. Use Already in Quicken to link the document row and exclude it from entry, or acknowledge that it is a separate transaction before approval. A link does not invent missing document fields. Matches use property and unit evidence, allow nearby payment dates, and can flag a payment within a printed service period when the bill has no date. New duplicate evidence invalidates affected approvals and open edits. Removing a row preserves it; use Remove directly on its table row, then Restore in the Removed tab if needed. Removal discards any unsaved inline edits. Restoring returns it to review. Editing an approved row requires approval again.
  7. Open a source document to add a missed transaction or choose Analyze again. Reanalysis produces a comparison when rows already exist. Use Compare analysis and select missing rows to add; saved edits, approvals, existing-transaction links and removed rows remain intact. New rows require review. Repeat requests cannot add them twice.
  8. Pair the Windows companion to populate its archive, including phone uploads. Optionally select the all-accounts QIF export file to sync it automatically whenever Quicken writes a changed export.

Catalog assignments from extraction are suggestions. Required dates, valid catalog names, exact amounts, and duplicate acknowledgement are enforced by the server at approval time. The model can still make reading mistakes; review the source before approval. No model output can initiate Quicken entry.

Model availability and recovery

Uploads, document storage, existing transaction review, and Quicken synchronization operate independently of model availability. Offline connections, timeouts, model loading and overload defer analysis automatically. The shared cooldown is stored in the database per endpoint, model and credentials. Delays grow from 30 seconds to one, two and four minutes, then cap at five minutes. A longer server-provided Retry-After is honored. After a cooldown, one document checks recovery before other waiting documents proceed; expired worker claims remain recoverable after a restart.

The analysis panel shows the waiting reason, next recovery check, and last successful analysis for the configured model. Retry now advances local cooldowns for waiting jobs, while respecting server-requested delays and active recovery checks. It does not revive failed documents. Analyze again creates a new attempt using current settings, preserving edits, removed transactions, and the comparison flow for missing transactions. Existing jobs retain their saved model settings until explicitly updated.

Bonsai receives a lightweight health check before analysis to detect loading or an unavailable model. Health does not guarantee that the next inference will fit in GPU memory. A memory error from health keeps documents waiting without blaming a document. Three memory failures during inference for the same document stop that job with an explanation to free memory or reduce pages/context before trying again. Authentication errors, unsupported requests, and explicit context capacity errors stop immediately. Invalid transaction JSON retains bounded retries. Raw provider errors are never saved or shown; only recognized failure reasons and sanitized messages are retained. Repeated availability failures update the current state instead of growing document analysis history indefinitely.

Concurrency still defaults to one. Quicker does not stop other GPU workloads, change Bonsai's resource allocation, or silently lower extraction quality to fit a request. Keeping the model host running or restarting it after resources become available remains the model host's responsibility.

Web dialogs can be dismissed with their close button, Escape, or a click on the background. Clicking inside or dragging from inside to outside does not dismiss them. Uploads and account submissions keep the existing disabled-close behavior until the request completes.

Quicken export synchronization

Select the intended .QDF in the companion and open that file in Quicken. Refresh from Quicken / reconcile creates a new all-accounts, all-dates export with transactions and reference lists, including changes made outside Quicker. The companion refreshes on connection and every 15 minutes when the desktop has been idle for at least a minute and Quicken is not the foreground app. Background refresh defers while the desktop is in use, locked, showing a dialog, or the foreground application covers its monitor, including fullscreen video and borderless games. After an automatic export, it restores the previously active window if it still exists and the user has not taken over or switched apps. Windows can refuse a focus change; restoration failures are logged. Manual refresh and the exports required for transaction entry still run on request. The optional QIF watcher still backs up externally created files; a watched file never authorizes entry.

Enter approved transactions is available in both apps after updating the server and configuring the companion. Keep the desktop idle while it works. On the browser's Review page, the button is enabled when the companion is connected and transactions are approved. It enters all approved transactions regardless of the current filter. Pending register edits are saved first; changed rows need approval again before entry. Physical input, focus loss, an unexpected dialog, a changed data file, or a connection failure stops the operation. Each run claims exact approved revisions only after its fresh export activates. Entry uses single-transaction QIF imports and verifies account, date, amount, payee, category, expense tag, rental tag and unique memo reference afterward. Bank, Cash and CCard accounts are supported. Payees are limited to 63 characters and source memos to 36, leaving 27 characters for the verification reference. Unsupported values return to review with an explanation; they are never silently shortened.

For uncertain outcomes, inspect Quicken and choose Refresh from Quicken / reconcile. The companion never repeats an attempted import. When a current export contains no verification reference, the browser can return the row to review after you confirm that it was not entered. Duplicate or mismatched references require correction in Quicken and another refresh. Preserve the companion's local state folder and journal when updating. See Windows client plan.

Settings lists each distinct received version, its transaction coverage and a backup download. The CLI import-qif command uses the same versioned storage. Exports with reduced history, unreadable amounts/dates, an older source timestamp or a conflicting reference update are backed up but held for review. Only activate such a version after checking its coverage. Retrying an upload never reactivates an archived version. Original export bytes are included in quicker backup. A QIF backup does not replace a full Quicken data-file backup.

Rows linked as Already in Quicken cannot be approved for entry. If their match vanishes from a later active export, they return to review. Separate-transaction acknowledgements are bound to the duplicate evidence; new matches require review again. Delivery rechecks eligibility and revisions atomically when claiming work. Fresh export events have IDs separate from content hashes: unchanged exports still record freshness, while retrying an old event cannot reactivate it.

LM Studio extraction

The LM Studio native protocol uses base path /api/v1 and offers a request-level reasoning control. The supplied local model was also tested with reasoning off because reasoning could exhaust its loaded 8,192-token context. Chat completions and Responses remain available for other compatible endpoints. The output limit bounds generation but does not enlarge the model's loaded context. Truncated or invalid results never create partial rows.

Private property configuration

Real property addresses, parcel identifiers, unit tags, and utility-service identifiers belong in data/private-profile.json, which Git ignores. Set QUICKER_PROFILE_PATH to use another private location. This JSON object contains properties, parcels, unit_mappings, and the installation's frozen legacy_units migration map. The synthetic fixture in tests/fixtures illustrates the structure; it is not production data. Restart server and worker after editing this configuration. Without a profile, automatic address/parcel/unit matching is unavailable; imported account routes still work.

The CLI backup includes this profile as private-profile.json. Restore it alongside the database and originals, and keep it private. Model hosts, credentials, and client server URLs also belong in local settings, not source or documentation. Tracked examples use fictional property identities and generic network addresses. Removing values from current files does not remove them from earlier Git commits.

Address-based property assignment

Bills and invoices can assign a property from a model-read service or job address. For utility bills, the customer address is also eligible when no conflicting service address is shown. The address must match a verified entry in the private property profile; house numbers are never fuzzy-matched. Street abbreviations and unit suffixes are normalized, while conflicting cities or states prevent a match. Mailing and supplier addresses, credit card statement addresses, and tax stubs do not trigger this rule.

The original address remains visible in the private review workspace. Further verified addresses can be added to the local property directory. Explicit business rules take precedence and conflicting evidence is flagged. Assignments remain editable. The destination account defaults to the property's highest mapped year, independently of the transaction date. A payment date is still required for approval; billing and due dates are not substituted.

Choosing and requesting accounts

Click an Account cell to open the existing options, then type to narrow the list without regard to capitalization. Property, unit, category, and expense-tag cells use the same searchable dropdown. Selecting an account manually is an override; choose Use automatic account to return to the property's latest mapped year. Date changes do not select an older account. Review older-year expenses explicitly if they should go to an older register.

For a name absent from the account list, choose Create … in Quicken or press Enter and confirm the exact name. New accounts are always Bank accounts; there is no account-type selector. Names may contain at most 39 characters, matching the verified Quicken import limit. Windows must be paired and configured for a Quicken file. Requests can wait while Windows is offline. The browser saves the requested account on the transaction but labels it pending; approval requires the account to appear in an imported Quicken account list. The request itself completes only after a fresh Windows export verifies creation. Requests do not insert invented accounts into the reference catalog.

The Windows companion creates the manual account using an account-only QIF import, without an opening-balance transaction or online connection. Fresh native exports before and after creation verify the exact name and Bank type. Older clients leave requests queued. Interrupted attempts are verified without repeating creation; if the account is absent, inspect Quicken and finish creating that exact account manually, then refresh. Keep the companion's journal when updating. The Windows client plan summarizes the guarantees to preserve and links to the implementation and tests.

Rental units and shared expenses

Multiple rentals can share one annual property register. Select the appropriate Quicken unit tag in the Unit column or detail form for a unit-specific expense, or Whole property for a shared expense. Unresolved means the available evidence does not identify a rental; it remains visible in the table and does not prevent approval. The unit never changes the property account or divides the amount.

Unit selection supplies the exact existing Quicken unit tag. The separate expense tag, such as Utilities, is retained. Review displays both intended Quicken tags; Changing properties clears the unit assignment. Unit selections must belong to the chosen property, and their tags must exist in the imported catalog before approval. Manual selections take precedence over automatic suggestions.

Verified parcel tax receipts default to Whole property. A property's main address alone does not select a rental. The full printed address, including any unit suffix, and printed utility account number are retained for review. Customer, premises and meter numbers are not substituted for the utility account number.

The private profile's unit_mappings records verified utility customer IDs and unit addresses. A service location and a Quicken rental label may remain unresolved until evidence establishes their relationship. Add a mapping only when bills or other records establish the identity. Each mapping is a dictionary with property, unit, merchant, a readable evidence explanation, and one or more of utility_account, service_customer_id, or address. An address has street, city and state.

The assignment rules require the same merchant and every configured identifier. Account matching tolerates spaces and hyphens but preserves leading zeros. Address matching requires a service/job address with matching street, unit, city and state; a mailing or utility-customer address cannot select a rental. Conflicting verified matches leave the unit unresolved. An explicit unresolved mapping can retain evidence without selecting a unit. These rules run only when proposals are created.

The database migration adds explicit unit fields to existing rows, moves recognized unit tags into the unit field, and marks verified parcel taxes as whole-property expenses. It preserves amounts, accounts, statuses and audit history, and increments revisions so an already-open editor cannot overwrite the migrated row.

Utility statements and service locations

Upload photos without selecting a document type. The vision model identifies credit card statements, tax receipts, invoices and utility bills from their contents. Separate documents may share an upload batch. The existing grouping checkbox is only for ordered pages of the same document.

WM service-detail pages produce one proposed expense per printed service-location subtotal, each with its own address and service customer ID. The billing customer ID stays separate; it must never be copied to every location as that location's ID. The combined statement total, individual subtotal components, previous payment lines and instructional sample invoices do not become additional expenses. This uses the bill's printed location amounts, without inventing allocations.

Single-location sewer bills use current charges, including sewer, storm and flood charges once. A prior payment shown in the account summary is not the payment for the current charges. Auto Pay notices do not establish a payment date. Invoice dates, invoice numbers and service periods are retained as source context, while payment dates remain blank unless established by the document. Missing pages are flagged; charges for unseen locations are never inferred from a remaining balance.

The September 2026 source batch is IMG_3336 through IMG_3341. IMG_3338 and IMG_3339 are consecutive WM detail pages whose six location totals reconcile to $572.31. IMG_3340 and IMG_3341 are different invoices, each showing only page 3 of 3; their visible charges do not cover the whole statement. Highlights, check marks and adjacent notes do not exclude their printed rows.

Bookkeeping defaults

The conventions for this setup live in server/quicker/profile.py; matching rules live in server/quicker/rules.py. Preferred categories appear first in review. Printed payees and source descriptions are preserved. Merchant aliases are used for matching, including card descriptors and the existing City Of/City 0f variants.

Recognized expense Preferred category Assignment
Auto insurance Insurance (Business):Truck R&K Properties; exact R&K Properties account
iCloud renewal ICloud R&K Properties; latest mapped account
HP All-In Plan All In Plan R&K Properties; latest mapped account
HP Instant Ink Printer Plan R&K Properties; latest mapped account
Sam's Club fuel Truck Gas R&K Properties; latest mapped account
The Wash Shop / Wash Shop Truck Wash R&K Properties; latest mapped account
USPS PO box renewal P.O. Box-6 Months R&K Properties; latest mapped account
USPS postage Postage and Delivery (Business) R&K Properties; latest mapped account
Explicit Spectrum Mobile/cell service Cell Phones R&K Properties; latest mapped account
TMWA Water Property remains for review
City of Reno / City of Sparks sewer Sewer Property remains for review
Waste Management Garbage Property remains for review
Paid Washoe County tax stub Property Tax Verified parcel mapping; latest mapped account

Eight Washoe parcel mappings in server/quicker/profile.py were verified from the property-location fields of the 2026 tax notices, IMG_3320.HEIC through IMG_3334.HEIC, even-numbered files. The notice pages remain external reference evidence. Only the second-page receipt images are imported as source documents. Parcel matching preserves leading zeros, accepts spaces and hyphens, and applies only to Washoe County Treasurer tax rows. Unknown parcels remain for review.

A Sam's Club charge without fuel details includes a confirmation warning; recognized membership, grocery and merchandise charges do not get that default. Generic Spectrum, State Farm and USPS charges remain for review when the service is unclear. HP All-In Plan and Instant Ink remain separate services. These rules run when proposals are created; saving a manual correction does not reapply them. Missing catalog categories or accounts prevent approval rather than substituting another destination. Historical records, transfers and opening balances remain reference data and never become new proposed transactions.

Windows companion

On Windows with Python 3.12 installed, run from the checkout:

py -3.12 -m venv .venv-windows
.\.venv-windows\Scripts\python.exe -m pip install -e '.[client]'
.\.venv-windows\Scripts\quicker-client.exe

Create a pairing code in the browser's Windows companion tab. Enter the Linux server address and code in the Windows app, choose separate input and archive folders, pair, then click Save & connect. Pairing codes expire in ten minutes. Only one active companion is supported; revoke an old device before replacement. The default input folder is Quicker Input on the Windows Desktop, including redirected desktops such as OneDrive. Existing saved folder choices are preserved.

The app waits until input-file size and modification time are stable across two scans. It uploads, checks the server receipt, writes and verifies the archive, then removes the unchanged input file. It persists upload request IDs, resumes interrupted downloads, and acknowledges archive copies only after checksums match. Unsupported files remain in the input folder. Archives and input folders cannot contain one another. Archive downloads never feed back into ingestion.

Paired installations start with Windows and minimized to the tray by default; both preferences are available in settings. Closing the window keeps sync running. Launch again or double-click the tray icon to open settings. The tray and window's Quicker menu provide web access, manual refresh, pause/resume of automatic exports, and Quit. Pause leaves document synchronization and explicitly requested operations available. Quit waits up to 15 seconds for workers; unfinished operations reconcile from their journals on restart. State and its revocable device credential live in %USERPROFILE%\.quicker. No incoming Windows network port is needed.

Open client\dist\Quicker\Quicker.exe (keep its _internal folder beside it). Launching from Explorer, a shortcut, or Windows sign-in uses the same per-user settings, independent of the working directory. The pairing code is temporary: after pairing, the app saves a device credential and reconnects with it on later launches. The Paired label confirms this; an empty New pairing code field does not mean pairing was lost. Server restarts normally do not require re-pairing. Re-pair only if the server rejects or revokes that device credential.

Connection settings live in %USERPROFILE%\.quicker\config.json, with a validated config.backup.json recovery copy. Both contain the private device credential; keep them private. The client recovers the backup if the primary is missing or unreadable, and refuses to overwrite settings changed by another process. Use Quicker > Reload saved settings after disconnecting if recovery is needed. Upgrades replace the application folder and retain this per-user state directory. This profile-level location avoids Windows AppData virtualization when the EXE is launched from a packaged desktop host. On first use, the client migrates the saved pairing and all journals from the former AppData location, including the Codex private cache when present. The legacy copy is retained. Migration requires the old companion to be stopped and refuses ambiguous installations. Tests and special installations can explicitly set QUICKER_STATE_DIR to another state directory.

To build a standalone Windows folder, run:

.\client\build.ps1

Distribute the entire client/dist/Quicker folder, including Quicker.exe. Packaging must run on Windows. The supported Quicken version, deployment notes, and background behavior are documented in the Windows client guide.

Persistent deployment and backups

The application and worker can run as systemd units. After installing the repository dependencies, building the browser, and configuring the shared login:

  1. Set an absolute QUICKER_DATA_DIR in .env and QUICKER_COOKIE_SECURE=true for HTTPS access.
  2. Adapt deploy/Caddyfile.example to a LAN hostname resolving to this server. Configure Caddy and trust its local certificate authority on client devices.
  3. Run sudo ./deploy/install-systemd.sh. The units run as the invoking user, bind the application to loopback, and restart failed processes. These units are provided but are not installed automatically by development setup.

Logs are available with journalctl -u quicker -u quicker-worker. Model provider errors are redacted before persistence; model keys are never returned to browsers. The document directory and database contain private source and review data.

Create a consistent database snapshot and copy its immutable originals:

uv run quicker backup /path/to/new-backup-directory

To restore, stop the application and worker, preserve the current data directory, copy the backup's quicker.sqlite3 and documents/ into a new data directory, point QUICKER_DATA_DIR there, and restart. Do not copy stale -wal or -shm files from a running installation. The client document archive does not contain review history and is not a substitute for this backup.

Development and verification

uv sync --extra dev --extra client
npm --prefix web ci
npm --prefix web run build
uv run playwright install chromium
uv run pytest -q
uv run ruff check server client tests

npm --prefix web run dev provides frontend hot reload and proxies /api to port 8999. Database migrations ship with the Python package; use quicker migrate or server startup to apply them. alembic revision --autogenerate -m "…" creates new migration drafts; inspect them before applying.

Tests cover authentication/CSRF, catalog import, upload idempotency, original access and range downloads, model settings, paid-tax/card rules, exact amounts, review validation and stale revisions, atomic batch approval, duplicate checks, remove/restore, worker recovery, companion archival integrity, and the real browser workflow on desktop and phone layouts. Browser tests use an isolated SQLite database and deterministic extraction, not private documents or Quicken.

Optional browser WebMCP support exposes read-only review inspection and status navigation. It does not approve or enter transactions. Unsupported browsers simply use the regular interface.

See PLAN.md for the design and IMPLEMENTATION.md for verification results and remaining integration work.

Contributors

ZachZimm

Issues