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-authSign 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-paywallPaywall 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.