Skip to main content
MinTax - Enterprise Tax & Expense Analysis Platform

MinTax - Enterprise Tax & Expense Analysis Platform

Solo-built offline-first desktop application for UK tax professionals featuring real-time analysis of 64-column expense datasets, AES-256-GCM encrypted data, and WebSocket streaming. Serving 500+ monthly users and generating $180K+ annual revenue.

June 1, 2024 - December 1, 2025
16 min read
Solo Full-Stack Developer
FinTech Β· UK Tax Compliance
Desktop (Windows / macOS)
Solo (I owned everything)
Electron.js Python FastAPI Pandas Multi-threading SQLite React Svelte TypeScript WebSocket Encryption

Overview

I was the sole developer on MinTax, a production-grade desktop application used by UK tax professionals and accountants to perform complex tax calculations, analyze expenses, and generate HMRC-compliant reports. Every line of code, every architecture decision, every deployment pipeline was mine.

The core challenge: take messy Excel files with 64+ expense columns and thousands of rows, run real-time tax categorization across P11D, PSA, and PAYE frameworks, and deliver results in under 2 seconds, all while keeping sensitive financial data encrypted and the entire system running 100% offline.

500+
Active Monthly Users
$180K+
Annual Revenue Generated
<2s
Complex Calculation Time
99.9%
Calculation Accuracy
Manual Process
~4 hours per client
99.98% faster
With MinTax
Under 2 seconds
Tax professionals previously reconciled 64-column expense spreadsheets manually in Excel. MinTax automates the entire categorization, calculation, and reporting pipeline.

What I Owned (Solo Developer Scope)

As the only engineer on this project, I was responsible for every layer of the stack, from pixel to deployment:

Architecture & Design
Designed the full multi-layer system: Electron shell, SvelteKit frontend, Python analysis engine, encrypted SQLite data layer
Frontend Development
Built every UI component in Svelte 5 β€” data tables, dashboards, multi-tab navigation, report viewers
Backend & Analysis Engine
Developed the Python FastAPI service with Pandas pipelines, multi-threading, and WebSocket streaming
Security Engineering
Implemented end-to-end AES-256-GCM encryption across three platforms (Python, Node.js, Browser)
Database Design
Designed normalized schema with field-level encryption, wrote all Knex.js migrations, built the repository layer
DevOps & Distribution
Set up GitHub Actions CI/CD, electron-builder packaging, auto-update system, and Nuitka Python compilation

Why I Chose This Stack

Every technology choice was driven by the specific constraints of this project: offline-first, security-critical, data-heavy.

Svelte 5 (Runes)

With 64+ columns streaming via WebSocket, I needed fine-grained reactivity without virtual DOM overhead. Svelte 5's $state and $derived runes give me surgical updates: only the cells that change re-render, not the entire table.

Evaluated: React, Vue 3, Solid.js

Python + FastAPI

Pandas is unmatched for tabular data manipulation at this scale. FastAPI gave me async endpoints and native WebSocket support with minimal boilerplate, critical for streaming analysis results in real-time.

Evaluated: Node.js, Rust, Go

Electron + WebContentsView

The client needed cross-platform desktop deployment with offline capability. WebContentsView (not BrowserView) provides true process isolation per tab, essential for preventing data leakage between client accounts.

Evaluated: Tauri, CEF, PWA

SQLite + Field-Level AES

An embedded database removes server dependencies for offline-first architecture. Field-level encryption (not full-disk) means I can query non-sensitive columns at full speed while keeping financial data encrypted at rest.

Evaluated: PostgreSQL, IndexedDB, LevelDB

Technical Architecture

The application employs a multi-layer architecture with strict security boundaries between each layer:


Technology Stack

Frontend

Svelte 5 β€” Runes-based reactivity ($state, $derived, $effect)
SvelteKit β€” File-based routing and SSR capabilities
TypeScript β€” Full type coverage across the codebase
Tailwind CSS β€” Utility-first styling with custom design system
Vite β€” Lightning-fast HMR and optimized builds

Desktop Platform

Electron 33 β€” Latest version with security patches
WebContentsView β€” Browser-like multi-tab system with process isolation
Context Isolation β€” Secure renderer-main separation
electron-builder β€” Cross-platform packaging (Windows/macOS)

Backend API

Python FastAPI β€” High-performance async API framework
Pandas β€” Data manipulation and analysis
NumPy β€” Mathematical operations optimization
OpenPyXL β€” Excel file processing
WebSockets β€” Real-time bidirectional streaming

Database

SQLite β€” Embedded relational database
Knex.js β€” SQL query builder and migrations
Field-level Encryption β€” AES encryption for sensitive columns

Security

AES-256-GCM β€” Authenticated encryption with associated data
PBKDF2 β€” 100,000 iteration key derivation
Session Keys β€” Per-launch encryption key generation
electron-store β€” Encrypted local configuration

DevOps & Testing

GitHub Actions β€” CI/CD pipeline automation
Playwright β€” End-to-end testing framework
Auto-updates β€” Seamless version distribution
Nuitka β€” Python to executable compilation

Key Features & Capabilities

Multi-Tab Navigation System

Implemented a browser-like tabbing system using Electron’s WebContentsView API. Each tab operates as an isolated renderer process with its own lifecycle, enabling users to work across multiple companies and reports simultaneously without data cross-contamination.

MinTax multi-tab navigation system built with Electron WebContentsView, showing dynamic tab creation, switching between company workspaces, and tab state persistence
Multi-tab system in action: creating new tabs, switching between client workspaces, and dragging tabs. Each tab runs in an isolated renderer process and receives live WebSocket updates.
  • Custom tab management with dynamic creation, destruction, and state persistence
  • Secure context isolation between tabs preventing cross-tab data leakage
  • Full back/forward navigation history per tab with URL-based state preservation
  • Graceful tab recovery and memory management preventing leaks

Real-Time Analysis Engine

Developed a Python-based expense analysis engine that processes complex Excel files (64+ columns) with real-time streaming updates. The engine categorizes expenses into taxable, non-taxable, and excluded categories while identifying review points requiring human attention.

  • WebSocket-based real-time progress streaming with batch data updates
  • Intelligent word-matching algorithms for automated expense categorization
  • Progressive result streaming enabling live UI updates during analysis
  • Multi-threaded processing for parallel computation on large datasets
  • Background workers ensuring non-blocking UI during calculations

Enterprise-Grade Security Architecture

Why Security Was a Business Feature

This wasn’t just a technical checkbox. The client marketed MinTax to high-tier UK accounting firms. HMRC compliance required that sensitive financial data (employee names, PAYE references, salary details) be encrypted at rest and in transit. Without this security posture, the app couldn’t be sold to enterprise clients. The encryption layer directly enabled the $180K+ revenue stream.

Implemented comprehensive security measures including end-to-end encryption for all API communications, field-level database encryption for sensitive data, and secure session key management.

  • AES-256-GCM encryption with PBKDF2 key derivation (100,000 iterations)
  • Session-based encryption keys regenerated per application launch
  • Cross-platform WebCrypto implementation matching browser and Node.js APIs
  • Encrypted electron-store for secure local configuration storage
  • License verification system with hardware fingerprinting

Advanced Data Table with Dynamic Filtering

Built a high-performance, Excel-like data table component handling thousands of expense records with automatic type detection, dynamic filter generation, and real-time updates from streaming WebSocket data.

MinTax ExpensesDataTable component built with Svelte 5, showing 64+ column data table with dynamic column filtering, type-aware filter controls, and real-time WebSocket data updates
The ExpensesDataTable: 64+ columns with auto-detected types, smart filtering (range sliders for currency, dropdowns for categories), and live WebSocket updates. Making this data density usable was one of the biggest UX challenges.
  • Automatic column type detection (currency, dates, booleans, categories)
  • Smart filter generation: range sliders for numbers, dropdowns for categories
  • Configurable column visibility with 64+ available data columns
  • Lazy loading and pagination for optimal performance with large datasets
  • Caching layer for frequently accessed calculation results

Professional Report Generation

Comprehensive reporting system generating client-ready documents for tax compliance and analysis review.

  • Multiple export formats: PDF, Excel (XLSX), and CSV
  • Detailed expense breakdowns by category, entity, and tax year
  • Review point summaries highlighting items requiring attention
  • Tax position reports for P11D, PSA, and PAYE compliance

Technical Challenges & Solutions

Challenge 1: Cross-Platform Encryption Compatibility

The Challenge

The Python backend initially used Fernet encryption (AES-CBC) while the browser frontend used WebCrypto’s AES-GCM. These incompatible encryption methods caused decryption failures and 400 errors across the entire communication layer.

The Solution

Implemented a unified AES-256-GCM encryption layer across all three platforms (Python, Node.js/Electron, Browser) using identical PBKDF2 parameters: same hashing algorithm (SHA-256), same iteration count (100,000), matching salt values. Created a custom EncryptionManager class in Python using cryptography.hazmat primitives, and leveraged WebCrypto’s subtle API for consistent encrypt/decrypt operations on the frontend.

# Python AES-GCM Encryption (matching browser WebCrypto)
class EncryptionManager:
    def __init__(self, encryption_key: str):
        self.salt = b'electron_api_salt_2024'
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,  # 256 bits
            salt=self.salt,
            iterations=100000,
        )
        derived_key = kdf.derive(encryption_key.encode('utf-8'))
        self.aesgcm = AESGCM(derived_key)
 
    def encrypt_data(self, data: dict) -> str:
        nonce = os.urandom(12)
        json_bytes = json.dumps(data).encode('utf-8')
        ciphertext = self.aesgcm.encrypt(nonce, json_bytes, None)
        return base64.b64encode(nonce + ciphertext).decode('utf-8')
// TypeScript AES-GCM Decryption (matching Python EncryptionManager)
class CryptoService {
  private key: CryptoKey;
 
  async deriveKey(password: string): Promise<CryptoKey> {
    const salt = new TextEncoder().encode('electron_api_salt_2024');
    const keyMaterial = await crypto.subtle.importKey(
      'raw', new TextEncoder().encode(password), 'PBKDF2', false, ['deriveKey']
    );
    return crypto.subtle.deriveKey(
      { name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
      keyMaterial,
      { name: 'AES-GCM', length: 256 },
      false, ['decrypt']
    );
  }
 
  async decrypt(encrypted: string): Promise<object> {
    const data = Uint8Array.from(atob(encrypted), c => c.charCodeAt(0));
    const nonce = data.slice(0, 12);      // First 12 bytes = nonce
    const ciphertext = data.slice(12);     // Rest = ciphertext + tag
    const decrypted = await crypto.subtle.decrypt(
      { name: 'AES-GCM', iv: nonce }, this.key, ciphertext
    );
    return JSON.parse(new TextDecoder().decode(decrypted));
  }
}

Challenge 2: Encrypted Database Migrations

The Challenge

Traditional SQL migrations couldn’t modify encrypted data. When consolidating duplicate payroll element records (a schema change), the encrypted data couldn’t be read or modified directly via SQL. Users with existing data would lose their configurations on upgrade.

The Solution

Developed a two-tier migration strategy: schema-level migrations via Knex.js for structural changes, and application-level data migrations that run through the SecureDbService repository layer. The data migrations read encrypted records, decrypt them, perform consolidation logic, and re-encrypt, all during application startup with comprehensive logging and rollback capabilities.

// Step 1: Schema-level migration: add the consolidated column
exports.up = function(knex) {
  return knex.schema.alterTable('payroll_elements', (table) => {
    table.text('merged_custom_types');  // New column for consolidated data
    table.boolean('is_consolidated').defaultTo(false);
  });
};
// Step 2: Application-level migration: runs through encrypted repo layer
async function consolidatePayrollElements(companyId: string) {
  // Auto-decrypts on read via SecureDbService
  const elements = await payrollRepo.getByCompanyId(companyId);
  
  const consolidated = groupByCategory(elements);
  
  for (const [category, items] of Object.entries(consolidated)) {
    const mergedTypes = items.flatMap(i => i.custom_types);
    // Auto-encrypts on write via SecureDbService
    await payrollRepo.update(items[0].id, { 
      custom_types: mergedTypes,
      is_consolidated: true 
    });
    
    // Delete duplicates β€” encrypted data handled transparently
    for (const item of items.slice(1)) {
      await payrollRepo.delete(item.id);
    }
  }
}

Challenge 3: Real-Time WebSocket Streaming with Encryption

The Challenge

Streaming analysis results required maintaining encryption while processing thousands of rows. Excel formula strings (like =A1*B1) were appearing in output instead of calculated values, and JSON serialization was failing for NumPy data types.

The Solution

Implemented an analysis stream callback architecture: each batch is encrypted before transmission over WebSocket, with formula detection and recalculation logic for affected columns. Created custom JSON serializers handling NumPy types, NaN values, and pandas Timestamps. The WebSocket client auto-reconnects and maintains encryption state across connection drops.

# Streaming encrypted analysis results over WebSocket
async def stream_analysis_batch(websocket, batch_data, encryption_manager):
    # Recalculate formula columns β€” Excel formulas don't transfer as values
    if "Taxable Amount (Β£)" in batch_data.columns:
        batch_data["Taxable Amount (Β£)"] = (
            df["Expense Amount (GBP)"].values * df["%"].values
        )
    
    # Handle NumPy/Pandas types that break json.dumps
    clean_data = batch_data.replace({np.nan: None}).to_dict(orient="records")
    
    # Encrypt batch and stream β€” client decrypts with matching AES-GCM key
    encrypted = encryption_manager.encrypt_data(clean_data)
    await websocket.send_json({"encrypted": encrypted, "progress": batch_index / total})

Challenge 4: Multi-Tab UI Performance with Heavy Components

The Challenge

Multiple tabs with heavy data tables (ExpensesDataTable with 64+ columns) were all mounting simultaneously, causing memory bloat and sluggish performance. Hidden tabs still consumed resources and reacted to state changes.

The Solution

Implemented dynamic component mounting using Svelte 5’s reactivity system: only the active tab’s component is mounted at any time. Switching tabs destroys the previous component and mounts the new one, with state preservation using URL parameters and parent component state.

<!-- Dynamic component mounting: only active tab exists in DOM -->
<script lang="ts">
  let activeTab = $state('expenses');
  
  // Components lazy-load and mount only when their tab is active
  // Previous tab is fully destroyed, freeing memory
  const tabComponents = {
    expenses: () => import('./ExpensesDataTable.svelte'),
    summaries: () => import('./SummariesTab.svelte'),
    review: () => import('./ReviewPointsTab.svelte'),
  };
</script>
 
{#key activeTab}
  {#await tabComponents[activeTab]() then module}
    <svelte:component this={module.default} {data} />
  {/await}
{/key}

Database Architecture

The application uses a normalized relational database design with field-level encryption for sensitive data. The SecureDbService layer automatically encrypts/decrypts designated fields transparently to the application logic.

Core Entities

EntityPurposeEncrypted Fields
CompaniesClient organizationsname, industry
EntitiesEmploying entities with PAYE referencesname, paye_reference, office_locations
ReportsAnalysis reports with tax year dataname, expenses_data, expenses_data_result
PSA CategoriesPAYE Settlement Agreement categoriesname
Payroll ElementsP11D and Payroll benefit typestype, custom_types
Annual EventsCompany events for categorizationname, custom_name, descriptions
Analysis PositionsTax positions for specific expense typeslate_night_taxis, working_lunches, incidental_expenses
Long Service AwardsService award configurationsserviceAwardType
Client GiftsGift reporting configurationsβ€”
Incidental ExpensesExpense reimbursement settingsβ€”

Entity Relationships

Companies (1) ─────┬───── (*) Entities
                   β”œβ”€β”€β”€β”€β”€ (*) Reports ────── (*) Entity_Reports (junction)
                   β”œβ”€β”€β”€β”€β”€ (*) PSA_Categories
                   β”œβ”€β”€β”€β”€β”€ (*) Payroll_Elements
                   β”œβ”€β”€β”€β”€β”€ (*) Annual_Events
                   β”œβ”€β”€β”€β”€β”€ (*) Analysis_Positions
                   β”œβ”€β”€β”€β”€β”€ (*) Long_Service_Awards
                   β”œβ”€β”€β”€β”€β”€ (*) Client_Gifts
                   └───── (*) Incidental_Expenses

Performance Optimization

Background Workers

Non-blocking UI during long-running calculations using Web Workers and async processing

Caching Layer

Frequently used calculation results cached to prevent redundant processing

Lazy Loading

On-demand data and component loading for large datasets

Virtual Scrolling

Only visible rows rendered in large data tables

Batch Processing

Parallel computation for multi-file analysis operations

Connection Pooling

Efficient database connection management via Knex.js


What Users Say

We used to spend half a day reconciling expense spreadsheets for a single client. MinTax does it in seconds and catches categorization issues we'd miss manually. It's completely changed how we handle P11D season.

ST
Senior Tax Advisor
UK Accounting Firm
Paraphrased

Results & Impact

500+
Active Monthly Users

Growing user base of UK tax professionals and accountants across firms of all sizes

$180K+
Annual Revenue

Subscription-based revenue generated for the client β€” enabled by enterprise security posture

<2 seconds
Calculation Speed

Down from ~4 hours of manual Excel work per client β€” a 99.98% efficiency gain

99.9%
Accuracy Rate

Verified calculation accuracy across all tax categories (P11D, PSA, PAYE)


Project Metrics

CategoryMetric
Codebase10+ database tables with migrations
15+ Svelte components
20+ IPC handlers
64 expense data columns
Full TypeScript coverage
FeaturesMulti-tab WebContentsView system
Real-time WebSocket streaming
AES-256-GCM encryption layer
License verification system
Dynamic filtering engine
PerformanceComplex calculations in <2 seconds
Handles 10,000+ row datasets
99.9% calculation accuracy
Business Impact500+ active monthly users
$180K+ annual subscription revenue
HMRC-compliant security posture

Skills & Competencies Demonstrated

Full-Stack Development

  • Frontend: Svelte 5 with runes-based reactivity ($state, $derived, $effect)
  • Backend: Python FastAPI with async endpoints and WebSocket support
  • Desktop: Electron with process isolation and secure IPC patterns
  • Database: SQLite with Knex.js migrations and repository pattern

Security Engineering

  • Cryptographic implementation: AES-256-GCM, PBKDF2, session key management
  • Cross-platform encryption compatibility (Python ↔ Node.js ↔ Browser)
  • Secure architecture: Context isolation, sandboxed preload scripts
  • Data protection: Field-level encryption, secure local storage

Software Architecture

  • Monorepo management with npm workspaces
  • Clean separation of concerns across packages
  • Repository pattern for data access abstraction
  • Event-driven architecture for real-time updates

Data Engineering

  • Complex Excel processing with 64+ column datasets
  • Pandas data transformation and analysis pipelines
  • Real-time streaming data processing with WebSockets
  • Dynamic type detection and intelligent filtering systems

DevOps & Tooling

  • CI/CD pipelines with GitHub Actions
  • Cross-platform builds with electron-builder
  • E2E testing with Playwright
  • Auto-update distribution system

Conclusion

MinTax was a solo effort across every layer of a production-grade desktop application, from cryptographic primitives to pixel-perfect data tables. It demonstrates that I can take a complex, security-critical, data-heavy product from architecture to deployment, alone, and deliver software that 500+ professionals rely on daily.

Built solo. Shipped to production. Serving 500+ users. Generating $180K+ in annual revenue.


Want to Work on Something Similar?

I'm available for freelance projects and full-time opportunities. Let's build something amazing together!