ExpenseTrace is a privacy-focused expense tracking tool that helps you manage your finances without sharing your banking details with third-party services. Built in Go, it provides a simple yet powerful way to track your expenses, categorize them, and generate insightful reports.
In an era where financial data privacy is increasingly important, ExpenseTrace offers a secure alternative to traditional expense tracking apps. Instead of connecting to your bank accounts or sharing sensitive financial information, ExpenseTrace allows you to:
- Import your expenses from CSV or JSON files through the web interface
- Automatically categorize transactions based on customizable regex patterns
- Generate detailed reports and insights
- Access your data through a user-friendly web interface
- Keep all your financial data local and private
- Multi-user support with authentication
- 🌐 Web interface for visual data exploration with import functionality
- 📈 Detailed financial reports and insights
- 🔒 Local data storage with SQLite
- 👥 Multi-user support with authentication
- 📝 Import expenses via web interface (CSV, JSON) with automatic or interactive mapping
- 🏷️ Automatic expense categorization using regex patterns
ExpenseTrace is designed with privacy in mind:
- All data is stored locally in a SQLite database
- No external API calls or data sharing
- No bank account connections required
- Full control over your financial data
If you need to allow cross-origin requests from specific domains (e.g., when running ExpenseTrace behind a reverse proxy or accessing it from multiple domains), you can configure trusted origins using the EXPENSETRACE_TRUSTED_ORIGINS environment variable:
services:
expensetrace:
image: gustavocaso/expensetrace:latest
environment:
EXPENSETRACE_TRUSTED_ORIGINS: "https://example.com,https://app.example.com"Note: Only add trusted origins if you control those domains. Adding untrusted origins could expose your application to CSRF attacks.
- X-Frame-Options: Set to
DENYby default to prevent clickjacking attacks. Can be disabled by settingEXPENSETRACE_ALLOW_EMBEDDING=trueif you need to embed the interface in an iframe.
- Clone the repository:
git clone https://github.com/GustavoCaso/expensetrace.git
cd expensetrace- Build the project:
CGO_ENABLED=1 go build- Start the web server:
./expensetraceExpenseTrace can be run using Docker. The simplest way is to use Docker Compose:
- Create a
docker-compose.ymlfile:
services:
expensetrace:
image: gustavocaso/expensetrace:latest
environment:
EXPENSETRACE_DB: /data/expenses.db # Path to the SQLite database file inside the container
EXPENSETRACE_PORT: 8081 # Port the application will listen on inside the container
EXPENSETRACE_LOG_LEVEL: info # Log level: debug, info, warn, error
EXPENSETRACE_LOG_FORMAT: text # Log format: text or json
EXPENSETRACE_LOG_OUTPUT: stdout # Log output: stdout, stderr, or file path
ports:
- "8082:8081" # Maps container port 8081 to host port 8082
volumes:
- ./:/data # Mounts the current directory to /data in the container.- Start the service:
docker compose upThe service will be available at http://localhost:8082. The configuration file and database will be persisted on the host machine.
ExpenseTrace can be configured entirely through environment variables. This makes it ideal for containerized deployments.
EXPENSETRACE_DB: Path to SQLite database file (default:expensetrace.db)
EXPENSETRACE_PORT: Web server port (default:8080)EXPENSETRACE_TIMEOUT: Server timeout duration (default:5s)EXPENSETRACE_ALLOW_EMBEDDING: Allow iframe embedding - set totrueto enable (default:false)
EXPENSETRACE_TRUSTED_ORIGINS: Comma-separated list of trusted origins for CSRF protection (e.g.,https://example.com,https://app.example.com). Only needed if accessing the application from multiple domains.EXPENSETRACE_SECURE_COOKIES: Set totrueto mark session cookies asSecure(HTTPS-only). Enable this when running behind HTTPS. Leave unset orfalsefor plain HTTP deployments (e.g., local network or Raspberry Pi). (default:false)
EXPENSETRACE_LOG_LEVEL: Log level -debug,info,warn,error(default:info)EXPENSETRACE_LOG_FORMAT: Log format -textorjson(default:text)EXPENSETRACE_LOG_OUTPUT: Log output -stdout,stderr, or file path (default:stdout)
ExpenseTrace provides a flexible import system through the web interface that supports both automatic and interactive workflows:
For supported banking providers, ExpenseTrace automatically handles field mapping. Simply name your CSV file with the provider prefix:
- EVO:
evo_transactions.csv - Revolut:
revolut_transactions.csv - Bankinter:
bankinter_transactions.csv
The system will automatically detect the provider and parse the CSV correctly.
Uploading a json file that containts an array with objects containing source, date, description, amount, and currency fields
Example JSON format:
[
{
"source": "MyBank",
"date": "2024-01-15T10:30:00Z",
"description": "grocery store",
"amount": -5000,
"currency": "EUR"
}
]For custom CSV or JSON files, ExpenseTrace provides an interactive 3-step import process:
- Upload & Preview: Upload your file and see a preview of the data
- Field Mapping: Map your file's columns to expense fields:
- Date column
- Description column
- Amount column
- Currency column
- Source (custom name for your data source)
- Review & Confirm: Preview the parsed expenses and confirm the import
ExpenseTrace uses regular expressions (regex) to automatically categorize your expenses based on transaction descriptions. Here's how to effectively use pattern matching:
- "Groceries" -> "supermarket|grocery|food market" # Matches any of these terms
- "Transportation" -> "uber|taxi|metro|bus|train" # Matches various transport services
- "Entertainment" -> "netflix|spotify|cinema|theater" # Matches entertainment services
- "Utilities" -> "electricity|water|gas|internet" # Matches utility billsPattern matching tips:
- Use
|(pipe) to match multiple patterns:"pattern1|pattern2" - Use
.*for wildcard matching:"amazon.*"matches anyamazontransaction - Use
^for start of string:"^starbucks"matches only ifstarbucksis at the start - Use
$for end of string:"subscription$"matches only ifsubscriptionis at the end - Use
\dfor digits:"payment-\d+"matchespayement-followed by any number
Note: Transaction descriptions are automatically converted to lowercase before matching against patterns. Therefore, patterns should be written in lowercase to match correctly.
Example with complex patterns:
- "Online Shopping" -> "amazon.*|ebay.*|walmart.*" # Matches any transaction from these retailers
- "Dining Out" -> "^restaurant|^cafe|^bar|^pizza" # Matches only if these terms appear at the start
- "Subscriptions" -> ".*subscription$|.*membership$" # Matches if these terms appear at the end
- "Healthcare" -> "pharmacy|doctor|hospital|medical" # Matches healthcare-related expensesContributions are welcome! Please feel free to submit a Pull Request.
When working on the web interface you can use make run_web to run the web server. This set the ENV variable EXPENSE_LIVERELOAD=true. That ensures changes to files in the router/templates folder are pickup when reloading the browser.
Making changes to the HTTP hanlder code, requires restarting the server.
This project is licensed under the MIT License - see the LICENSE file for details.
