A reference architecture and starter system for modular desktop applications built with Rust and Tauri.
Website and documentation: casoon.github.io/origin
Origin is not a framework that replaces Tauri, and not a monolithic crate every application must depend on. It is an opinionated set of architecture rules, reusable platform crates, security conventions and build processes — plus a reference application that demonstrates all of it.
A new desktop application should not have to reinvent authentication, storage, synchronisation, permissions, updates, notifications, logging and project structure every single time.
Independent where useful. Shared where proven. Explicit where different.
Current release: 0.2.0. The project is still pre-1.0.
Implemented: the architecture contract, the platform contracts, the Tauri host layer, OAuth with PKCE, account management, the connector contract, the sync engine, background jobs, local workspace/process contracts and adapters, MCP over stdio and authenticated loopback HTTP, the app manifest with generated capabilities, and a running reference application.
Distribution is prepared in the template: a tag-driven release workflow that builds unsigned by default, states in the log what the artifact is, and turns signing on one secret at a time. Nothing there is verified end to end — there is no product to sign yet.
Domain code does not know Tauri exists.
Tauri is the desktop host. The application itself is a set of independently testable
Rust components that depend on traits, not on an AppHandle.
PRODUCT APP examples/demo — later other independent products
↓ composition root
APPLICATION MODULES feature areas, registered at compile time
↓
CONNECTORS external service integrations
↓
ORIGIN PLATFORM events · secrets · settings · storage · telemetry · app
↓
PLATFORM CONTRACTS the OS capabilities domain code may depend on
↓
TAURI HOST plugins, tray, IPC, capabilities
The quality gate that keeps this honest: the whole application must be testable without starting Tauri.
let application = ApplicationBuilder::in_memory()
.clock(Arc::new(FakeClock::new(now)))
.notifications(Arc::new(RecordingNotificationService::new()))
.module(PulseModule)
.build()?;The full rule set lives in ARCHITECTURE.md; the reasoning behind each decision lives in adr/.
crates/ platform crates — never depend on Tauri, never know a product
origin-domain error model, domain primitives, Clock port
origin-events typed event bus
origin-platform OS contracts: notifications, workspace, process, tray and more
origin-secrets SecretStore contract + shared contract test suite
origin-settings typed settings
origin-storage Storage port + TTL cache
origin-http HttpClient port, rate limits, status mapping
origin-auth OAuth 2.0 + PKCE, token storage and refresh
origin-accounts several accounts per connector
origin-connector the connector contract
origin-sync sync engine: policies, backoff, offline, health
origin-jobs background jobs: progress, cancellation
origin-mcp-core the MCP boundary: tools an external AI may invoke
origin-ai inference the application performs itself
origin-manifest app.toml: what a product is, plus the security profiles
origin-xtask the maintenance tasks, as a library
origin-telemetry tracing setup and logging conventions
origin-app ApplicationBuilder, modules, service registry
adapters/ concrete implementations of the contracts
origin-storage-sqlite SQLite
origin-secrets-system Keychain / Credential Manager / Secret Service
origin-notifications-tauri native notifications
origin-http-reqwest HTTP via reqwest
origin-auth-loopback RFC 8252 loopback redirect listener
origin-mcp-stdio MCP over stdio
origin-mcp-http MCP over authenticated loopback HTTP
origin-process-std allowlisted local process execution
origin-workspace-fs workspace-scoped filesystem access
origin-workspace-watch workspace filesystem watching
host/origin-tauri plugin wiring, tray, IPC commands, event bridge
frontend/client @casoon/origin-client — the only package that speaks Tauri IPC
frontend/ui @casoon/origin-ui — shared Svelte 5 components and design tokens
examples/demo the reference application
templates/app the project template `cargo xtask new` instantiates,
with its own English documentation set
xtask entry point; the tasks live in crates/origin-xtask
adr/ architecture decision records
| Tool | Version |
|---|---|
| Rust | 1.88 or newer |
| Node | 22 or newer |
| pnpm | 10 or newer |
| Tauri CLI | 2.x (cargo install tauri-cli --version "^2") |
On Linux you also need the Tauri system dependencies (libwebkit2gtk-4.1-dev,
libgtk-3-dev, libayatana-appindicator3-dev, librsvg2-dev).
Note: If you use Volta as your Node version manager on macOS, ensure your user owns
~/.volta(chown -R $(whoami) ~/.volta) to prevent Volta permission errors duringpnpmworkspace checks.
pnpm install
cargo xtask demoThat builds and runs the reference application: a tray app with a background loop,
cached read model, typed events, native notifications and a Svelte 5 frontend that
never calls invoke directly.
Other tasks:
cargo xtask validate # enforce the architecture rules
cargo xtask ci # fmt + clippy + test + generated files + validate
cargo test --workspace # Rust tests, no desktop session required
pnpm -r check # TypeScript and Svelte checksThe system-keychain contract test is excluded by default because it touches your real login keychain. Run it deliberately:
cargo test -p origin-secrets-system -- --ignored- One error model. Adapters translate
rusqlite,reqwestandtaurierrors intoAppErrorat the boundary; the frontend receives a stable, classified contract and can branch onkindinstead of parsing messages. - Contract tests. Every swappable implementation passes the same suite, so the in-memory double and the real system keychain cannot drift apart.
- Typed events.
bus.subscribe::<PlatformEvent>(), notbus.on("sync:done"). A renamed field is a compile error. - Least privilege. Capabilities are scoped per window as named security profiles.
Notifications and URL opening happen in Rust, so the frontend needs no permission for
either.
cargo xtask validatefails the build on a blanketfs:*orshell:*grant. - OAuth that does not cut corners. PKCE always,
stateverified before the code is used, loopback redirect (no custom URL scheme), single-flight refresh, and a refresh response without arefresh_tokenkeeps the old one instead of logging the user out. - Scheduling that belongs to the platform. A connector says how to fetch; the engine owns when — retry, exponential backoff with jitter, offline handling, validators and single-flight. Scheduling is tested by moving a fake clock, not by sleeping.
- Credentials in the OS keychain, never in the application database, addressed per account so revoking one never touches another. Disconnecting an account clears everything stored under it in one call, by namespace convention.
- A composition root that documents the product — every dependency an application has is visible in one function.
- Bring your own AI client, not your own API key. MCP makes the application controllable by the AI the user already has — with a permission level of its own, defaulting to read/propose only. Commit and delete require both an explicit grant and a human confirmation; a missing or failed confirmation denies the call. Inference the application performs itself is a separate, swappable port.
- Contracts generated, not mirrored. Platform IPC types and each product's own
command results are derived from their Rust definitions; a rename in Rust fails CI
instead of surfacing as
undefinedin production. - An upgrade path, not a one-time copy. Each project records the Origin version it
tracks;
cargo xtask updateruns the migrations between it and the current one, regenerates, and hands back a checklist for anything only a human can decide. The migrations are tested against frozen fixture projects, and Origin's CI scaffolds a project from its own template on every pull request. - A manifest instead of copied config.
app.tomlsays what the product is; capability files are generated from it, so a security profile is a named choice rather than a permission list somebody widens one line at a time. The tasks themselves live inorigin-xtaskas a library — a derivative'sxtaskis three lines, and a new architecture rule reaches it with a version bump.
Security first · Testability first · Replaceability first · Explicit architecture
· Minimal hidden magic · Small reusable components · Context-free libraries
Origin deliberately avoids god objects, global app state, direct Tauri access from domain code, string-based event systems, tokens in SQLite, API-specific errors in the UI, and premature universal abstractions.
Read ARCHITECTURE.md first — it is binding. New abstractions follow the Promotion Rule (ADR-0009): a feature starts where it is needed and moves into the platform only once a genuinely neutral shape has emerged, in practice at the third occurrence. Deviating from a recommendation is allowed, but the deviation must be explicit — in the app manifest or as an ADR.
Origin is released under the MIT License. Individual crates are intended to stay usable on their own, outside Origin — in CLIs, services, automations and other projects. No lock-in.