Managers & Repositories
The non-UI engine room — the singletons that talk to your backends. Every one is a Koin
singleton you inject with koinInject() (Compose) or constructor injection (ViewModels), and
every one no-ops safely until you configure its provider, so the kit builds and runs with
no keys.
// in a ViewModel
class HomeViewModel(
private val auth: AuthRepository,
private val purchases: PurchaseManager,
private val analytics: AnalyticsManager,
) : ViewModel()
AuthRepository
Provider-agnostic auth. One interface, three implementations (Supabase / Firebase / Stub)
picked by KitConfig.AUTH_PROVIDER.
auth.signInWithEmail(email, password): AuthResult
auth.signUpWithEmail(email, password): AuthResult
auth.signInWithGoogleIdToken(idToken, nonce): AuthResult
auth.sendPasswordReset(email): AuthResult
auth.signOut()
auth.deleteAccount(): AuthResult // Play-mandatory account + data deletion
auth.currentUser(): AuthUser?
auth.sessionState: Flow<SessionState> // drives the nav guard
Configure with /kit-setup-auth → Sign In Screen.
PurchaseManager
RevenueCat wrapper. Exposes subscription state as StateFlows the nav resolver reads.
purchases.isPremium: StateFlow<Boolean>
purchases.customerInfo: StateFlow<CustomerInfo?>
purchases.currentOffering: StateFlow<Offering?>
purchases.purchase(activity, pkg): PurchaseOutcome
purchases.restore(): PurchaseOutcome
Configure with /kit-setup-paywall → Paywall Screen.
AnalyticsManager
One facade fanning out to whichever providers you enabled — PostHog, Firebase Analytics, Crashlytics, Sentry.
analytics.logEvent("habit_completed", mapOf("streak" to 7))
analytics.logScreen("Home")
analytics.setUser(userId, email)
analytics.logError(throwable, "sync failed") // never gated — always reports
analytics.reset() // on sign-out
Product events respect the user's privacy toggle; crash reporting is never gated.
Configure with /kit-setup-analytics; plan release events with
/kit-plan-release-analytics.
OpenRouterAiRepository
Call any AI model — Claude, GPT, Gemini, Llama, 100+ — through one OpenRouter key. Model-first, use-case-generic.
ai.generateText(model = "anthropic/claude-3.5-sonnet", prompt = "Suggest a habit")
ai.generateTextWithMessages(model, messages, temperature = 0.7)
ai.streamText(model, prompt) // Flow<String> — SSE chunks
ai.generateJson(model, prompt, schema) // structured output
Isolated Retrofit instance (retrofit_openrouter) — never collides with your own API.
Configure with /kit-setup-ai.
UpdateManager & ChangelogManager
Read conventional keys from RemoteAppConfig to power the
update gate, maintenance mode, and changelog. Provider = LOCAL /
Supabase / Firebase. Configure with /kit-setup-updates.
InAppReviewManager
Wraps Play's In-App Review API. Wire it to a trigger (Nth launch / key action / time delay)
with /kit-setup-review-dialog; it fires once (DataStore reviewPrompted). The
Settings Rate button instead opens your Play listing directly (PlayStoreLauncher).
SecureDataStore
Encrypted key-value storage for dev-held secrets (user-entered API keys, third-party OAuth tokens). AES-256/GCM, master key in the Android Keystore. No extra dependencies.
secure.putString("openai_key", key)
secure.getString("openai_key"): String?
secure.observe("openai_key"): Flow<String?>
secure.remove("openai_key")
secure.clear()
Your Supabase / Firebase / RevenueCat tokens are already encrypted by those SDKs — this is for your secrets. Deep dive: Features → Security.