01

Architecture

Evern uses a shared Rust core with platform-native UI on Android (Jetpack Compose) and iOS (SwiftUI). UniFFI generates idiomatic Kotlin and Swift bindings from the Rust source.

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.

architecture
┌─────────────────────────────────────────┐
│ Android UI iOS UI │
│ Kotlin/Compose Swift/SwiftUI │
├─────────────────────────────────────────┤
│ UniFFI Bindings (auto-gen) │
├─────────────────────────────────────────┤
│ evern-core (Rust) │
│ Terminal │ Networking │ AI Intelligence│
│ Persistence │ Sessions │ Identity │
└─────────────────────────────────────────┘

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.

runtime data flow
# Input path
User types/speaks → Compose/SwiftUI → UniFFI
→ send_input() → evern-core → SSH/Mosh → Server
 
# Output path
Server → SSH/Mosh → evern-core → terminal emulator
→ output parser (Layer 0/1/2) → OutputEvents
→ UniFFI → drain_output_events() → native rendering
 
# Credential path
Keystore/Keychain ←FFI trait→ evern-core → russh

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

tree evern/
evern/
├── core/
│ ├── evern-core/ # Main library crate
│ │ ├── terminal/ # alacritty_terminal wrapper
│ │ ├── networking/ # SSH (russh) + Mosh (custom)
│ │ ├── intelligence/ # Three-layer AI parser
│ │ ├── session/ # Session lifecycle, history
│ │ ├── persistence/ # SQLite CRUD
│ │ ├── credentials.rs # Platform credential trait
│ │ └── identity/ # Design Identity Engine
│ ├── evern-mosh/ # Standalone Mosh/SSP crate
│ └── evern-test/ # Integration tests
├── android/
│ └── app/
│ ├── ui/ # Compose screens
│ └── platform/ # Keystore, voice, services
├── ios/
│ └── Evern/
│ ├── UI/ # SwiftUI views
│ └── Platform/ # Keychain, voice, background
└── .github/workflows/ # CI configuration

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 with rustup target add (see below).
  • Android: Android Studio Ladybug (2024.2+) with NDK r27+, CMake 3.22+. Set ANDROID_NDK_HOME in your environment.
  • iOS: Xcode 16+ with iOS 17+ SDK. macOS only.
  • Cargo plugins: cargo-ndk, cargo-lipo, uniffi-bindgen-cli

Environment Setup

environment setup
# Install Rust targets for Android
rustup target add aarch64-linux-android \
armv7-linux-androideabi x86_64-linux-android
 
# Install Rust targets for iOS
rustup target add aarch64-apple-ios \
x86_64-apple-ios aarch64-apple-ios-sim
 
# Install cargo plugins
cargo install cargo-ndk cargo-lipo uniffi-bindgen-cli
 
# Set NDK path (adjust for your install)
export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/27.0.12077973

Build the Rust Core

build commands
git clone https://github.com/scottdaly/evern.git
cd evern/core
 
# Run tests first
cargo test -p evern-core
cargo test -p evern-mosh
 
# Build for Android (outputs .so to jniLibs)
cargo ndk -t arm64-v8a \
-t armeabi-v7a -t x86_64 \
-o ../android/app/src/main/jniLibs \
build --release
 
# Build for iOS (universal XCFramework)
cargo lipo --release
# Or use the project script:
../ios/scripts/build-rust-xcframework.sh
 
# Generate UniFFI bindings
uniffi-bindgen generate \
evern-core/src/evern_core.udl \
--language kotlin \
--out-dir ../android/app/src/main/kotlin/
uniffi-bindgen generate \
evern-core/src/evern_core.udl \
--language swift \
--out-dir ../ios/Evern/Bridge/

Build the Platform Apps

  • Android: Open android/ in Android Studio. The Gradle build expects the .so files in app/src/main/jniLibs/ and the generated Kotlin bindings in place. Build and run on an emulator or device.
  • iOS: Open ios/Evern.xcodeproj in 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