4 KiB
4 KiB
LibreChat Mobile
Native mobile client for LibreChat (Android & iOS). Connects to existing LibreChat backend servers (no backend changes). Users specify server URL during onboarding.
Tech Stack
- UI: Jetpack Compose + Navigation Compose 3 (Nav 3)
- DI: Koin (KMP-ready)
- Network: Ktor Client (OkHttp engine)
- Serialization: Kotlinx Serialization
- Local Storage: Room (cache), DataStore (prefs), EncryptedSharedPreferences (tokens)
- Build: Gradle 9.4.1, AGP 9.1.0, Kotlin 2.3.20, compileSdk 36, minSdk 26
Module Layout
app/ → Single Activity, adaptive navigation (phone/tablet)
shared/ → KMP shared framework (iOS entry point, shared navigation)
build-logic/ → 13 convention plugins for consistent Gradle config
core/common/ → Result type, dispatcher DI, coroutine scopes, extensions
core/model/ → @Serializable data classes (pure Kotlin, no Android deps)
core/network/ → Ktor client, 16 API services, SSE client, auth interceptor
core/data/ → Room DB, DataStore, EncryptedSharedPrefs, repository impls
core/ui/ → Material 3 theme, shared composables
feature/auth/ → Server URL, Login, Register, 2FA, OAuth, Forgot Password
feature/chat/ → Real-time chat with SSE streaming, message rendering
feature/conversations/→ Paginated list, CRUD, tags, search, export/import
feature/settings/ → Account, appearance, about, danger zone
feature/agents/ → Agent marketplace with search and categories
feature/files/ → File upload, management, image viewer
Each module has its own CLAUDE.md with specific guidance.
Architecture Rules
- Feature modules depend on
:core:*only, never on each other - Single Activity with Nav 3 (
NavDisplay+NavBackStack<NavKey>+entryProvider) - Unidirectional data flow: UI → ViewModel → Repository → API/Room
- Room is a read-through cache; server is source of truth
- Custom SSE parser over raw ByteReadChannel (not Ktor SSE plugin)
Adding a New Feature Module
- Create the module directory under
feature/ - Apply the convention plugin in
build.gradle.kts— this auto-applies Koin + Compose deps:librechat.kmp.feature— for KMP modules with shared iOS + Android code (most features)librechat.mobile.feature— for Android-only modules
- Create
di/<Feature>Module.ktwith a Koinmodule { }containingviewModelOf(::YourViewModel)definitions - Register the module in
LibreChatApplication.kt'sstartKoin { modules(...) }list - Use
koinViewModel()in screen composables to inject ViewModels
Backend Quirks
- Mutation endpoints wrap body in
argfield:{ "arg": { ... } } - Two-phase SSE: POST →
{ streamId }, then GET stream.streamId === conversationId GET /api/configdrives feature availability — never hardcode- ua-parser-js middleware rejects non-browser User-Agents with 403 (workaround: Chrome UA string)
- Refresh token sent via request body (not HTTP-only cookies)
- iOS SSE streaming uses a custom
NWConnection-based HTTP/1.1 transport (core/network/src/iosMain/.../sse/SseHttpTransport.ios.kt) to bypass NSURLSession's undocumentedtext/*content-type buffering. Seecore/network/CLAUDE.mdSSE section for the two-layer buffering story.
Upstream Sync
upstream/— Git submodule of the official LibreChat repo. Read-only reference for API and web app parity. Do not modify.UPSTREAM_VERSION— Tracks which official tag/commit this mobile build is based on. Updated by the/sync-upstreamskill.BackendVersion.kt(core/common/) —SUPPORTED_BACKEND_VERSIONconstant must match the tag inUPSTREAM_VERSION(withoutvprefix)./sync-upstream— Claude Code skill to diff upstream releases, identify gaps, propose changes, and implement them with user approval. Uses Agent Teams (investigator, android-expert, implementer, verifier).