Skip to content

Repository files navigation

🧾 Expenn

Receipts in, reimbursements out.

Self-hosted expense and travel management for teams. Snap a receipt on the road, log it against a trip, submit it when you land β€” and let finance approve the whole thing from one dashboard. No spreadsheet graveyards, no shoebox of crumpled receipts.


Live demo API Docs

iOS Android


API CI Web CI Android CI iOS CI License: MIT

.NET 9 Β· ASP.NET Core Β· Next.js Β· Expo Β· PostgreSQL Β· RabbitMQ Β· MinIO Β· one docker compose up


🎬 Try the demo

Where Link What you'll see
🌍 Web app expenn.osas.cloud Workspaces, trips, the expense review queue, and the document vault
βš™οΈ API api.expenn.osas.cloud The live .NET API the web and mobile clients talk to
πŸ“– Help center expenn.osas.cloud/docs End-user guides: getting started, trips, expenses, documents, mobile

πŸ’‘ Sign-in is passwordless. Enter an email, get a one-time code, and you're in β€” then pick a personal account or a company workspace. Self-hosting the stack gives you both sides of the flow (traveler and approver) on your own machine in a couple of minutes.

✨ Two sides, one flow

For travelers πŸŽ’ β€” capture receipts on the go, log expenses against a trip, store passports and itineraries in the document vault, and submit for reimbursement when you're back.

For finance and admins πŸ“Š β€” create and assign trips, review submitted expenses, approve or reject with a note, and track team spend across every trip from one dashboard.

capture ──▢ log against a trip ──▢ submit ──▢ review ──▢ approve ──▢ reimburse
  πŸ“Έ              🧳                  πŸ“€          πŸ‘€         βœ…          πŸ’Έ

πŸ—ΊοΈ What's inside

Surface Highlights
🌍 Web Workspace dashboards, trip management, the review queue, Fumadocs help center
πŸŽ’ Traveler Personal expense log, trip view, document vault, reimbursement status
πŸ“± Mobile Expo app with OTP sign-in, camera receipt capture, biometrics, offline-first sync
βš™οΈ API .NET 9 API β€” owns auth, roles, business rules, and the PostgreSQL schema
πŸ“¬ Async MassTransit over RabbitMQ for background work and notifications
πŸ“¦ Storage MinIO (S3-compatible) for receipts and travel documents

🧱 Tech stack

Surface Stack
API .NET 9, ASP.NET Core, EF Core, PostgreSQL, MassTransit
Web Next.js, React, TypeScript, Fumadocs
Mobile Expo, React Native, Fastlane
Infrastructure MinIO, RabbitMQ, Docker Compose
Delivery GitHub Actions, GHCR, Play Console, TestFlight

πŸš€ Quick start

Docker Compose is the supported full-stack development path. It starts PostgreSQL, MinIO, RabbitMQ, the API, and the web app, and creates the object-storage bucket automatically.

git clone https://github.com/usmhic/expenn.git && cd expenn
cp .env.example .env       # PowerShell: Copy-Item .env.example .env
# Fill the secrets marked FILL ME at the bottom of .env, then:
docker compose up --build

.env.example already carries working localhost defaults for everything that is not a secret, so the only values you need to invent are the passwords and the JWT secret. .env is gitignored; never commit it.

β˜• Grab a coffee for the first build, then open:

Service URL
🌍 Web http://localhost:3000
βš™οΈ API http://localhost:5000
❀️ API health http://localhost:5000/health
πŸ“¦ MinIO console http://localhost:9001
πŸ‡ RabbitMQ console http://localhost:15672
🐘 PostgreSQL localhost:5432

Useful commands:

docker compose logs -f api web
docker compose down
docker compose down --volumes  # also deletes local data

πŸ›οΈ Architecture at a glance

Browser / Mobile app
        β”‚  HTTPS
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Next.js (web)   β”‚  :3000
β”‚   Dashboards      │────────────▢  ASP.NET Core (api)  :5000
β”‚   Traveler + Adminβ”‚                EF Core Β· JWT Β· roles
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                MinIO Β· RabbitMQ Β· PostgreSQL
Mobile (Expo) ──── Bearer token ───▢

The API is the single source of truth for data, auth, and business rules. Full breakdown in ARCHITECTURE.md.

πŸ“ Repository layout

Path Purpose
api/Expenn.Api/ .NET 9 API; owns auth, business logic, and the PostgreSQL schema
web/ Next.js web app
mobile/ Expo SDK 54 mobile client
docker-compose.yml Complete local/deployment stack
.env.example The only environment template

πŸ”§ Configuration

All Compose configuration lives in the ignored root .env. The tracked .env.example lists the required variable names with intentionally empty values; fill the local copy before starting the stack.

Variable Purpose
APP_URL Public web URL and allowed browser origin
API_URL Public API URL compiled into browser clients
STORAGE_URL Public MinIO URL returned for uploaded files
POSTGRES_PASSWORD PostgreSQL password
MINIO_PASSWORD MinIO password shared with the API
RABBITMQ_PASSWORD RabbitMQ password shared with the API
JWT_SECRET API token-signing secret

⚠️ Change every password and secret before deployment. Optional email and SSO values are documented in api/README.md and web/README.md; inject them through the deployment platform when enabling those features.

Mobile apps

iOS Android

πŸ“± The App Store and Play Store listings aren't live yet β€” signed builds already ship through TestFlight and Play Console from CI, and public listings are next. Until then, running the traveler app takes about a minute:

docker compose up -d db minio minio-init rabbitmq api   # backend in Compose
cd mobile && pnpm install && pnpm start                 # app on your host

Scan the QR code with Expo Go and you're in. Set EXPO_PUBLIC_API_URL in your shell when a simulator or physical device cannot reach http://localhost:5000 β€” use the computer's LAN address for a physical device. See mobile/README.md.

Identifier
🍎 iOS bundle com.expenn.traveler
πŸ€– Android package com.osascloud.expenn

🌐 Deployment

Use production secrets, set all three public URLs to HTTPS endpoints, and run:

docker compose up -d --build

Place a TLS-terminating reverse proxy in front of web, API, and object storage. Do not commit the production .env or expose PostgreSQL, RabbitMQ, or MinIO administration ports publicly; restrict those published ports in the deployment platform or a production override.

πŸ€– CI/CD

Workflow Purpose
api-ci.yml Restore/build the .NET API, validate Compose, build/publish the API image
web-ci.yml Type-check, lint, build, publish, and smoke-test the web image
mobile-android-dev.yml Type-check, signed APK, distribute to testers via Firebase App Distribution (dev)
mobile-ios-dev.yml Signed Ad Hoc IPA, distribute to testers via Firebase App Distribution (manual)
mobile-android-release.yml Signed AAB, upload to Google Play Console (main)
mobile-ios-release.yml Signed IPA, upload to App Store Connect / TestFlight (main)
release.yml Create a GitHub release from a vX.Y.Z tag

Container images live at ghcr.io/usmhic/expenn-api and ghcr.io/usmhic/expenn-web. Mobile credentials remain in GitHub Secrets; see the mobile guide.

πŸ“š Documentation

Doc What's in it
Architecture Service boundaries, data flow, and directory purposes
API guide .NET modules, migrations, and optional integrations
Web guide Next.js app, workspace routing, and the help center
Mobile guide Expo setup, device testing, Fastlane, and releases
Mobile delivery Signing secrets, App Distribution, and store submission
Engineering standards Shared conventions across every usmhic project
Package naming Public package, namespace, and app identifiers
Coding-agent guide Repository map, commands, and guardrails
Security policy Private vulnerability reporting and deployment notes

🀝 Contributing

Issues and pull requests are welcome β€” focused fixes, bold ideas, and thoughtful docs improvements all count. Start with CONTRIBUTING.md and follow the Code of Conduct.

πŸ“„ License

MIT Β© usmhic

Because nobody has ever enjoyed a shoebox full of receipts. 🧾

About

Expense and travel management for teams

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages