System Overview
The business logic, networking, data persistence, and AI output parsing all live in a shared Rust library (evern-core). Each platform has a fully native UI layer that consumes this library through auto-generated UniFFI bindings.
This approach — shared core with native UI — is the same used by 1Password, Figma, and Signal. It provides feature parity across platforms while allowing each app to use native GPU rendering, platform credential storage, and OS-specific background APIs.
Data Flow
At runtime, user input flows from the platform UI through UniFFI into the Rust core, which manages the SSH/Mosh network connection. Terminal output flows back through the same boundary, passing through the three-layer parser before reaching the native rendering layer.
Tech Stack
| Component | Technology | Rationale |
|---|---|---|
| Shared Core | Rust | Memory-safe, near-C performance, compiles to all mobile targets |
| FFI | UniFFI (Mozilla) | Auto-generates idiomatic Kotlin + Swift bindings |
| Terminal | alacritty_terminal |
VT100/xterm battle-tested, MIT licensed, Rust-native |
| SSH | russh (pure Rust) | Async with tokio; no C cross-compilation |
| Mosh | Custom Rust (SSP) | Avoids GPL-3; well-documented protocol; bounded scope |
| AI Parser | Rust + tree-sitter | Three-layer rendering; 30+ language grammars |
| Async I/O | tokio | Industry-standard Rust async runtime |
| Persistence | rusqlite (SQLite) | Single shared database across platforms |
| STT Engine | sherpa-onnx (Moonshine v2) | Bundled offline model (~30MB), platform-native fallback |
| Android UI | Kotlin + Jetpack Compose | Modern declarative UI with Material 3 |
| iOS UI | Swift + SwiftUI | Modern declarative UI with platform conventions |
System Layers
| Layer | Technology | Shared? |
|---|---|---|
| UI Shell | Compose (Android) / SwiftUI (iOS) — input, tabs, server management, rendering, settings | No |
| FFI Bindings | UniFFI auto-generated Kotlin + Swift | Generated |
| Terminal Core | Rust (alacritty_terminal) — VT100/xterm state machine, scrollback |
Yes |
| AI Intelligence | Rust + tree-sitter — output parser, agent detection, syntax highlighting | Yes |
| Networking | Rust (russh + custom Mosh + tokio) — SSH2, Mosh, async I/O, reconnection | Yes |
| Session Manager | Rust — multi-session state, tab lifecycle, persistence, history | Yes |
| Persistence | Rust (rusqlite) — profiles, history, scrollback cache, snippets | Yes |
| Credential Storage | Android Keystore / iOS Keychain via FFI trait | Bridge |
| GPU Rendering | Android: SurfaceView+OpenGL; iOS: Metal/CoreText | No |
| Voice Input | Moonshine v2 Tiny (shared) + platform-native fallback | Partial |
Key Decisions
Why Rust core with UniFFI?
Feature parity across platforms from day one. A single codebase means a single set of bugs, a single set of tests, and near-C performance on both Android and iOS. UniFFI generates idiomatic bindings — no manual JNI or C bridging.
Why russh instead of libssh2?
Pure Rust means no C cross-compilation headaches, native integration with tokio for async I/O, and a simpler build pipeline. russh supports Ed25519, RSA, and ECDSA keys.
Why custom Mosh implementation?
The official Mosh client is GPL-3, incompatible with Apache 2.0 licensing. The State Synchronization Protocol is well-documented with bounded scope — a focused Rust implementation avoids the license conflict entirely.
Why no cross-platform UI?
Terminal rendering demands platform-specific GPU access (OpenGL ES on Android, Metal on iOS). The UI layer is thin — the heavy logic lives in the Rust core. Native UI gives each platform its expected look and interaction patterns.
Performance Targets
| Metric | Target | Status |
|---|---|---|
| Input latency | <16ms keystroke to display | Target |
| Streaming output | <8ms per frame | Target |
| SSH connection | <2s on Wi-Fi; <4s on LTE | Target |
| Mosh handoff | <500ms | Target |
| Cold start | <1.5s | Target |
| Memory usage | <80MB per session | Target |
| Battery impact | <3% per hour (active) | Target |
| Binary size | <8MB per ABI | Target |
All targets are measured on mid-range devices (Pixel 7 / iPhone 14). Validation against these targets is planned for Phase 5 (pre-launch profiling across 3+ device tiers per platform). None have been formally validated yet.
Repository Structure
Building from Source
Building Evern requires the Rust toolchain, platform SDKs, and a few cargo plugins. The Rust core compiles to Android NDK targets via cargo-ndk and to iOS universal frameworks via cargo-lipo.
Prerequisites
- Rust:
rustup(latest stable). Add mobile targets withrustup target add(see below). - Android: Android Studio Ladybug (2024.2+) with NDK r27+, CMake 3.22+. Set
ANDROID_NDK_HOMEin your environment. - iOS: Xcode 16+ with iOS 17+ SDK. macOS only.
- Cargo plugins:
cargo-ndk,cargo-lipo,uniffi-bindgen-cli
Environment Setup
Build the Rust Core
Build the Platform Apps
- Android: Open
android/in Android Studio. The Gradle build expects the.sofiles inapp/src/main/jniLibs/and the generated Kotlin bindings in place. Build and run on an emulator or device. - iOS: Open
ios/Evern.xcodeprojin Xcode. The XCFramework must be built first (see above). Build and run on a simulator or device.
Common Build Issues
| Error | Cause | Fix |
|---|---|---|
linker not found for Android targets |
ANDROID_NDK_HOME not set or NDK not installed |
Install NDK via Android Studio SDK Manager and set the environment variable |
no such target for iOS |
Missing Rust target | Run rustup target add aarch64-apple-ios |
| UniFFI version mismatch | uniffi-bindgen-cli version differs from uniffi crate dependency |
Match the CLI version to the version in Cargo.toml |
| XCFramework build fails (lipo error) | Sherpa-onnx static libraries contain multiple architectures | Use ios/scripts/build-rust-xcframework.sh which thins archives per target |
CI Workflows
- rust-core.yml — builds Rust for all targets, runs tests, generates bindings, smoke tests Kotlin/Swift runtime
- android.yml — builds the Android app
- ios.yml — builds the iOS app
- release.yml — coordinated release pipeline for both platforms