TaxHacker Documentation

Self-hosted AI accounting app. LLM analyzer for receipts, invoices, transactions with custom prompts and categories

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy TaxHacker on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name taxhacker -p 7331:7331 --restart always taxhacker:local
Configured Services & Ports
โš™๏ธ app
image: taxhacker:local
๐Ÿ”Œ Ports: 127.0.0.1:7331:7331
โš™๏ธ cron
image: taxhacker:local
โš™๏ธ postgres
image: postgres:17-alpine
๐Ÿ”Œ Ports: 127.0.0.1:5432:5432
docker-compose.yml
yaml (docker-compose.yml)
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: taxhacker:local
    ports:
      - "127.0.0.1:7331:7331"
    environment:
      - NODE_ENV=production
      - SELF_HOSTED_MODE=true
      - UPLOAD_PATH=/app/data/uploads
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-taxhacker}
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-}
    volumes:
      - ./data:/app/data
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"

  cron:
    image: taxhacker:local
    environment:
      - NODE_ENV=production
      - SELF_HOSTED_MODE=true
      - UPLOAD_PATH=/app/data/uploads
      - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-taxhacker}
      # Optional override. When unset, both containers use the same persisted secret from ./data.
      - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-}
    volumes:
      - ./data:/app/data
      - ./etc/crontab:/mnt/crontab:ro
    restart: unless-stopped
    depends_on:
      - postgres
      - app
    security_opt:
      - no-new-privileges:true
    command: ["docker-cron-entrypoint.sh"]
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"

  postgres:
    image: postgres:17-alpine
    container_name: taxhacker-postgres
    environment:
      - POSTGRES_USER=${POSTGRES_USER:-postgres}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in your .env file}
      - POSTGRES_DB=${POSTGRES_DB:-taxhacker}
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    restart: unless-stopped
    ports:
      - "127.0.0.1:5432:5432"
    security_opt:
      - no-new-privileges:true
    logging:
      driver: "local"
      options:
        max-size: "100M"
        max-file: "3"

Technical Documentation: vas3k/TaxHacker

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

[!NOTE]
โ˜๏ธ I'm currently looking for a job! Particularly interested in companies in Berlin or remote positions in Germany. Here's my CV and Linkedin profile. Thank you ๐Ÿ™

TaxHacker is a self-hosted accounting app designed for freelancers, indie-hackers, and small businesses who want to save time and automate expense and income tracking using the power of modern AI.

Upload photos of receipts, invoices, or PDFs, and TaxHacker will automatically recognize and extract all the important data you need for accounting: product names, amounts, items, dates, merchants, taxes, and save it into a structured Excel-like database. You can even create custom fields with your own AI prompts to extract any specific information you need.

The app features automatic currency conversion (including crypto!) based on historical exchange rates from the transaction date. With built-in filtering, multi-project support, import/export capabilities, and custom categories, TaxHacker simplifies reporting and makes tax filing a bit easier.

๐ŸŽฅ Watch demo video

[!IMPORTANT]

This project is still in early development. Use at your own risk! Star us to get notified about new features and bugfixes โญ๏ธ

โœจ Features

`1` Analyze photos and invoices with AI

Snap a photo of any receipt or upload an invoice PDF, and TaxHacker will automatically recognize, extract, categorize, and store all the information in a structured database.

  • Upload and organize your docs: Store multiple documents in "unsorted" until you're ready to process them manually or with AI assistance
  • AI data extraction: Use AI to automatically pull key information like dates, amounts, vendors, and line items
  • Auto-categorization: Transactions are automatically sorted into relevant categories based on their content
  • Item splitting: Extract individual items from invoices and split them into separate transactions when needed
  • Structured storage: Everything gets saved in an organized database for easy filtering and retrieval
  • Choose your LLM: You can use OpenAI, Google Gemini, or Mistral or even your local LLM (in the self-hosted version). Only you're responsible for the quality and privacy of your data.

TaxHacker works with a wide variety of documents, including store receipts, restaurant bills, invoices, bank statements, letters, even handwritten receipts. It handles any language and any currency with ease.

`2` Multi-currency support with automatic conversion (even crypto!)

TaxHacker automatically detects currencies in your documents and converts them to your base currency using historical exchange rates.

  • Foreight currency detection: Automatically identify the currency used in any document
  • Historical rates: Get conversion rates from the actual transaction date
  • All-world coverage: Support for 170+ world currencies and 14 popular cryptocurrencies (BTC, ETH, LTC, DOT, and more)
  • Flexible input: Manual entry is always available when you need more control

`3` Use your own LLM: Ollama, LM Studio, vLLM, LocalAI etc

It's compatible with your local LLM OpenAI-compatible API endpoint. Just make sure that your local model is good in OCR tasks, results are not guaranteed :)

`4` Organize your transactions using fully customizable categories, projects and fields

Adapt TaxHacker to your unique needs with unlimited customization options. Create custom fields, projects, and categories that better suit your specific needs, idustry standards or country.

  • Custom categories and projecst: Create your own categories and projects to group your transactions in any convenient way
  • Custom fields: You can create unlimited number of custom fields to extraxt more information from your invoices (it's like creating extra columns in Excel)
  • Full-text search: Search through the actual content of recognized documents
  • Advanced filtering: Find exactly what you need with search and filter options
  • AI-powered extraction: Write your own prompts to extract any custom information from documents
  • Bulk operations: Process multiple documents or transactions at once

`5` Customize any LLM prompt. Even system ones

Take full control of how TaxHacker's AI processes your documents. Write custom AI prompts for fields, categories, and projects, or modify the built-in ones to match your specific needs.

  • Customizable system prompts: Modify the general prompt template in settings to suit your business
  • Field or project-specific prompts: Create custom extraction rules for your industry-specific documents
  • Full control: Adjust field extraction priorities and naming conventions to match your workflow
  • Industry optimization: Fine-tune the AI to understand your specific type of business documents
  • Full transparency: Every aspect of the AI extraction process is under your control and can be changed right in settings

TaxHacker is 100% adaptable and tunable to your unique requirements โ€” whether you need to extract emails, addresses, project codes, or any other custom information from your documents.

`6` Flexible data filtering and export

Once your documents are processed, easily view, filter, and export your complete transaction history exactly how you need it.

  • Advanced filtering: Filter by date ranges, categories, projects, amounts, and any custom fields
  • Flexible exports: Export filtered transactions to CSV with all attached documents included
  • Tax-ready reports: Generate comprehensive reports for your accountant or tax advisor
  • Data portability: Download complete data archives to migrate to other servicesโ€”your data stays yours

`7` Self-hosted mode for data privacy

Keep complete control over your financial data with local storage and self-hosting options. TaxHacker respects your privacy and gives you full ownership of your information.

  • Home server ready: Host on your own infrastructure for maximum privacy and control
  • Docker native: Simple setup with provided Docker containers and compose files
  • Data ownership: Your financial documents never leaves your control
  • No vendor lock-in: Export everything and migrate whenever you want
  • Transparent operations: Full access to source code and complete operational transparency

๐Ÿ›ณ Deployment and Self-hosting

TaxHacker can be easily self-hosted on your own infrastructure for complete control over your data and application environment. We provide a Docker image and Docker Compose setup that makes deployment simple:

bash
curl -O https://raw.githubusercontent.com/vas3k/TaxHacker/main/docker-compose.yml

docker compose up

The Docker Compose setup includes:

  • TaxHacker application container
  • PostgreSQL 17+ database (or connect to your existing database)
  • Automatic database migrations on startup
  • Volume mounts for persistent data storage
  • Production-ready configuration
  • no-new-privileges security option on all containers

New Docker images are automatically built and published with every release. You can use specific version tags (e.g., v1.0.0) or latest for the most recent version.

For advanced setups, you can customize the Docker Compose configuration to fit your infrastructure. The default configuration uses the pre-built image from GitHub Container Registry, but you can also build locally using the provided Dockerfile.

Example custom configuration:

yaml
services:
  app:
    image: ghcr.io/vas3k/taxhacker:latest
    ports:
      - "7331:7331"
    environment:
      - SELF_HOSTED_MODE=true
      - UPLOAD_PATH=/app/data/uploads
      - DATABASE_URL=postgresql://postgres:postgres@localhost:5432/taxhacker
    volumes:
      - ./data:/app/data
    restart: unless-stopped

Environment Variables

Configure TaxHacker for your specific needs with these environment variables:

Variable Required Description Example
UPLOAD_PATH Yes Local directory for file uploads and storage ./data/uploads
DATABASE_URL Yes PostgreSQL connection string postgresql://user@localhost:5432/taxhacker
PORT No Port to run the application on 7331 (default)
BASE_URL No Base URL for the application http://localhost:7331
SELF_HOSTED_MODE No Set to "true" for self-hosting: enables auto-login, custom API keys, and additional features true
DISABLE_SIGNUP No Disable new user registration on your instance false
BETTER_AUTH_SECRET Recommended Secret key for authentication and email credential encryption (minimum 16 characters). In self-hosted Docker, TaxHacker auto-generates and persists one in ./data/.better_auth_secret if unset. your-secure-random-key
POSTGRES_PASSWORD Yes (docker-compose.build.yml) Password for the bundled Postgres when building locally โ€” used in both POSTGRES_PASSWORD and DATABASE_URL output of openssl rand -hex 24
POSTGRES_USER No (docker-compose.build.yml) Database user (defaults to postgres) postgres
POSTGRES_DB No (docker-compose.build.yml) Database name (defaults to taxhacker) taxhacker

๐Ÿ’ก Need internet access with authentication? See docs/self-hosted-public-access.md for a workaround (yes, it's cursed).

โŒจ๏ธ Local Development

We use:

  • Next.js 15+ for the frontend and API
  • Prisma for database models and migrations
  • PostgreSQL as the database (PostgreSQL 17+ recommended)
  • Ghostscript and GraphicsMagick for PDF processing (install on macOS via brew install gs graphicsmagick)

Set up your local development environment:

bash
# Clone the repository
git clone https://github.com/vas3k/TaxHacker.git
cd TaxHacker

# Install dependencies
npm install

# Set up environment variables
cp .env.example .env

# Edit .env with your configuration
# Make sure to set DATABASE_URL to your PostgreSQL connection string
# Example: postgresql://user@localhost:5432/taxhacker

# Initialize the database
npx prisma generate && npx prisma migrate dev

# Start the development server
npm run dev

Visit http://localhost:7331 to see your local TaxHacker instance in action.

For a production build, instead of npm run dev use the following commands:

bash
# Build the application
npm run build

# Start the production server
npm run start

๐Ÿค Contributing

No AI-slop PRs. Please open a new Issue and discuss the details with maintainers before sending new changes.

โค๏ธ Support the Project

If TaxHacker has helped you save time or manage your finances better, consider supporting its development! Your donations help us maintain the project, add new features, and keep it free and open source. Every contribution helps ensure we can keep improving and maintaining this tool for the community: https://vas3k.com/donate/

๐Ÿ“„ License

TaxHacker is licensed under the MIT License.


Overview (`app/(app)/apps/email/README.md`)

Email Server Monitor

This app allows you to connect to email servers and automatically monitor incoming emails for file attachments that can be processed by TaxHacker.

๐Ÿ”ง **Setup**

1. Add Email Server

  1. Go to Apps โ†’ Email Server Monitor
  2. Click "Add Server"
  3. Choose your email provider (Gmail, Outlook, etc.)
  4. Enter your email and app password
  5. Choose a sync frequency (default: Hourly โ€” options range from every 15 minutes to daily)
  6. Set allowed file extensions (default: .pdf, .jpg, .jpeg, .png)

2. Email Provider Settings

Gmail

  • Host: imap.gmail.com
  • Port: 993 (SSL)
  • Note: You need to use an App Password, not your regular password
  • Setup: Go to Google Account โ†’ Security โ†’ 2-Step Verification โ†’ App Passwords

Outlook/Hotmail

  • Host: outlook.office365.com
  • Port: 993 (SSL)
  • Note: You may need to enable IMAP in Outlook settings

Apple iCloud

  • Host: imap.mail.me.com
  • Port: 993 (SSL)
  • Note: You need to use an App-Specific Password

Other Providers

  • Choose "Custom IMAP" and enter your provider's IMAP settings

โš™๏ธ **How It Works**

Automatic Sync

  • Cron Job: A heartbeat fires every 5 minutes, but each server is only synced once its per-server Sync frequency (set in the UI, 15 min โ€“ daily) has elapsed (the app throttles; a manual "Sync Now" bypasses it). The 5-minute heartbeat is the resolution floor โ€” the per-mailbox frequency is the real cadence.
  • File Processing: Only downloads attachments with allowed extensions
  • Duplication Prevention: Tracks highest processed IMAP UID per server; mail is fetched read-only and never marked as read
  • Status Updates: Updates server status and last sync time

Manual Sync

  • "Sync Now" Button: Trigger immediate sync from the UI
  • API Endpoint: POST /api/email/sync for programmatic access
  • Independent Script: npm run email:sync can be run manually

๐Ÿณ **Docker Setup**

Scheduled tasks (including email sync) run in a shared cron container:

yaml
# docker-compose.yml
cron:
  image: ghcr.io/vas3k/taxhacker:latest
  volumes:
    - ./data:/app/data
    - ./etc/crontab:/mnt/crontab:ro
  environment:
    - DATABASE_URL=postgresql://...
    # Optional override. If unset, the container reads the persisted secret from ./data/.better_auth_secret.
    - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-}
  command: ["docker-cron-entrypoint.sh"]

Cron Configuration

File: etc/crontab

bash
# Cron jobs source /etc/cron.env (written at container startup).
*/5 * * * * set -a; . /etc/cron.env; cd /app && npm run email:sync >> /var/log/cron.log 2>&1

Env propagation: cron strips the environment, so the container's startup script dumps the
needed vars to /etc/cron.env and each job sources it. In self-hosted Docker, if
BETTER_AUTH_SECRET is unset, TaxHacker generates and persists one in ./data/.better_auth_secret
so the app and cron containers still share the same key.

๐Ÿ“Š **Data Storage**

Email Servers

  • Stored in appData table with app = 'email'
  • Each user can have multiple email servers
  • Settings include sync interval, file extensions, credentials

Downloaded Files

  • Saved to UPLOAD_PATH directory
  • Created as File records in database
  • Metadata includes email details (subject, sender, date)
  • Source marked as 'email' for tracking

Sync Status

  • lastSyncedAt: When server was last checked
  • lastProcessedUid: Highest IMAP UID processed per server (prevents duplicates; mail is never marked as read)
  • status: connected, error, pending, paused

๐Ÿ”ง **Commands**

bash
# Manual sync (run once)
npm run email:sync

# View logs
docker logs taxhacker_cron

# Check installed crontab
docker exec taxhacker_cron crontab -l

๐Ÿšจ **Troubleshooting**

Authentication Issues

  • Gmail: Make sure you're using an App Password, not your regular password
  • Outlook: Enable IMAP access in settings
  • 2FA: Most providers require app-specific passwords when 2FA is enabled

Connection Issues

  • Check firewall settings for IMAP ports (usually 993 or 143)
  • Verify server settings match your provider's documentation
  • Test connection using "Test Connection" button

No Emails Found

  • Check if the sync frequency has elapsed since the last sync
  • Verify the mailbox has new messages (above the last processed UID) carrying attachments โ€” fetch is read-only and does not depend on unread status
  • Check allowed file extensions match your attachments
  • Review logs: docker logs taxhacker_cron

Performance

  • Default sync frequency is Hourly โ€” pick a shorter one (down to every 15 min) in the UI if needed
  • Large attachments may take time to download
  • Monitor storage usage for attachment files

๐Ÿ“ **Logs**

Email sync logs are available:

  • Container logs: docker logs taxhacker_cron
  • Cron log: /var/log/cron.log inside container
  • Manual sync: Output shown in terminal when running npm run email:sync

๐Ÿ”’ **Security**

  • Passwords: Stored encrypted at rest (AES-256-GCM), with the key derived from the BETTER_AUTH_SECRET environment variable. Decrypted only at sync/connection-test time.
    • โš ๏ธ Rotating BETTER_AUTH_SECRET invalidates stored mailbox passwords. After changing it, re-enter each server's password (the server shows a decryption error until you do).
  • IMAP SSL: Enabled by default for all preset providers
  • Access Control: Each user can only access their own email servers
  • App Passwords: Recommended for all providers supporting them

Migrate 0.3 0.5 (`docs/migrate-0.3-0.5.md`)

How to migrate data from v0.3 to v0.5

In v0.5 we changed the database from SQLite to Postgres. Because of this, it was not possible to seamlessly migrate data from one database to another and you will have to do it yourself.

Don't worry, even if you already upgraded โ€” your data is not lost!

Here's how to migrate properly:

Step 1: Update your docker-compose to v0.3.0

yaml
services:
  app:
    image: ghcr.io/vas3k/taxhacker:v0.3.0
    ports:
      - "7331:7331"

// everything else stays the same

Step 2: Restart your app and make a backup

yaml
docker compose down
docker compose up -d

Go to your app -> Settings -> Backups -> Download Data Archive

Save .zip archive on your machine.

Step 3: Upgrade your TaxHacker instance

Update your docker compose to latest version again.

yaml
services:
  app:
    image: ghcr.io/vas3k/taxhacker:latest
    ports:
      - "7331:7331"

// everything else stays the same

Restart again.

Step 4: Upload your data to the new instance

Open your app -> Settings -> Backups -> Restore from a backup

Upload your zip archive and click restore. After couple of seconds it must show you import stats.

If import fails with an error about file size, go to next.config.ts and change bodySizeLimit: "256mb" to something bigger.


Self Hosted Public Access (`docs/self-hosted-public-access.md`)

Self-Hosted Public Access

โš ๏ธ Warning: This is a workaround ("hack") for exposing self-hosted instances to the internet.
This approach has security implications and should be used with caution. For production deployments, consider implementing proper authentication with reverse proxy or using the official cloud version.

Overview

By default, TaxHacker's self-hosted mode (SELF_HOSTED_MODE=true) disables the built-in authentication system, assuming the instance runs on a trusted local network or behind a reverse proxy with its own authentication.

However, if you want to expose your self-hosted instance to the internet and still use TaxHacker's built-in login system, you can use this workaround.

How It Works

The approach involves:

  1. Running TaxHacker with SELF_HOSTED_MODE=false (cloud mode)
  2. Manually creating a user via direct database access
  3. Retrieving the OTP (one-time password) from the database
  4. Using that OTP to sign in

Setup Instructions

1. Configure Environment Variables

Run with these environment variables:

env
SELF_HOSTED_MODE=false
DISABLE_SIGNUP=true
BASE_URL=<URL you'll use to access TaxHacker>

2. Create a User Directly in the Database

Create your user directly in the database (replace email as needed):

bash
docker exec -it postgres psql -U postgres -d taxhacker -c "INSERT INTO users (id, email, name, membership_plan, is_email_verified, updated_at) VALUES ('6f5b4f8e-6f7a-4c3d-9b8b-7f2d2d61a9c3','[email protected]','Owner','unlimited', true, now());"

3. Sign In with OTP from Database

Open your instance in the browser, click Sign In, and enter your email address.

Fetch the latest OTP from the database and use it to sign in:

bash
docker exec -it postgres psql -U postgres -d taxhacker -c "SELECT value FROM verification ORDER BY created_at DESC LIMIT 1;"

4. Seed the Database

Because we skipped the normal route, database seeding won't happen automatically. Open <YOUR_URL>/settings/backups and click [Reset fields, currencies and categories]


--- METRICS ---