AI-Powered Expense Tracker — Go + HTMX + SQLite
Find a file
cclohmar cb42efa395 fix: database ownership preserved during backup — app user must be able to write
- Backup now uses chown --reference to preserve original owner
- Found: rsync changed database ownership to root, blocking writes
2026-06-03 07:52:23 +00:00
contrib fix: resolve 7 critical security findings from code review 2026-05-31 01:50:08 +00:00
internal feat: single OTP field, event metadata on page, delete event 2026-06-03 07:46:12 +00:00
static feat: send receipt images as ZIP attachment with report 2026-05-30 13:38:23 +00:00
templates feat: single OTP field, event metadata on page, delete event 2026-06-03 07:46:12 +00:00
.env.example fix: remove orphaned fallthrough after ollama removal 2026-05-30 17:40:40 +00:00
.gitignore chore: comprehensive README with binary distribution docs + build-all.sh 2026-05-29 19:54:20 +00:00
build-all.sh chore: rename binary from receiptnext to app 2026-05-30 15:53:24 +00:00
go.mod chore: switch from mattn/go-sqlite3 (CGO) to modernc.org/sqlite (pure Go) 2026-05-30 15:02:07 +00:00
go.sum chore: switch from mattn/go-sqlite3 (CGO) to modernc.org/sqlite (pure Go) 2026-05-30 15:02:07 +00:00
install.sh fix: database ownership preserved during backup — app user must be able to write 2026-06-03 07:52:23 +00:00
main.go feat: single OTP field, event metadata on page, delete event 2026-06-03 07:46:12 +00:00
Makefile chore: rename binary from receiptnext to app 2026-05-30 15:53:24 +00:00
README.md docs: update README — binary name, update process, config table, structure 2026-06-01 23:21:21 +00:00

ReceiptNext — AI-Powered Expense Tracker

A production-ready, mobile-first Progressive Web App (PWA) for expense management with passwordless OTP login, AI receipt extraction (Gemini / OpenAI-compatible), and CSV/PDF email reporting with receipt images.

Tech Stack: Go 1.23+ · HTMX · SQLite (pure Go) · Google Gemini / OpenAI · PWA


🚀 One-Command Install

bash -c "$(curl -fsSL https://git.lohmar.co.uk/cclohmar/ReceiptNext/raw/branch/main/install.sh)"

Or from a local clone:

./install.sh

The installer will:

  1. Clone the repo to /opt/receiptnext/

  2. Build the binary (pure Go, no CGO, no dependencies)

  3. Ask which AI provider to use:

    1) Google Gemini  (cloud API, needs API key)
    2) OpenAI / Compatible  (OpenAI, Perplexity, Groq, etc.)
    
  4. Prompt for SMTP settings (for OTP emails and report delivery)

  5. Create /opt/receiptnext/.env with all configuration

  6. Set up a systemd service that auto-starts on boot

  7. Start ReceiptNext

Result: A fully configured, always-running expense tracker at http://YOUR_SERVER:8080.


🔄 Updating

bash /opt/receiptnext/install.sh -update

Update pulls the latest code, copies updated templates/static, rebuilds the binary, automatically backs up the database, and restarts the service.


📦 Binary Download (manual)

Pre-compiled static binaries are on the Releases page:

# Linux amd64
curl -L -o app https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/v1.0.0/app-linux-amd64
chmod +x app
./app

# Linux arm64 (Raspberry Pi, etc.)
curl -L -o app https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/v1.0.0/app-linux-arm64
chmod +x app
./app

No dependencies, no CGO, no libc. Drop it on any Linux box and run.


🔧 Building from Source

Prerequisites

  • Go 1.23+Download
  • No GCC, no CGO, no cross-compilers needed

Build

git clone https://git.lohmar.co.uk/cclohmar/ReceiptNext.git
cd ReceiptNext

# Build for your current platform
CGO_ENABLED=0 go build -o app .

# Cross-compile for any platform (no extra tools needed!)
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o app-linux-arm64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o app-darwin-amd64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o app-darwin-arm64 .

The binary is fully static — zero runtime dependencies.


⚙️ Configuration

Configuration is via environment variables in .env. The install script builds this for you interactively.

Variable Required Default Description
PORT No 8080 HTTP server port
BASE_URL No http://localhost:8080 Public URL for email links
AI Provider
AI_PROVIDER No gemini gemini or openai
GEMINI_API_KEY For Gemini Google Gemini API key
OPENAI_API_KEY For OpenAI API key (omit for Ollama via OpenAI compat)
AI_MODEL For OpenAI gpt-4o-mini Model name
AI_BASE_URL For OpenAI https://api.openai.com/v1 API endpoint
SMTP
SMTP_HOST See note SMTP server hostname
SMTP_PORT See note 587 SMTP server port
SMTP_USER See note SMTP username
SMTP_PASS See note SMTP password

Note: SMTP is optional — without it, OTP codes are logged to the server console for testing and reports cannot be emailed.


🚀 Usage

1. Start the Server

# If installed via systemd:
systemctl start receiptnext

# Or run directly:
./app

2. Open in Browser

Navigate to http://YOUR_SERVER:8080

3. Full Acceptance Flow

1. Enter your email → click "Send Verification Code"
2. Check your inbox (or server log) for the 6-digit OTP
3. Enter OTP → click "Verify Code"
4. Click "+ New" → name your event → set claim currency → enter a conversion sample
5. Click "Open" on the event
6. Tap "📷 Camera" or "📁 Upload" to add a receipt
7. AI extracts amount, merchant, category, date → form is pre-filled
8. Click "Save Expense"
9. Click "Submit Event" → enter recipient email → choose CSV or PDF
10. Report + receipt images ZIP are emailed → event auto-closes
11. Click "Reopen" to add more receipts

📡 API Reference

Public

Method Path Description
GET / Landing page
POST /request-otp Request OTP code
POST /verify-otp Verify OTP and create session
POST /logout Clear session

Protected (requires session)

Method Path Description
GET /dashboard Event list
POST /events Create event
GET /events/{id}/expenses View event + expenses
PUT /events/{id}/reopen Reopen closed event
POST /expenses/upload Upload receipt image/PDF
POST /expenses Save expense
GET /expenses/{id}/edit Get edit form
PUT /expenses/{id} Update expense
POST /events/{id}/file File report (CSV/PDF + images ZIP)

🏗️ Project Structure

/opt/receiptnext/
├── app                      # Compiled binary
├── .env                     # Configuration (optional)
├── backups/                 # Automatic DB backups (from -update)
├── templates/               # Go HTML templates
├── static/                  # CSS, icons, favicon, service worker
├── templates/               # Go HTML templates
├── static/                  # CSS, icons, favicon, service worker
│   ├── css/style.css
│   ├── favicon.svg          # Rx logo
│   ├── manifest.json
│   ├── sw.js
│   └── icons/
├── storage/                 # Uploaded receipt images (runtime)
├── install.sh               # Installer script
├── Makefile                 # Build targets
└── contrib/
    └── receiptnext.service  # Systemd service file

🧩 Features

🔐 Passwordless OTP Authentication

  • Email-based 6-digit code, 5-minute expiry
  • 3 failed attempts → 1-minute cooldown
  • HTTP-only session cookie, 24h TTL

🤖 AI Receipt Extraction

  • Google Gemini (default) — cloud vision API
  • OpenAI-compatible — works with OpenAI, Perplexity, Groq, Together AI, etc.
  • Supports: JPEG, PNG, WebP, HEIC, PDF (email receipts from Uber, etc.)

💱 Currency Conversion

  • Sample-based: enter a real receipt amount and what you were charged
  • System computes the rate automatically
  • Each expense stores original + converted amount

📧 Email Reporting

  • CSV or PDF report
  • Receipt images bundled as ZIP (expense-{event}-images.zip)
  • Filenames: expense-{event}-report.csv, expense-{event}-report.pdf
  • Item numbers match between report rows and ZIP images

📱 Progressive Web App

  • Installable on mobile home screen
  • Camera capture + gallery upload
  • Dark "Terminal Mint" theme (#0F172A base)
  • Rx favicon in emerald green

🗄️ Database

SQLite, auto-created on first run — 4 tables: users, auth_otps, events, expenses.


🔒 Security

Area Implementation
Sessions crypto/rand tokens, in-memory, 24h TTL
OTP crypto/rand codes, 5min expiry, lockout after 3 failures
Cookies HTTP-only, SameSite=Lax, path-restricted
SQL Parameterized queries everywhere
Uploads Magic byte validation, max 10MB, UUID filenames
Config .env is optional — app runs with defaults if absent

Built with Go, HTMX, SQLite and ❤️