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.tsxrenders the landing;app/(app)/apps/invoices/page.tsxis the main invoice-upload screen. - AI extraction:
ai/analyze.tsorchestrates provider calls;ai/prompt.tsandai/schema.tsdefine extraction logic;ai/attachments.tshandles file preprocessing. - Database: Prisma schema in
prisma/maps to Postgres. Key models live inmodels/(transactions, categories, projects). The circular import involvesmodels/transactions.ts,app/(app)/apps/invoices/actions.ts, andapp/(app)/apps/invoices/page.tsx. - Currency conversion:
lib/stats.tsandapp/api/currency/route.tshandle rate lookups; branching density of 21 points over 73 lines indicates this logic could be decomposed. - Export:
components/export/transactions.tsxandapp/(app)/export/transactions/route.tshandle data export; a 5.6MBvideo.mp4inpublic/landinginflates 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.ymlanddocker-release.ymluse@v3–@v6tags fordocker/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.ymlcovering npm and github-actions ecosystems. - MEDIUM – Container base image not pinned by digest:
Dockerfileusesnode:23-slimwithout a digest. Mutable base means different libc/CVE sets across builds with no record. Fix: usenode:23-slim@sha256:<digest>and enable Dependabot Docker ecosystem. - MEDIUM – No dependency vulnerability scan in CI: no
dependency-review-actionorosv-scannerstep. A known-vulnerable package could merge unnoticed. Fix: add dependency-review onpull_request. - MEDIUM – Large binary in repo:
public/landing/video.mp4is 5.6MB. Every clone and CI checkout pays for undiffed data. Fix: add.dockerignoreentry or track via Git LFS. - MEDIUM – No non-root USER in Docker image:
Dockerfilehas noUSERdirective. 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, andUSERat 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 realistictimeout-minutesto 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