Genome declaration
language: tauri-rust
solution_name: ScriptureStudy
desktop_runtime:
framework: tauri
local_store:
kind: sqlite
path: "~/.app/scripture.db"
code_signing: true
auto_update:
endpoint: "https://releases.myapp.com"
pubkey: "${TAURI_UPDATE_PUBKEY}"
runtime_services:
- name: TranslationService
state_machine:
states: [idle, loading, translating, done, error]
transitions:
idle: { start: loading }
loading: { ready: translating, fail: error }
translating: { complete: done, fail: error }
health_pulse_ms: 5000
fallback_policy: retry_then_degrade
retrieval_indexes:
- name: ScriptureIndex
corpus_id: "canonical-kjv"
parsers: [verse, chapter, book]
index_type: full_text
reranker: confidence_weighted
What gets generated
tauri-app/
├── src-tauri/
│ ├── Cargo.toml # Dependencies: tauri, rusqlite, tokio, serde
│ ├── tauri.conf.json # App id, bundle identifier, allowlist,
│ │ # updater config, code signing
│ ├── src/
│ │ ├── main.rs # Entry point: state setup, plugin registration,
│ │ │ # IPC command registration
│ │ ├── commands/
│ │ │ └── {ipc_command}.rs # One file per IPC command: #[tauri::command],
│ │ │ # typed args, typed return, error handling
│ │ ├── services/
│ │ │ └── {runtime_service}.rs # State machine impl: Arc<Mutex<T>>, health pulse,
│ │ │ # fallback policy, degraded mode
│ │ ├── db/
│ │ │ ├── connection.rs # rusqlite pool: lazy_static connection, migrations
│ │ │ └── migrations/
│ │ │ └── 001_initial.sql # Schema for declared entities
│ │ └── retrieval/
│ │ └── {index_name}.rs # Corpus loader, FTS5 index, confidence reranker
│ └── icons/ # 512×512 + platform-specific icon assets
├── src/
│ ├── main.tsx # React + Vite entry
│ ├── App.tsx # Root: router + theme provider
│ ├── api/
│ │ └── ipc.ts # Typed TypeScript wrappers for every IPC command:
│ │ # invoke<ReturnType>('command_name', args)
│ └── pages/
│ └── {Page}.tsx # One page per genome module
├── .github/workflows/
│ └── tauri-release.yml # matrix: [windows-latest, macos-latest, ubuntu-latest]
│ # steps: cargo check → test → tauri build → sign → upload
└── README.md # Build + release checklist (deploy in 30 min, customize 2–3 days)
Typed IPC command in Rust
// src-tauri/src/commands/translate.rs
use serde::{Deserialize, Serialize};
use tauri::State;
use crate::services::translation::TranslationService;
#[derive(Deserialize)]
pub struct TranslateArgs {
pub text: String,
pub source_lang: String,
pub target_lang: String,
}
#[derive(Serialize)]
pub struct TranslateResult {
pub translation: String,
pub confidence: f32,
}
#[tauri::command]
pub async fn translate(
args: TranslateArgs,
service: State<'_, TranslationService>,
) -> Result<TranslateResult, String> {
service.translate(&args.text, &args.source_lang, &args.target_lang)
.await
.map_err(|e| e.to_string())
}
TypeScript IPC client (type-safe)
// src/api/ipc.ts
import { invoke } from "@tauri-apps/api/tauri";
export interface TranslateArgs {
text: string;
sourceLang: string;
targetLang: string;
}
export interface TranslateResult {
translation: string;
confidence: number;
}
export const translate = (args: TranslateArgs): Promise<TranslateResult> =>
invoke<TranslateResult>("translate", args);
The TypeScript types mirror the Rust structs. If you add a field in Rust and forget to update the TypeScript wrapper, the call still compiles — but the generated IPC client is the single source of truth, updated in the same render.
Rust state machine with Arc<Mutex<T>>
// src-tauri/src/services/translation.rs
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, PartialEq)]
pub enum TranslationState { Idle, Loading, Translating, Done, Error(String) }
pub struct TranslationService {
state: Arc<Mutex<TranslationState>>,
}
impl TranslationService {
pub fn transition(&self, trigger: &str) -> Result<(), String> {
let mut s = self.state.lock().unwrap();
*s = match (&*s, trigger) {
(TranslationState::Idle, "start") => TranslationState::Loading,
(TranslationState::Loading, "ready") => TranslationState::Translating,
(TranslationState::Loading, "fail") => TranslationState::Error("load failed".into()),
(TranslationState::Translating, "complete") => TranslationState::Done,
_ => return Err(format!("No transition '{}' from '{:?}'", trigger, *s)),
};
Ok(())
}
}
Two-flow translation contract (canonical lookup vs MT)
When retrieval_indexes are declared, retrieval pipelines use corpus lookup — never machine-translation APIs. MT providers are only allowed for open-domain translation (translation_mode: nmt). The validator enforces this at genome parse time; the generated Rust code includes a header comment documenting which flow applies.
What ships in docs/
docs/decisions/ADR-tauri-vs-electron.md— bundle size, memory, security surface comparison with rejected alternativesdocs/compliance/desktop-code-signing.md— Windows (EV cert), macOS (notarization), Linux (AppImage signing)docs/runbooks/tauri-release.md— local build, GitHub Actions matrix build, auto-updater endpoint setup
Internal links
- SAP CAP integration — server-side counterpart for cloud deployments
- SAP BTP genome — full cloud platform topology generator
CTA
Try it — free plan, no credit card. archiet.com.
Add desktop_runtime: { framework: tauri } to your genome. Generate. Open Cargo.toml and the IPC command files. Check whether the Rust compiles.