Skip to main content

Architecture Decision Record: Client-Side Gemini Routing for Mobile Companion 📱🦉

Status​

Decided (June 2026)


Context​

Athena Helm's mobile companion companion supports both online cloud AI (Gemini) and offline/local AI (Ollama/deterministic parser). In the React and React Native web/mobile applications, cloud-based requests are executed directly from the client web context to Google Gemini's REST API.

However, in the Kotlin Compose Multiplatform (KMP) mobile application, all AI requests were previously routed through a local Go loopback server running on the device via JNI/Go-Mobile. While this works well for offline scenarios, running cloud requests through the JNI loopback server created several issues:

  1. Network Credentials: Resolving Firebase Applet configurations and custom developer API keys client-side is far simpler than passing dynamic secrets across the JNI bridge to the Go environment.
  2. Loopback Restrictions: Android security policies heavily restrict loopback cleartext traffic, creating connection failures for some JNI server bindings.
  3. Architectural Parity: Diverging from the client-side execution model of the React and React Native apps increased developer overhead and test complexity.

Decision​

We elected to route all cloud-based Gemini companion queries (gemini_firebase and gemini_api_key) directly from the Kotlin KMP client to the Gemini REST API, completely bypassing the local JNI loopback server.

  1. REST Client: Implement a native Ktor HTTP client call to https://generativelanguage.googleapis.com/v1beta/models/gemini-3.5-flash:generateContent?key={apiKey} using Kotlin serialization models.
  2. Prompt Compilation: Build the prompt client-side with telemetry and manual context, ensuring exact prompt structure parity with React Native.
  3. Actuator Execution: After receiving the JSON response, parse the structured actuator actions client-side and post them to /api/boat/actuators on the local Yacht Server.
  4. Relay Fallback: Local LLM and fully offline operations continue to use the Yacht Server relay or local regex parsing.

Consequences​

  • Eliminated JNI Connectivity Issues: Bypassing the JNI bridge for cloud calls completely eliminates cleartext loopback network failures during active internet connections.
  • Unified Prompt Interface: Ensured exact prompt parity (personality matrices, manual contexts, and telemetry formats) across Web, React Native, and Kotlin apps.
  • Improved Extensibility: Using standard Ktor clients allows future integrations with other providers (e.g. OpenAI or Claude) directly within Kotlin without backend changes.