Expense Buddy is a privacy-first expense tracker built with React Native and Expo, designed around one core idea: turn transaction SMS messages into draft expenses automatically, let you review them on-device, and keep the confirmed data synced to a GitHub repository you control.
Manual entry, analytics, multi-currency support, and GitHub sync are all there, but the product's differentiator is the import pipeline: scan recent Android SMS messages, extract likely expenses, stage them locally, and import only what you approve.
- Auto-detect likely expenses from Android transaction SMS messages
- Keep raw SMS content on-device and out of GitHub sync
- Review, edit, reject, or dismiss imported items before they become expenses
- Sync confirmed expenses across devices using your own private GitHub repository
- Track expenses manually too, with custom categories, payment methods, and saved instruments
- Explore spending with charts, filters, and multi-currency analytics
Expense Buddy ships a hybrid SMS import pipeline for Android. Transaction detection and field extraction stay deterministic and explainable, while category suggestion can be upgraded by a bundled on-device LiteRT model. The flow remains intentionally review-first: the app finds likely transactions, stages them locally, and waits for your approval before anything is added to your expense history.
Today, the import system:
- scans a recent SMS window on Android
- uses deterministic rules for transaction extraction
- runs a bundled native LiteRT classifier for default-category suggestions
- falls back to the regex category suggestion when model confidence is low
- keeps raw SMS data local to the device
Planned direction:
- keep the import flow fully on-device
- broaden parser coverage beyond the initial regex packs
- replace the seed LiteRT model with a model trained on more independent labels
- keep ML limited to suggestion-quality improvements while review-first import remains the guardrail
The current ML model is a first integration milestone, not a final quality claim. It is trained on a small heuristic-shaped seed set and is intentionally gated behind conservative fallback behavior.
For the model-workspace architecture diagrams and the current Android-ready model contract, see tools/sms-ml/docs/model-architectures.md.
- Android-only SMS import using the native
expense-buddy-sms-importmodule - Inline
READ_SMSpermission handling from Settings - Review queue where each staged item can be accepted, edited, rejected, or dismissed
- Native LiteRT category suggestion with regex fallback for low-confidence predictions
- Conservative category suggestion resolver built around shipped default categories
- Local-only processing with no backend parsing
- Quick add flow with amount, category, date, notes, and payment method
- Optional math expression entry like
120+30 - Custom categories with icons, colors, editing, and reordering
- Saved payment instruments for cards and UPI handles
- Full create, edit, delete, and soft-delete sync behavior
- Day-level detail view and searchable history
- Private repository sync using a fetch-merge-push workflow
- Daily CSV files in
expenses-YYYY-MM-DD.csvformat - Optional settings sync for non-sensitive app settings
- Dirty-day tracking so only changed dates are re-uploaded
- Differential fetch and upload using remote blob SHA caching
- Timestamp-based auto-resolution with true conflict detection when needed
- Manual sync controls plus optional auto-sync on launch or on change
- Daily trend charts and category or payment-method breakdowns
- Multi-currency grouping and filtering
- Advanced filters for date range, amount, search text, categories, methods, and saved instruments
- Shared filter state between History and Analytics
- Works on Android, iOS, and Web for core expense tracking
- SMS import is Android-only and requires a native build
- Play-installed Android builds support native in-app update checks and flexible downloads
- In-app review prompts are Play-only and conservatively gated to avoid repeated interruptions
- Dynamic locale loading for English (US, UK, IN), Hindi, and Japanese
- Dark mode, changelog gating, update notifications, and reusable Tamagui UI primitives
- Open Settings and scan recent messages.
- Expense Buddy reads recent Android transaction SMS messages on-device.
- The parser extracts likely transaction details and a regex fallback category locally.
- The native Android module can score the same messages with a bundled LiteRT category model.
- The app stages the candidate with the ML category only when the model is confident enough.
- You review each candidate and decide whether to accept, edit, reject, or dismiss it.
- Only accepted items become normal expense records and participate in optional GitHub sync.
This review-first model is deliberate. The app aims to reduce manual entry without hiding how an import decision was made.
- Node.js 24.x or higher
- Yarn 4.5.0
- Expo CLI
- For iOS: Xcode and CocoaPods
- For Android: Android Studio and SDK
- For SMS import development: an Android development build or release build, because Expo Go cannot load the custom native SMS module
git clone https://github.com/sudokoi/expense-buddy.git
cd expense-buddy
yarn install
yarn start- iOS:
yarn ios - Android:
yarn android - Web:
yarn web
- Go to Settings > GitHub Sync.
- Tap Sign in with GitHub.
- Approve the device-flow code in the browser.
- Pick a personal repository you own and can write to.
- Save the branch and test the connection.
- Create a GitHub Personal Access Token.
- Grant
Contents: Read and writefor a fine-grained token, or use the classicreposcope for a private repository. - In the app, enter the token, repository in
owner/repoformat, and branch. - Save the config and test the connection.
- Sync on app launch
- Sync after every add, edit, or delete
- Manual sync with upload and download controls
Expense Buddy uses Expo Router for navigation, Tamagui for UI, XState stores for app state, and a service layer for sync, storage, analytics, and SMS import logic.
Key implementation details:
- file-based routing under
app/ - XState stores for expenses, settings, filters, notifications, UI state, and SMS review state
- service-layer sync engine for GitHub fetch, merge, conflict handling, and uploads
- native Android SMS module bridged through
services/sms-import/ - property-based and unit tests across storage, sync, parsing, and store behavior
- tracked Expo native modules for Android SMS import and Play Core integrations
For the deeper architecture write-up, see ARCHITECTURE.md.
expense-buddy/
├── app/
│ ├── (tabs)/
│ │ ├── index.tsx # dashboard
│ │ ├── add.tsx # manual expense entry
│ │ ├── analytics.tsx # charts and breakdowns
│ │ ├── history.tsx # history and filters
│ │ ├── settings.tsx # sync, import, and app settings
│ │ └── _layout.tsx
│ ├── +html.tsx
│ ├── +not-found.tsx
│ ├── day/[date].tsx # day detail screen
│ ├── github/repo-picker.tsx # GitHub repository selection flow
│ ├── modal.tsx
│ └── _layout.tsx
├── components/
│ ├── analytics/ # charts, filters, and analytics UI
│ ├── history/ # history list and related UI
│ ├── ui/ # shared styled components
│ ├── NotificationStack.tsx
│ ├── Provider.tsx
│ └── SyncIndicator.tsx
├── hooks/ # analytics, auth, sync, changelog, and update hooks
├── services/
│ ├── sms-import/
│ │ ├── android-sms-module.ts
│ │ ├── bootstrap.ts
│ │ ├── parser.ts
│ │ └── suggestion-resolver.ts
│ ├── github-sync.ts # GitHub API client
│ ├── sync-machine.ts # sync state machine
│ ├── sync-manager.ts # sync orchestration
│ ├── merge-engine.ts # conflict resolution and merge logic
│ ├── expense-storage.ts # local expense persistence
│ ├── settings-manager.ts # settings persistence and sync support
│ ├── update-checker.ts
│ └── auto-sync-service.ts
├── stores/
│ ├── expense-store.ts
│ ├── settings-store.ts
│ ├── filter-store.ts
│ ├── sms-import-review-store.ts
│ ├── notification-store.ts
│ ├── ui-state-store.ts
│ └── store-provider.tsx
├── modules/
│ ├── expense-buddy-play-core/ # Play Store update and review native module
│ └── expense-buddy-sms-import/ # Android SMS import native module
├── locales/ # en-US, en-GB, en-IN, hi, ja
├── decisions/ # ADRs and technical decisions
├── assets/
├── constants/
├── types/
├── utils/
└── scripts/
yarn test
yarn test:watchThe test suite includes unit tests and property-based tests for sync, storage, parsing, and state-management behavior.
The app does not require user-provided environment variables.
The GitHub OAuth Client ID is configured in app.config.js and can be overridden for local Expo development with EXPO_PUBLIC_GITHUB_OAUTH_CLIENT_ID.
Build profiles are defined in eas.json:
developmentfor dev client buildspreviewfor internal testingproductionfor store-ready buildsinternalfor direct APK distribution
yarn start# iOS
eas build --platform ios --profile production
# Android
eas build --platform android --profile production
# APK for testing
eas build --platform android --profile internalRelease automation is documented in .github/RELEASE.md.
Expense Buddy does not collect user data. Expense data lives on-device by default, and optional sync writes only to the GitHub repository you configure. Raw SMS import data is processed locally and is not uploaded as part of sync.
See PRIVACY.md for the full privacy policy.
- In the app: Settings > App Information > Report an Issue
- On GitHub: Issue templates
Read CONTRIBUTING.md before opening a pull request.
This project uses a strict issue-first contribution process. If you want to propose a fix, feature, refactor, or documentation change, open an issue first and discuss the problem and proposed solution before sending code.
Expense Buddy is available under the AGPL-3.0 License.

