Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System Architecture

gibb.eri.sh is organized as a Modular Monolith—a single binary with strictly decoupled internal components.

Why Modular Monolith?

ArchitectureProsCons
MonolithSimple deployment, shared memoryTight coupling, hard to test
MicroservicesIndependent scaling, isolationNetwork overhead, complexity
Modular MonolithBest of bothRequires discipline

Performance of a monolith. Maintainability of services.

The Two Layers

┌─────────────────────────────────────────────────────────┐
│                     Tauri App                            │
│  ┌─────────────────────────────────────────────────────┐│
│  │                  plugins/                            ││
│  │  Adapters: Translate between crates and Tauri IPC   ││
│  │  • recorder/  • stt-worker/  • tools/               ││
│  └─────────────────────────────────────────────────────┘│
│                         │                                │
│                         ▼                                │
│  ┌─────────────────────────────────────────────────────┐│
│  │                   crates/                            ││
│  │  Pure Rust: Zero dependencies on Tauri or UI        ││
│  │  • audio/  • bus/  • context/  • stt/  • vad/       ││
│  └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘

crates/ — The Engine

Pure Rust libraries with no UI dependencies:

  • Can be compiled to CLI tools
  • Can be wrapped with FFI for iOS/Android
  • Fully unit-testable

plugins/ — The Adapters

Tauri-specific glue code:

  • Exposes crate functionality as Tauri commands
  • Handles IPC serialization
  • Manages permissions

Key Design Patterns

Dependency Inversion

High-level modules don’t depend on low-level modules. Both depend on abstractions.

#![allow(unused)]
fn main() {
// crates/application doesn't know about Sherpa or Parakeet
// It only knows about the SttEngine trait
pub fn transcribe(engine: &dyn SttEngine, audio: &[f32]) -> Vec<Segment> {
    engine.transcribe(audio)
}
}

Strategy Pattern

Swap implementations at runtime without changing calling code.

#![allow(unused)]
fn main() {
let engine: Box<dyn SttEngine> = match config.mode {
    Mode::Streaming => Box::new(SherpaEngine::new()?),
    Mode::Batch => Box::new(ParakeetEngine::new()?),
};
}

Event-Driven Communication

Components communicate via events, not direct calls.

#![allow(unused)]
fn main() {
// Producer (STT Worker)
app.emit("stt:stream_commit", &segment)?;

// Consumer (Tools Plugin) - doesn't know about STT internals
app.listen("stt:stream_commit", |event| { ... });
}

Deep Dives