Here's a concise, professional technical briefing for the TaxHacker repository, written in the style of a senior AI engineer consultant report.


The Problem

TaxHacker is a self-hosted AI accounting application that processes receipts, invoices, and transactions using LLMs. It targets freelancers and small businesses seeking to automate expense tracking. The codebase is structured as a portfolio of 8 semi-independent projects sharing a Next.js/React foundation, with significant code duplication and import cycles that increase maintenance risk.

What This Does

TaxHacker accepts receipt/invoice uploads, runs AI extraction via configured LLM providers (OpenAI, Gemini, Mistral), and stores structured data in a Postgres database managed by Prisma. It supports multi-currency conversion (170+ fiat + 14 crypto), custom categories/projects, and field-level AI prompts. The app flows through: upload → AI extraction → categorization → database storage → filtering/export. A circular import involving lib/config, app/(app)/apps/invoices/actions, app/(app)/apps/invoices/page, and models/transactions creates a soundness hazard—changes to any one module risk breaking the others. Additionally, 81 repeated 6-line blocks across 30 files indicate duplicated logic that should be extracted into shared helpers.

How It Is Wired

  • Entry point: app/(app)/page.tsx renders the landing; app/(app)/apps/invoices/page.tsx is the main invoice-upload screen.
  • AI extraction: ai/analyze.ts orchestrates provider calls; ai/prompt.ts and ai/schema.ts define extraction logic; ai/attachments.ts handles file preprocessing.
  • Database: Prisma schema in prisma/ maps to Postgres. Key models live in models/ (transactions, categories, projects). The circular import involves models/transactions.ts, app/(app)/apps/invoices/actions.ts, and app/(app)/apps/invoices/page.tsx.
  • Currency conversion: lib/stats.ts and app/api/currency/route.ts handle rate lookups; branching density of 21 points over 73 lines indicates this logic could be decomposed.
  • Export: components/export/transactions.tsx and app/(app)/export/transactions/route.ts handle data export; a 5.6MB video.mp4 in public/landing inflates clone size.
  • Settings: app/(app)/settings/ exposes UI for LLM provider config, categories, currencies, and business fields.

Files that leave the process: ai/analyze.ts (calls LLM API), lib/stats.ts (computes metrics), prisma/migrations/* (writes to DB). The widest blast radius comes from the invoice-action/page cycle (Ca 2–4, Ce 3–4) and the transaction model (Ca 3, Ce 2) — any refactor must break the cycle or risk cascading failures.

How To Use It

Setup: npm install then npm run build (inferred from package.json). Configuration: Copy .env.example and set OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, and DATABASE_URL. The .env.example file documents the required keys. Running it: npm start (or node .next/standalone/server.js after build). The dev server starts at http://localhost:3000. Docker: docker-compose up -d builds and starts the container (Dockerfile present; service defined in docker-compose.yml).

Real-World Use

A freelancer uploads a stack of receipt photos to the "unsorted" area. ai/attachments.ts preprocesses images, ai/analyze.ts sends them to the configured LLM (e.g., OpenAI), and extracted data (date, amount, vendor) lands in the Postgres database via Prisma. The user reviews, re-categorizes, or splits line items. Month-end, they filter by project or currency and export to CSV for the accountant.

Code Health & Issues

  • HIGH – Pin third-party GitHub Actions to commit SHA: .github/workflows/docker-latest.yml and docker-release.yml use @v3–@v6 tags for docker/setup-buildx-action, docker/login-action, etc. A tag can move, so secrets executed with whatever the owner last pushed. Fix: replace with 40-char SHA; let Dependabot bump them.
  • HIGH – No test suite: 200+ source files have zero test coverage. Any regression reaches production undetected. Fix: add one test per public entry point; add a CI step to run them.
  • MEDIUM – No Dependabot/Renovate configured: 1 manifest, no update bot. Unpatched advisories go unnoticed. Fix: commit .github/dependabot.yml covering npm and github-actions ecosystems.
  • MEDIUM – Container base image not pinned by digest: Dockerfile uses node:23-slim without a digest. Mutable base means different libc/CVE sets across builds with no record. Fix: use node:23-slim@sha256:<digest> and enable Dependabot Docker ecosystem.
  • MEDIUM – No dependency vulnerability scan in CI: no dependency-review-action or osv-scanner step. A known-vulnerable package could merge unnoticed. Fix: add dependency-review on pull_request.
  • MEDIUM – Large binary in repo: public/landing/video.mp4 is 5.6MB. Every clone and CI checkout pays for undiffed data. Fix: add .dockerignore entry or track via Git LFS.
  • MEDIUM – No non-root USER in Docker image: Dockerfile has no USER directive. Process runs as root, so any container escape or writable mount becomes a host-level issue. Fix: add a non-root user, chown needed paths, and USER at the end of the Dockerfile.
  • LOW – Workflow jobs have no timeout-minutes: 2 jobs in .github/workflows/ declare no timeout. A wedged step runs to the 6-hour platform default, risking overlap on a two-hourly schedule. Fix: add realistic timeout-minutes to each job.

The Bottom Line

TaxHacker is a functional prototype that solves a real pain point—AI-extracted, categorized accounting data from receipts and invoices—with solid Next.js/React UI and Prisma-backed Postgres. The portfolio structure means you get multiple experiment buckets under one repo, but the import cycles, duplicated blocks, and zero test coverage are production risks. It’s suitable for early-stage use or as a sandbox for LLM-to-database workflows, but any team considering it for recurring client work should budget time to break the circular imports, add tests, and lock down the CI/CD pipeline before promoting past prototype stage.


Word count: 512