Backend Modes
Standalone (Supabase) vs Unified (Soar API): how to choose, how the mode is resolved, and what does not mix.
The template ships with two complete backends behind one set of business
interfaces. Which one the app is assembled against is a build-level choice
made in local.properties — a single install authenticates and stores data
against exactly one backend, and there is no user-facing switch.
| Standalone Mode | Unified Mode | |
|---|---|---|
SOAR_BACKEND_MODE | supabase (default) | soarApi |
| Auth | Supabase Auth | Better Auth (/api/auth/*, Bearer token) |
| Data | PostgREST + RLS | Soar App API (/api/app/v1/*) |
| Storage | Supabase Storage | Object storage via presigned upload API |
| Push | FCM via Supabase Edge Function | FCM token registry; v1 test-push endpoint is APNs-only |
| User ID | Supabase UUID | Better Auth text ID (opaque string) |
| Shares users/data with a web app | No | Yes — same host as a conforming Web template |
Standalone Mode is the classic single-template setup: the Android app talks directly to your Supabase project. If you only bought the Kotlin template, this is your mode and nothing about it changed.
Unified Mode points the app at a conforming Web host's Soar App API instead. Web and Android then share one Better Auth user, one todos table, one profile, and one subscription row — a purchase on either platform unlocks the other. Use it when you run a Web host and Android as one product.
Start here
Pick the mode, then follow exactly one setup guide:
Standalone: Supabase Setup
Create the Supabase project: schema, auth callback, Edge Functions, and secrets.
Unified: Soar API Setup
Connect to a conforming host: client build keys, host environment, sessions, and verification.
If you are undecided, the platform-level mode guide and Compatibility Matrix compare the two deployments product-by-product.
How the mode is resolved
SOAR_BACKEND_MODE flows from local.properties into BuildConfig, and
core/backend/BackendMode.kt resolves it once at startup:
soarApi(case-insensitive) → Unified Mode.supabaseor blank → Standalone Mode.- Any other value fails startup with a
BackendConfigurationException("Invalid SOAR_BACKEND_MODE") — a typo cannot silently pick a backend.
core/backend/BackendConfig.kt then validates the Unified keys eagerly:
SOAR_API_BASE_URL must be an absolute HTTP(S) service root (release builds
require HTTPS; do not include /api/app/v1 — the client appends it), and
SOAR_API_ORIGIN must be a bare scheme ending in ://. The DI graph
assembles repositories for the resolved mode only; view models and composables
depend on the business interfaces and contain no backend type checks.
No silent fallback. If Unified Mode is selected but a SOAR_API_* value
is missing or malformed, startup fails with a diagnosable configuration
error — the app never quietly degrades to Supabase. A misconfigured backend
should be impossible to miss.
The two modes do not mix
Each backend is a separate identity source. A Supabase session cannot call the Soar App API, and a Better Auth session cannot read your Supabase tables — by design there is no bridging, no dual-write, and no fallback from one mode to the other:
- Auth, data, storage, and subscription always come from the same backend preset. You cannot sign in with Supabase and store todos via the Soar API.
- Users created in one mode do not exist in the other. The same email registered in both places is two unrelated accounts.
Switching a production app between modes is a data migration, not a config
flip. Flipping SOAR_BACKEND_MODE on an install base means: migrating user
accounts into the new identity source (users must re-authenticate — password
hashes and OAuth links don't transfer automatically), moving todos, profiles,
and uploaded files, and re-pointing RevenueCat appUserIDs to the new user
IDs. Pick the mode per deployment before launch; treat a later switch as a
planned migration project.
