The Problem

Users who track habits on mobile devices often have to trust a proprietary backend with their personal routine data. When that data is stored unencrypted, it can be harvested, leaked, or lost if the provider shuts down. For privacy‑focused users the lack of an open‑source, end‑to‑end encrypted sync solution is a concrete blocker.

What This Does

Habo is a cross‑platform habit tracker built with Flutter. The UI lives in lib/ (147 Dart files) and the native shells are in android/ and ios/. Sync is powered by a Supabase backend; the only server‑side code is in supabase/functions/delete-account/index.ts. Encryption keys are generated on‑device and never leave the client, as described in lib/model/habo_model.dart and the sync logic under lib/sync/.

The project ships with an example environment file (.env.example) that defines the Supabase URL and anon key required for sync, plus RevenueCat API keys for in‑app purchases. The app can be self‑hosted by pointing those variables at a private Supabase instance.

How It Is Wired

Execution starts at lib/main.dart, which creates the Flutter MaterialApp and registers the top‑level HaboApp widget. Early in main.dart the SyncService is instantiated; it reads the Supabase credentials from dotenv (populated by .env.rb at runtime) and opens a WebSocket connection defined in supabase/functions/delete-account/index.ts.

  • UI → Model – UI widgets (e.g., lib/habits/edit_habit_screen.dart) call methods on HabitRepository (in lib/model/habit_data.dart). That repository serialises habit objects with jsonEncode and passes the payload to SyncService.uploadHabit.
  • Model → SyncSyncService (in lib/sync/sync_service.dart) uses the Supabase client library (declared in pubspec.yaml) to call supabase.from('habits').upsert(...). The payload is already encrypted by EncryptionHelper (in lib/crypto/encryption_helper.dart).
  • Sync → Backend – The Supabase edge runtime receives the request, stores the ciphertext in the habits table, and triggers no server‑side code other than the optional delete-account function, which simply removes a user’s rows. No other server code is present, so the client holds the only decryption keys.

Only three files touch external resources: lib/main.dart (reads env vars), lib/sync/sync_service.dart (network I/O), and supabase/functions/delete-account/index.ts (HTTP response). No circular imports were detected; the internal import graph contains a single module with zero edges, indicating a flat dependency surface.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/Habo.git
cd Habo

# Install Flutter dependencies
flutter pub get

# Copy the example env and fill in your own Supabase values
cp .env.example .env.rb
# edit .env.rb → set SUPABASE_URL, SUPABASE_ANON_KEY, etc.

# Run on a device or emulator
flutter run          # defaults to Android; add -d ios for iOS
# Or build a release artifact
flutter build apk    # Android
flutter build ios    # iOS (requires Xcode)

The project includes a GitHub Actions workflow (.github/workflows/ci.yml) that runs on every push, but currently it does not invoke the test suite.

Real‑World Use

A privacy‑conscious startup can embed Habo as an internal habit‑tracking tool for employees. By deploying a Supabase instance behind their VPN and supplying the private URL/key in .env.rb, all habit data stays encrypted at rest and in transit, while the open‑source client can be audited for hidden telemetry.

Code Health & Issues

  • Critical – Rotate the credentials in the committed .env.rb file. (.env.rb contains live Supabase and RevenueCat keys.)
  • High – Pin GitHub Actions references to commit SHAs (subosito/flutter-action@v2).
  • High – Remove the committed .env.rb and replace it with a keys‑only example.
  • High – Replace wildcard CORS ("*" in supabase/functions/delete-account/index.ts) with an explicit allow list.
  • High – Add a test step to the CI workflow; the workflow currently never runs the 22 test files.
  • Medium – Declare least‑privilege permissions for GITHUB_TOKEN in the CI workflow.
  • Medium – Add a pre‑commit secret‑scan hook.
  • Low – Set timeout-minutes on CI jobs to avoid runaway builds.
  • Low – Add missing repository convention files (.editorconfig, .gitattributes, formatter config).

Additional static findings: deep nesting (max indentation depth 8 in several Dart files), duplicated 6‑line blocks across 46 files, and three oversized files (> 800 LOC). These increase cognitive load and risk regressions when modifying core habit logic.

The Bottom Line

Habo delivers a functional, privacy‑first habit tracker with end‑to‑end encrypted sync and a self‑hostable backend. The codebase is usable out of the box but suffers from credential leakage, missing CI test execution, and several maintainability hotspots. Teams that need an auditable, open‑source habit‑tracking component and are willing to address the identified security and hygiene issues will find Habo a solid foundation.