This app is not publicly viewable through this resume or on GitHub. To see it in action, request access by reaching out through the contact links in the footer below.

A One-Person Engineering Operation

A full-stack financial technology product built, deployed, and maintained entirely by one person. The app is live and accessible to real users on the internet. It includes a mobile app, a backend API, a data enrichment pipeline, AI-powered features, payment processing, and bank connectivity. Every decision was made with two things in mind: keeping costs as low as possible while building at a quality level that can scale.

React Native / Expo Node.js / TypeScript PostgreSQL Docker GitHub Actions Cloudflare CDN + WAF Tailscale VPN OpenAI GPT-4o-mini Plaid Twilio Stripe Sentry Slack Alerts GDPR / CCPA Compliant

Infrastructure and Hosting

The app runs on a self-hosted Linux server rather than a managed cloud provider. This cuts hosting costs significantly compared to AWS or Google Cloud for the same workload. Users access the app through Cloudflare, which handles security and performance at the network edge. A separate private channel (Tailscale) is used exclusively for developer access to the server, keeping admin tools completely off the public internet.

How a user request travels through the system

User / Internet Anyone with the app can access this
Cloudflare Blocks attacks, caches content, manages SSL
nginx Routes traffic, enforces rate limits
API Server Node.js backend running in Docker
Database PostgreSQL, internal only, never exposed
Developer access (deploys, logs, DB) uses a separate private Tailscale VPN channel, completely separate from user traffic

Why Self-Host

Running on a dedicated server instead of a cloud provider like AWS reduces monthly infrastructure costs by a large margin for the same performance. The tradeoff is more setup work upfront, which is manageable when you understand the stack end-to-end.

Cloudflare (Free Tier)

Cloudflare sits in front of the server and handles DDoS protection, a web application firewall, SSL certificates, and content caching at no cost. This gives the app enterprise-grade network protection without an enterprise budget.

Docker for Consistency

  • The API and database each run in their own container
  • Containers restart automatically if they crash
  • The same setup runs in staging and production, so there are no "it works on my machine" surprises
  • Rollbacks are fast because old container images are preserved

Cost angle: Self-hosting + Cloudflare free tier replaces services that would cost hundreds of dollars per month on managed cloud infrastructure. The savings compound as the user base grows.

Code and Deployment Pipeline

All code lives on GitHub. Every time code is pushed, an automated pipeline runs tests and deploys to staging without any manual work. Deploying to production requires a deliberate manual step, which acts as a safety check before changes reach real users.

Step 1
Push Code
Developer pushes to GitHub, pipeline starts automatically
Step 2
Automated Tests
Code is checked for errors and type issues before it goes anywhere
Step 3
Staging Deploy
Automatically deployed to a staging environment for verification
Step 4
Production Deploy
Manual approval required before changes reach real users

GitHub Actions with Self-Hosted Runners

Instead of paying for a cloud deployment service, GitHub Actions runners are installed directly on the servers. Deployments run on the machine itself, which means no extra service fees and no external access required. GitHub Actions is free for public repos and very low cost for private ones.

Safe Database Updates

Every deployment automatically runs database migrations before the app starts. The migration system only allows additive changes in production, which means you can never accidentally delete or break existing data during a deploy. This is a standard safety pattern that many teams skip until something goes wrong.

Security

Security is layered so that no single failure exposes user data. Each layer is independent, meaning a breach at one level does not automatically mean a breach at the next. Sensitive financial data is encrypted in the database, so even if someone accessed the database directly, they would only see scrambled text.

L1 Network Cloudflare blocks DDoS attacks and malicious traffic before it reaches the server
L2 HTTP Security headers prevent common web attacks; rate limiting stops automated abuse
L3 Auth No passwords stored anywhere. Users log in via a one-time code sent to their phone (Twilio)
L4 Data Bank tokens, transaction data, and phone numbers are all encrypted in the database
L5 External APIs Payment webhooks are cryptographically verified; bank access is read-only

No Passwords

Users authenticate using their phone number and a one-time SMS code. There are no passwords to store, hash, or worry about leaking. This eliminates an entire category of security risk and also simplifies the user experience.

Compliance Built In

GDPR and CCPA compliance is part of the database design, not an afterthought. The system records exactly what users consented to and when. Deleting an account removes all associated data including the bank connection. Consent is required before any data is collected.

Data Enrichment Pipeline

Bank transaction data is messy. The same merchant might appear as "AMZN*MX49K" at one bank and "AMAZON.COM" at another. The enrichment pipeline standardizes and categorizes every transaction automatically, so that the app can give users meaningful information rather than just raw bank strings. This pipeline is also where most of the AI cost optimization happens.

The problem it solves: Banks provide transaction data but not much context. The enrichment layer adds the context: what kind of purchase was it, which merchant, is it a recurring subscription, did the price go up? Without this layer, the AI features would have nothing useful to work with.

How a transaction moves through the enrichment pipeline

1. Bank Sync (Incremental)

Transactions are pulled from the bank using Plaid. Instead of fetching all transactions every time, the system only fetches what is new or changed since the last sync. This keeps API call volume low, which directly reduces cost.

2. Encrypted Storage

Transaction details are encrypted before being saved to the database. The encryption happens at the application level, so even someone with direct database access cannot read the data without the encryption key.

3. Shared Merchant Cache (Key Cost Optimization)

Before calling AI, the system checks a shared merchant database. If this merchant has already been classified for any user, that result is reused instantly at no cost. The AI is only called for merchants the system has never seen before. As more users join, the cache grows and AI costs per transaction drop toward zero.

4. AI Classification (Only When Needed)

For unknown merchants, the transaction is sent to OpenAI (GPT-4o-mini, chosen specifically for its low cost and strong performance). The model returns the merchant category, transaction type, and a confidence score. It also flags things like price increases on recurring charges. GPT-4o-mini costs a fraction of GPT-4, making it the right tool for high-volume classification work.

5. Cache Updated for Everyone

After classification, the merchant is added to the shared cache. Every future user who encounters the same merchant benefits immediately without triggering another AI call. The cost of classifying a merchant is paid once, then amortized across all users.

6. User Feedback Loop

Users can correct classifications they disagree with. These corrections feed back into the system and improve confidence scores over time. The enrichment data gets more accurate as users interact with it, without requiring manual review.

7. Retry on Failure

If the AI call fails for any reason, the transaction is queued for retry with a delay that grows with each attempt. Failed enrichments never block the rest of the app, and they never disappear silently.

Cost optimization result: A naive implementation would call AI for every transaction from every user. This design calls AI once per unique merchant, ever. In practice, common merchants like Amazon, Netflix, and Spotify are classified once and then served from cache for all future users at zero marginal cost. AI spend scales with merchant diversity, not with transaction volume or user count.

AI Features

AI is used in four places in the product. Each use case was chosen because it solves a real user problem and because the cost can be kept under control. GPT-4o-mini was selected across the board because it performs comparably to larger models for these tasks at significantly lower cost.

Transaction Classification

High volume, cached

Categorizes every transaction and identifies recurring subscriptions. Cached in the shared merchant database, so AI is called once per merchant regardless of how many users have it.

Spending Insights

Rate-limited per user

Analyzes a user's transaction history and surfaces specific, actionable observations like unusual spending spikes or subscriptions they may have forgotten about. Free users get one per month, paid users get more.

Financial Score Explanation

Generated once, cached

Each month's financial health score comes with a plain-language explanation. The explanation is generated once and stored, so it does not need to be regenerated every time a user views it.

Conversational Chat

Paid tier only

Users can ask questions about their finances in plain language. This feature is restricted to paying subscribers to keep AI costs proportional to revenue. Rate limited at 20 messages per hour.

Cost controls in place: Monthly generation caps per user tier, result caching wherever possible, GPT-4o-mini instead of GPT-4, lazy initialization (the AI client only starts when actually needed), and paid-tier gating on the highest-cost features. AI spend is tied directly to user activity and revenue.

External Services

Six external services power the core product features. Each was selected based on fit, pricing model, and reliability. The architecture ensures that a problem with one service does not bring down the rest of the app.

Plaid

Bank connectivity

Securely connects to a user's bank and retrieves transaction history, balances, and account details. Access is read-only. Bank credentials are never handled or stored by the app.

Twilio Verify

Phone authentication

Sends the one-time SMS code used to log in. Handles code generation, delivery, and validation. No passwords are ever created or stored anywhere in the system.

Stripe

Subscription billing

Handles all payments, subscription management, and billing. The app never touches a credit card number. Stripe notifies the app in real time when subscriptions are created, updated, or cancelled.

Expo Push

Mobile notifications

Sends push notifications to users' phones when something worth their attention happens, such as a new monthly score or a flagged subscription price increase. Works cross-platform on iOS and Android.

Sentry

Error monitoring

Captures and reports runtime errors in real time across the API and mobile app. When something breaks in production, Sentry records the full stack trace, affected user count, and environment context so the issue can be diagnosed and fixed quickly.

Slack

Operational alerts

Receives automated alerts for critical system events: new user signups, failed payment webhooks, pipeline errors, and Sentry-triggered incidents. Acts as the real-time operations feed for a one-person team with no dedicated monitoring dashboard.

Mobile App

The mobile app is built with React Native and Expo, which allows a single codebase to run on both iOS and Android. One of the key advantages of Expo is over-the-air updates: most app changes can be pushed directly to users' phones without going through an app store review process.

Over-the-Air Updates

Bug fixes and UI changes ship directly to users in minutes rather than waiting days for app store approval. Only changes that touch native device features require a full app store release. This dramatically reduces the cost and friction of maintaining the app post-launch.

Offline-First Data

The app caches server data locally, so it loads immediately from the previous session while fresh data loads in the background. Users never see a blank screen while waiting for the network. This is handled by a combination of Zustand for app state and TanStack Query for server data with local caching.

Skills Demonstrated

Everything involved in designing, building, and operating this system as a single person.

Systems Architecture
End-to-end system design API design Database schema Service integration Cost optimization
AI Implementation
LLM integration Prompt engineering AI cost controls Caching strategies Feedback loops
Data Engineering
Enrichment pipelines Incremental sync Shared caching Retry logic Confidence scoring
Infrastructure
Docker nginx Cloudflare Tailscale VPN Self-hosted CI/CD
Security and Compliance
Encryption at rest Passwordless auth GDPR / CCPA Defense in depth Rate limiting
Backend
Node.js / TypeScript PostgreSQL Prisma REST APIs Webhook handling
Mobile
React Native Expo OTA updates Offline caching Push notifications
Integrations
Plaid Twilio Stripe Sentry Slack OpenAI GitHub Actions

Want to discuss the architecture?

Ask the AI about any design decision or technology used in this system

Chat with AI