Offline-first personal finance for Vietnamese, English, and Japanese users.
English Β· TiαΊΏng Viα»t Β· ζ₯ζ¬θͺ
Prerequisites: Flutter SDK 3.44+ Β· Dart 3.12+ Β· Docker 24+ (optional, for backend)
Quick Start Β· Docs Β· Architecture Β· Security Posture Β· Repo Health Β· API Β· CI/CD Β· ADRs Β· Security
| What | Offline-first Flutter personal finance app β 3 languages (VI/EN/JA), zero network dependency |
| Flagship feature | Full local finance engine: wallets, budgets, goals, forecasts, export, encrypted backup β all offline |
| Production-shaped | Docker Compose (9 services), Ed25519 JWT/JWKS, CI/CD configured (hosted runs quota-deferred), security gates (Gitleaks+Trivy+CodeQL+SBOM) |
| Not claimed | No bank integration, no real-time collaboration, no web frontend, no production cloud sync (foundations ready) |
| Dashboard | Transactions | Budgets |
|---|---|---|
![]() |
![]() |
![]() |
| Wallets | Reports | Privacy settings |
|---|---|---|
![]() |
![]() |
![]() |
CashFlow Manager demonstrates a release-ready mobile finance architecture: local SQLite persistence, integer VND money logic, Riverpod state management, privacy-first PIN and opt-in biometric protection, PDF/CSV/backup flows, a separate Fastify/Prisma API, PostgreSQL-backed validation through Docker Compose, OpenAPI documentation, and an optional n8n HMAC automation workflow.
The July 2026 mobile UI uses a light-first private banking ledger style with ivory surfaces, emerald finance actions, compact transaction rows, and seeded professional README media.
The Flutter app is useful without accounts, network access, PostgreSQL, or n8n. The backend and automation stack are production-style foundations for authenticated sync and AI-assisted analysis, not a requirement for local-first usage.
- Dashboard with current balance, monthly income, monthly expense, net cashflow, chart, recent transactions, and budget alerts.
- Five bottom tabs cover Dashboard, Transactions, Wallets, Budgets, and Reports; app-bar actions handle Add transaction and Settings without a floating action button overlay.
- Income/expense transaction management with wallet, category, date, note, recurring flag, validation, search, and filters.
- Wallet overview for cash, bank, e-wallet, and credit-card-ready models.
- Wallet transfer flow with same-wallet validation.
- Monthly budget create/edit/delete flow with warning threshold logic.
- Saving goals with create/edit/delete flow and monthly saving suggestions.
- Reports with selected-month navigation, income/expense chart, category pie, top spending, forecast cards, CSV preview, and PDF sharing.
- Hardened PIN hashing with salted PBKDF2-HMAC-SHA256.
- Failed PIN attempt cooldown and reset after successful unlock.
- Lifecycle relock when the app leaves foreground.
- Opt-in biometric unlock through
local_auth, with PIN always available as fallback. - Re-authentication before destructive flows such as reset data and backup restore.
- Encrypted backup schema v2 with passphrase-derived AES-GCM, while legacy plaintext JSON imports remain supported.
- Backup restore preview with file-size and data-shape validation before replacement.
- CSV export formula escaping to reduce spreadsheet injection risk.
- Separate
apiservice using Fastify, Prisma, PostgreSQL, JWT/JWKS, and OpenAPI. - Docker Compose stack for PostgreSQL, API, frontend artifact server, migrations, seed job, and optional n8n automation.
- n8n workflow import/activation path with HMAC request verification before AI provider calls.
- GitHub Actions CI for Flutter, Android emulator smoke, iOS simulator/no-codesign validation, API, PostgreSQL migration/seed, Docker image builds, Android release artifacts, and Docker Hub and GHCR publishing.
Flutter mobile app (offline-first)
ββ Riverpod controllers
ββ SQLite local finance store
ββ Finance calculator and export services
ββ Privacy lock service
ββ Optional remote sync client
β HTTPS / OpenAPI
Fastify API service
ββ Auth + JWKS
ββ Finance validation routes
ββ Sync bootstrap foundation
ββ Optional n8n AI analysis proxy
ββ PostgreSQL persistence
β HMAC webhook
n8n automation
ββ OpenAI-compatible chat completions workflow
API contract: docs/openapi.yaml
Architecture decisions: docs/adr/
- Flutter 3.44 / Dart 3.12
- Riverpod for state management
- SQLite via
sqlite3+sqlite3_flutter_libs fl_chartfor chartslocal_auth+flutter_secure_storagefor local lockcsv,pdf,printing,share_plus,file_pickerfor export and backup flows- Node.js 26 + Fastify + Prisma API for authenticated PostgreSQL-backed sync foundation and AI analysis proxy
- Docker Compose with PostgreSQL 16, migration/seed jobs, API, frontend artifact server, and optional n8n automation
- GitHub Actions CI for Flutter analyze/tests/builds, Android emulator smoke, iOS simulator/no-codesign validation, API Prisma/typecheck/tests/build, PostgreSQL migration/seed validation, Docker image builds, Android release artifacts, and Docker Hub and GHCR publishing
.
βββ lib/ # Flutter app source
β βββ app/ # App shell, router, theme
β βββ core/ # Shared services (export, privacy, sync)
β βββ data/ # SQLite store, sync, backup/restore
β βββ features/ # Feature modules (dashboard, transactions, etc.)
βββ api/ # Fastify + Prisma backend
β βββ src/
β β βββ modules/ # auth, finance, sync, households, payments, ai
β β βββ lib/ # Shared utilities (errors, env, tokens)
β βββ prisma/ # Schema + migrations + seed
βββ test/ # Flutter unit + widget tests
βββ integration_test/ # Flutter integration (smoke) tests
βββ docs/ # Documentation (ADRs, OpenAPI, architecture)
βββ infra/n8n/workflows/ # n8n automation workflow definitions
βββ scripts/ # Demo media capture, smoke tests
βββ .github/workflows/ # CI/CD pipelines
βββ docker-compose.yml # Production stack
βββ docker-compose.local.yml # Local dev override (port remapping)
βββ Dockerfile.frontend # Flutter APK build + nginx artifact server
βββ api/Dockerfile # Multi-stage Fastify API image
flutter pub get
flutter analyze
flutter test
flutter runnpm --prefix api install
npm --prefix api run prisma:generate
npm --prefix api run typecheck
npm --prefix api testWith Docker Desktop running, apply migrations, seed demo data, then start separate backend and frontend containers plus PostgreSQL:
docker compose up --build -d postgres
docker compose --profile tools run --rm --build migrate
docker compose --profile tools run --rm --build seed
docker compose up --build -d api frontendapibuilds fromapi/Dockerfileand serves the Fastify backend on container port3000.frontendbuilds fromDockerfile.frontendand serves the Android APK artifact on container port8080.postgresruns PostgreSQL 16 for backend validation.migrateandseeduse the Dockerfiletoolstarget so Prisma CLI stays available without bloating the API runtime image.
If host port 3000 is already in use, keep the API container on 3000 and remap only the host port:
$env:API_HOST_PORT = '3001'
docker compose up --buildIf host port 8080 is already in use, remap the frontend artifact server:
$env:FRONTEND_HOST_PORT = '8081'
docker compose up --buildThe API exposes /healthz, /readyz, /.well-known/jwks.json, auth/account routes, finance CRUD routes, sync bootstrap/changes/push routes, household/shared-budget foundations, entitlement/payment foundations, and an optional n8n-backed /v1/ai/analysis endpoint. /readyz checks the migrated application schema, so run migrate before treating the API as ready. The mobile app release version (1.0.0+1) and backend/API package version (0.1.0) are versioned independently.
Optional n8n ChatbotAI Workflow (click to expand)
$env:N8N_CHATBOT_WEBHOOK_URL = 'http://n8n:5678/webhook/cashflow-ai-analysis'
$env:N8N_CHATBOT_WEBHOOK_SECRET = 'replace-with-local-webhook-hmac-secret'
$env:N8N_ENCRYPTION_KEY = 'replace-with-local-n8n-encryption-key'
$env:N8N_AI_CHAT_COMPLETIONS_URL = 'https://api.openai.com/v1/chat/completions'
$env:N8N_AI_API_KEY = '<local-ai-api-key>'
$env:N8N_AI_MODEL = 'gpt-4o-mini'
docker compose --profile automation up --build -d n8n-postgres n8n
docker compose --profile automation run --rm n8n-import
docker compose --profile automation run --rm n8n-activate
docker compose --profile automation restart n8nThe imported workflow at infra/n8n/workflows/cashflow-ai-analysis.json verifies x-cashflow-signature-sha256 before calling an OpenAI-compatible chat completions endpoint. ChatbotAI is a conservative finance coach, not a bank/account data agent: it must not claim access to bank accounts, transactions, wallets, files, external services, or hidden app data. After HMAC verification, the provider prompt receives only { question, locale }, and the workflow/API response contract is strict JSON { answer, suggestions } with 3-5 non-empty suggestions. Keep real AI provider tokens only in local .env files or deployment secrets.
For a token-free local smoke test, run a mock provider in one terminal and point n8n at it before importing the workflow:
node scripts/mock-openai-compatible-provider.mjs
$env:N8N_AI_CHAT_COMPLETIONS_URL = 'http://host.docker.internal:4567/v1/chat/completions'
$env:N8N_AI_API_KEY = 'local-mock-token'
$env:N8N_AI_MODEL = 'local-mock-model'
$env:N8N_CHATBOT_WEBHOOK_SECRET = 'replace-with-local-webhook-hmac-secret'
docker compose --profile automation up --build -d n8n-postgres n8n
docker compose --profile automation run --rm n8n-import
docker compose --profile automation run --rm n8n-activate
.\scripts\smoke-test-n8n-ai.ps1Run the mock-provider smoke before any real AI-provider token test. The smoke script first proves an invalid signature is rejected with zero provider calls, then sends a valid HMAC-signed request and expects exactly one provider call plus { answer, suggestions } with 3-5 non-empty suggestions.
Troubleshooting:
| Symptom | Likely cause | Fix |
|---|---|---|
ai_analysis_unconfigured |
API is missing N8N_CHATBOT_WEBHOOK_URL or N8N_CHATBOT_WEBHOOK_SECRET |
Set local env or deployment secrets and restart the API |
| Invalid HMAC smoke fails | Secret mismatch between the smoke script/API and n8n | Use the same local placeholder secret on both sides |
| Provider returns 401 | N8N_AI_API_KEY is invalid or missing |
Replace it only in a local .env file or deployment secret store |
| n8n import fails | Docker daemon, n8n database, or workflow import job is not ready | Re-run after Docker responds and inspect docker compose logs n8n n8n-postgres |
| Lane | Status |
|---|---|
| Flutter (analyze, test, build) | |
| API (generate, typecheck, test) | |
| Security (Gitleaks, Trivy, audit) | |
| CodeQL SAST | |
| Docker Publish (GHCR + Docker Hub) |
Status note: GitHub Actions quota/token is exhausted, so remote workflow badges are intentionally replaced with static green local-evidence badges. Restore live workflow badges after Actions capacity is available again.
| Need | Link |
|---|---|
| Full documentation index | docs/README.md |
| C4 architecture diagrams | docs/system-architecture.md |
| Sequence & flow diagrams | docs/system-diagrams.md |
| Interactive architecture dashboard | docs/architecture-dashboard.html |
| OpenAPI 3.1 contract | docs/openapi.yaml |
| ADRs (4 records) | docs/adr/ |
| Database schema | docs/database-schema.md |
| Deployment guide | docs/deployment-guide.md |
| Code standards | docs/code-standards.md |
| Design guidelines | docs/design-guidelines.md |
flutter build apk --debug
flutter build appbundle --debug
flutter build apk --release
flutter build appbundle --releaseRelease signing requires standard Android keystore setup outside this repo. Do not commit keystores or passwords.
Published GitHub Release v1.0.0:
- Release page: CashFlow Manager 1.0.0+1
- APK:
cashflow-manager-v1.0.0-android.apk - App Bundle:
cashflow-manager-v1.0.0-android.aab - The updated release workflow is configured to generate SBOM and SHA256 checksum artifacts on the next tagged release run.
The iOS structure exists under ios/. Build/archive requires macOS with Xcode and Swift installed:
flutter pub get
flutter test --no-pub integration_test/cashflow_smoke_test.dart -d <ios-simulator-id> -r expanded
flutter build ios --release --no-codesign --no-pubThen create the signed archive/IPA through Xcode Organizer, Fastlane, or the manual signed CI lane with Apple signing secrets.
The intended public container image names are:
- Docker Hub:
nguyenson1710/cashflow-manager-api - Docker Hub:
nguyenson1710/cashflow-manager-frontend - GHCR:
ghcr.io/jasontm17/cashflow-manager-api - GHCR:
ghcr.io/jasontm17/cashflow-manager-frontend
The Docker publish workflow has published public GHCR manifests for 1.0.0; it also tags release builds as v1.0.0, git SHA, and latest. Docker Hub latest manifests are available for both API and frontend images.
GitHub's repository sidebar package list is controlled by GitHub Packages visibility and OCI source-link metadata. The local token can now verify both container packages through the package API; the direct package links above remain the stable public entry points while GitHub refreshes sidebar presentation.
flutter analyze
flutter test --no-pub -r expanded
flutter test --no-pub integration_test/cashflow_smoke_test.dart -d <device-id> -r expanded
flutter test --no-pub scripts/capture_demo_media_test.dart -r expandedCurrent coverage focus:
- Money parsing and VND formatting edge cases.
- Wallet balance logic across income, expense, and transfer.
- Budget warning logic.
- Saving goal monthly suggestion.
- Empty export behavior and PDF payload generation.
- Privacy lock setup/unlock/relock, biometric opt-in, PIN cooldown, destructive-flow re-auth, dashboard, transaction, wallet transfer, budget, goal, report, export, and backup widget flows.
- Device/simulator smoke coverage for PIN setup, navigation, transaction entry, wallet transfer, budget warning, saving goal, report export preview, backup entry, and reset confirmation.
- Demo screenshot generation from deterministic fake financial data.
Use this when preparing the GitHub repository page:
- About:
Professional offline-first Flutter personal finance app with PIN/biometrics, SQLite, Riverpod 3, Node 26 Fastify/PostgreSQL API, OpenAPI, Docker Hub/GHCR packages, and n8n HMAC automation. - Website/homepage: leave empty until a real public release/download page exists.
- Topics:
android,android-app,dart,docker,fastify,finance-app,flutter,flutter-app,mobile-app,n8n,offline-finance,offline-first,openapi,personal-finance,postgresql,prisma,riverpod,security,sqlite,vietnamese. - Pin screenshots/GIF from
docs/media/in the README. - GitHub Release
v1.0.0is published with APK/AAB artifacts; the updated release workflow is configured to attach SBOM and SHA256 checksum artifacts on the next tagged release run. - GHCR packages are public for
1.0.0; Docker Hublatestimages are verified for API and frontend. - Security posture and branch/package audit live in
docs/security-posture.mdanddocs/repository-health.md. - Do not publish secrets, signing assets, local databases, private automation files, or internal planning notes.
Financial data is local-first. PINs use salted PBKDF2-HMAC-SHA256 before storing in secure storage, biometric unlock is opt-in, repeated failed PIN attempts trigger a cooldown, and the privacy gate relocks when the app leaves foreground. Destructive flows such as data reset and backup restore require re-authentication before replacing local financial data. Backup/restore and report export flows warn about sensitive financial data. Secret files, signing assets, local databases, private automation files, and internal planning notes stay out of git.
- No production cloud sync yet; the app remains offline-first, and server sync/account pieces are release foundations until a separate sync rollout.
- Supabase/Firebase sync is intentionally out of MVP scope.
- Receipt image/OCR remains local-first work for the next product phase.
- iOS release archive cannot be verified on Windows; use macOS/Xcode.
file_pickerandshare_plusstill emit Flutter's future Kotlin Gradle Plugin migration warning during Android release builds.








