⚙️ AppShell
A dependency-free application runtime and domain foundation for Rust.
App Engine (runtime infrastructure) + Domain Foundation (reusable domain models) — all in one coherent library.
Features ·
Quick Start ·
Architecture ·
Guides
What Is This?
AppShell is a two-layer library for building Rust applications:
- App Engine — generic application runtime infrastructure (lifecycle, commands, tasks, scheduling, events, signals, history, resources, services, plugins, configuration) without knowing what your application does.
- Domain Foundation — reusable, headless domain models (Canvas2D, Timeline, Layers, NodeGraph, Viewport3D, Table, TextEditor, Relations, Utilities) that sit between the App Engine and your application-specific Domain Engine.
flowchart TD
subgraph AE["APP ENGINE"]
EX["Execution<br/>Commands<br/>Tasks<br/>Scheduler<br/>Resources"]
CM["Communication<br/>Events<br/>Signals<br/>History"]
XT["Extension<br/>Plugins<br/>Services<br/>Permissions"]
SP["Supporting<br/>Runtime + Lifecycle (+ cleanup)<br/>Context + State (thread-safe)<br/>Configuration + Preferences (+ persistence)<br/>Execution Control (cancel, deadline, stream)<br/>Concurrency (thread-safe, async, TaskPool)<br/>Serialization (Serializer, registry, IPC)<br/>Testing (mocks, TestEngine, assertions)<br/>Metadata (Version, PayloadSchema)"]
RL["Reliability<br/>Panic isolation (catch_unwind)<br/>Event priority (High/Normal/Low)<br/>Retry policies (Fixed/Exponential)<br/>RAII resource handles (auto-release)"]
end
subgraph DF["DOMAIN FOUNDATION"]
ENV["Environments<br/>Canvas2D, Timeline, Layers<br/>NodeGraph, Viewport3D, Table, TextEditor"]
REL["Relations<br/>Priority-sorted evaluation<br/>Batch propagation<br/>Value source/sink traits"]
UTIL["Utilities<br/>Filtering, Querying, Tagging<br/>Sorting, Grouping, Validation, Batch"]
end
AE -->|"contracts"| DF
DF -->|"composes"| DE["DOMAIN ENGINE<br/>(app meaning)"]
DE -->|"integration"| API["API / MCP / UI"]
Zero external dependencies by default. Optional tokio for async. Optional serde for serialization.
Comparison
| Feature |
App Engine |
Domain Foundation |
bevy_app |
tokio |
iced |
tauri |
| Lifecycle |
✅ 5-state |
— |
✅ |
❌ |
❌ |
❌ |
| Commands |
✅ |
— |
❌ |
❌ |
❌ |
❌ |
| Tasks |
✅ |
— |
❌ |
✅ |
❌ |
❌ |
| Scheduler |
✅ |
— |
❌ |
✅ (async) |
❌ |
❌ |
| Events |
✅ priority + isolation |
— |
❌ |
❌ |
❌ |
❌ |
| Signals |
✅ thread-safe |
— |
❌ |
❌ |
✅ |
❌ |
| History |
✅ undo/redo/replay |
— |
❌ |
❌ |
❌ |
❌ |
| Resources |
✅ RAII + deps |
— |
❌ |
❌ |
❌ |
❌ |
| Services |
✅ panic-isolated |
— |
✅ |
❌ |
❌ |
❌ |
| Plugins |
✅ permissions |
— |
✅ |
❌ |
❌ |
✅ |
| Config |
✅ persistent |
— |
❌ |
❌ |
❌ |
✅ |
| Persistence |
✅ file/memory |
— |
❌ |
❌ |
❌ |
❌ |
| Serialization |
✅ zero deps |
✅ optional serde |
❌ |
❌ |
❌ |
❌ |
| Concurrency |
✅ Send + Sync |
✅ Send + Sync |
✅ |
✅ |
❌ |
❌ |
| Async |
✅ optional tokio |
— |
❌ |
✅ |
❌ |
❌ |
| Panic isolation |
✅ all handlers |
— |
❌ |
❌ |
❌ |
❌ |
| 2D Canvas |
— |
✅ headless |
❌ |
❌ |
❌ |
❌ |
| Timeline |
— |
✅ headless |
❌ |
❌ |
❌ |
❌ |
| Layers |
— |
✅ hierarchy |
❌ |
❌ |
❌ |
❌ |
| NodeGraph |
— |
✅ + Canvas2D |
❌ |
❌ |
❌ |
❌ |
| Viewport3D |
— |
✅ headless |
❌ |
❌ |
❌ |
❌ |
| Table |
— |
✅ + merged cells |
❌ |
❌ |
❌ |
❌ |
| TextEditor |
— |
✅ Unicode |
❌ |
❌ |
❌ |
❌ |
| Relations |
— |
✅ priority + batch |
❌ |
❌ |
❌ |
❌ |
| Undoable ops |
— |
✅ all operations |
❌ |
❌ |
❌ |
❌ |
| Dependencies |
zero by default |
zero by default |
many |
tokio |
many |
many |
Features
App Engine — Execution Systems
| System |
What It Does |
| Commands |
Application intents — "do X." Registered, executed, panic-isolated. |
| Tasks |
Managed work with lifecycle: Pending → Running → Completed/Failed/Cancelled. Cancellation, deadlines, progress streaming. |
| Scheduler |
Decides when tasks run: immediate, delayed, interval, triggered. Priority queue. Retry policies with fixed/exponential backoff. |
| Resources |
Load, cache, track, and release generic data. RAII handles auto-release on drop. Dependency tracking. Thread-safe. |
App Engine — Communication Systems
| System |
What It Does |
| Events |
"Something happened." One-to-many, priority-ordered, panic-isolated. Serialized payload support for IPC. |
| Signals |
"Something changed." Lightweight, thread-safe. Progress streaming from task handlers. |
| History |
Ordered operations with undo/redo/replay/transactions. Persistence-ready. |
App Engine — Extension Systems
| System |
What It Does |
| Services |
Long-lived capabilities that start/stop with the app. Panic-isolated lifecycle. |
| Plugins |
Extension packages contributing commands, tasks, resources, services, handlers. Permission-controlled. Dynamic loader stub for .so/.dll. |
App Engine — Foundation Systems
| System |
What It Does |
| Runtime + Lifecycle |
Owns the application lifetime. Cancels tasks on stop. Unloads resources on dispose. |
| Context |
Identity hierarchy: Application → Session → Execution. |
| State |
Generic key-value state store with validated transitions. Thread-safe. |
| Configuration |
Persistent settings. File/memory backends. Change notifications. Thread-safe. |
| Preferences |
User-facing settings. Thread-safe. |
| EngineRef |
Read-only bundle of subsystems passed to handlers. Handlers can publish events, load resources, write config, submit to scheduler. |
| Execution Control |
CancellationToken, Deadline, TaskOutputChannel. |
| Persistence |
StorageBackend trait with FileStorageBackend and MemoryStorageBackend. |
| Serialization |
Serializer trait + SerializationRegistry. Serialized bytes on all payloads for IPC. Zero external deps. |
| Concurrency |
All managers Send + Sync. TaskPool. Optional AsyncRuntime (tokio). |
| Panic Isolation |
All handler calls wrapped in catch_unwind. Mutex not poisoned. |
| Testing |
MockCommandExecutor, MockEventBus, MockSignalBus, TestEngine, assertion helpers. |
| Metadata |
Version, PayloadSchema on all definitions. |
Domain Foundation — Environments
| Environment |
What It Does |
| Canvas2D |
Arbitrary 2D spatial organization with objects, transforms, z-ordering, spatial queries. |
| Timeline |
Temporal organization with tracks, clips, keyframes, interpolation, overlap constraints. |
| Layers |
Hierarchical organization with parent-child, visibility inheritance, reparenting. |
| NodeGraph |
Graph connectivity with ports, edges, type matching, topological sort. Composes Canvas2D. |
| Viewport3D |
3D spatial organization with Euler angles, parent-child transforms, spatial queries. |
| Table |
Tabular data with rows, columns, cells, merged cells, ordering, swap operations. |
| TextEditor |
Headless text editing with Unicode positions, ranges, search/replace, line/column. |
Domain Foundation — Relations
| Feature |
What It Does |
| Definitions |
Declarative relation metadata: endpoints, trigger, propagation mode, priority. |
| Evaluation |
Apply developer-supplied effect closures when source properties change. |
| Propagation |
Direct or cascading with cycle protection and max depth. |
| Batch |
Accumulate multiple property changes and propagate together. |
| Value Source/Sink |
Traits for bridging Domain Engine state to evaluation engine. |
Domain Foundation — Utilities
| Utility |
What It Does |
| Filtering |
Composable predicates (AND/OR/NOT) over any data type. |
| Querying |
Named, reusable queries built on filtering. |
| Tagging |
Tags and tag sets with deterministic iteration (BTreeSet). |
| Sorting |
Criteria-based ordering, in-place or non-mutating. |
| Grouping |
Group by key, with deterministic sorted output. |
| Validation |
Rules, multi-rule validation, collect all failures. |
| Batch |
Multi-object operations via trait or closure. |
Quick Start
App Engine
use app_shell::app_engine::*;
use std::any::Any;
// --- Your command handler ---
struct GreetCommand;
impl CommandHandler for GreetCommand {
fn execute(
&self,
input: &CommandInput,
_: &CommandContext,
engine: &EngineRef,
) -> Result<CommandResult, CommandError> {
let name: &String = input.get::<String>()
.ok_or_else(|| CommandError::InvalidArguments("expected name".to_string()))?;
engine.event_bus.publish(
&Event::new("greet.done").with_source("GreetCommand")
);
Ok(CommandResult::success_with(format!("Hello, {}!", name)))
}
}
// --- Main ---
fn main() {
let mut engine: AppEngine = Bootstrap::create().expect("bootstrap failed");
engine.command_executor_mut().register(
CommandDefinition::new("app.greet", "Greet", "Greet someone"),
Box::new(GreetCommand),
).unwrap();
engine.initialize().unwrap();
engine.start().unwrap();
let cmd = Command::new(
"app.greet",
CommandContext::new(ExecutionId::new()),
CommandInput::new("World".to_string()),
);
let result = engine.execute_command(&cmd).unwrap();
println!("{}", result.output::<String>().unwrap());
engine.stop().unwrap();
engine.dispose().unwrap();
}
Domain Foundation
use app_shell::domain_engine::DomainEngine;
use app_shell::domain_foundation::environments::canvas_2d::*;
let mut engine = DomainEngine::new();
// Create a canvas object
let id = engine.execute_canvas(CanvasOperation::Create {
transform: Transform::from_translation(Vec2::new(100.0, 200.0)),
}).unwrap();
// Move it
engine.execute_canvas(CanvasOperation::Move {
id,
translation: Vec2::new(50.0, 50.0),
}).unwrap();
// Undo
engine.undo().unwrap();
assert_eq!(
engine.canvas().get(id).unwrap().transform.translation,
Vec2::new(100.0, 200.0),
);
// Transactions
engine.begin_transaction();
engine.execute_canvas(CanvasOperation::Create { transform: Transform::identity() }).unwrap();
engine.execute_canvas(CanvasOperation::Create { transform: Transform::identity() }).unwrap();
engine.commit_transaction();
// Undo both as one unit
engine.undo().unwrap();
assert_eq!(engine.canvas().len(), 0);
Architecture
flowchart TD
R["Runtime"] --> C["Context"]
R --> S["State"]
R --> CF["Configuration"]
S --> G1["Commands<br/>Tasks<br/>Scheduler<br/>Resources"]
G1 --> G2["Events<br/>Signals<br/>History"]
G2 --> G3["Services<br/>Plugins<br/>Preferences"]
Dependency Direction
Domain Engine ──→ Domain Foundation ──→ App Engine ──→ (nothing)
App Engine ✗→ Domain Foundation
Domain Engine ✗→ App Engine (directly)
App Engine ✗→ UI / API / MCP
Layered Architecture
flowchart TD
A["External Layer (UI / API / MCP / CLI)"] -->|"commands"| B["Domain Engine (application meaning)"]
B -->|"composes"| C["Domain Foundation (reusable domain models)"]
C -->|"uses contracts"| D["App Engine (runtime infrastructure)"]
Each layer knows only the layer below it. The App Engine never imports domain types. The Domain Foundation never imports UI types.
Installation
Add to your Cargo.toml:
[dependencies]
app_shell = { path = "path/to/AppShell" }
# Optional: async runtime
app_shell = { path = "path/to/AppShell", features = ["async-runtime"] }
# Optional: serialization
app_shell = { path = "path/to/AppShell", features = ["serde"] }
Features
| Feature |
Description |
default |
Zero external dependencies. All systems included (sync only). |
async-runtime |
Adds tokio for async task execution via AsyncTaskHandler. |
serde |
Adds serde derives on all serializable Domain Foundation types. |
Key Properties
| Property |
App Engine |
Domain Foundation |
| Dependency-free |
✅ Zero external crates by default |
✅ Zero external crates by default |
| Thread-safe |
✅ All managers are Send + Sync |
✅ All environments are Send + Sync |
| Panic-isolated |
✅ All handler calls wrapped in catch_unwind |
— |
| RAII resources |
✅ Handles auto-release on drop |
— |
| Backward compatible |
✅ Default permissions = FULL_ACCESS |
— |
| Headless |
✅ Runs without UI, API, or MCP |
✅ No UI, rendering, or input |
| Serializable |
✅ Serializer trait, zero deps |
✅ Optional serde feature |
| Persistent |
✅ StorageBackend trait, file + memory |
— |
| Testable |
✅ Mocks, TestEngine, assertion helpers |
✅ 414 tests + proptest + benchmarks |
| Versioned |
✅ Version, PayloadSchema on all definitions |
— |
| Async-ready |
✅ Optional tokio integration |
— |
| Composable |
— |
✅ Environments compose concretely |
| Undoable operations |
— |
✅ Every operation carries undo data |
| Unicode-aware |
— |
✅ TextEditor uses Unicode scalar value positions |
| Priority-sorted relations |
— |
✅ Higher priority relations fire first |
| Cycle-safe |
— |
✅ Propagator + Layer/Viewport3D reject cycles |
Subsystem Summary
flowchart TD
subgraph APPSHELL["APPSHELL"]
subgraph AE["APP ENGINE"]
EX["Execution"]
CO["Communication"]
EN["Extension"]
FO["Foundation"]
RE["Reliability"]
EX --> EX1["Commands (intent → execution, panic-isolated)"]
EX --> EX2["Tasks (managed work, cancel, deadline, progress)"]
EX --> EX3["Scheduler (when work runs, retry, triggers)"]
EX --> EX4["Resources (load, cache, RAII handles, dependencies)"]
CO --> CO1["Events (one-to-many, priority, panic-isolated)"]
CO --> CO2["Signals (lightweight change notifications)"]
CO --> CO3["History (undo/redo/replay/transactions)"]
EN --> EN1["Services (long-lived, panic-isolated lifecycle)"]
EN --> EN2["Plugins (extension packages, permission-controlled)"]
FO --> FO1["Runtime + Lifecycle (+ cleanup on stop/dispose)"]
FO --> FO2["Context (Application → Session → Execution)"]
FO --> FO3["State (generic key-value, thread-safe)"]
FO --> FO4["Configuration (persistent, thread-safe, change notifications)"]
FO --> FO5["Preferences (user-facing, thread-safe)"]
FO --> FO6["EngineRef (handler capabilities)"]
FO --> FO7["Execution Control (cancel, deadline, stream)"]
FO --> FO8["Persistence (StorageBackend trait)"]
FO --> FO9["Serialization (Serializer trait, registry)"]
FO --> FO10["Concurrency (TaskPool, AsyncRuntime)"]
FO --> FO11["Testing (mocks, TestEngine)"]
FO --> FO12["Metadata (Version, PayloadSchema)"]
RE --> RE1["Panic Isolation (catch_unwind on all handlers)"]
RE --> RE2["Event Priority (High/Normal/Low)"]
RE --> RE3["Retry Policies (Fixed/Exponential)"]
RE --> RE4["RAII Handles (auto-release on drop)"]
end
subgraph DF["DOMAIN FOUNDATION"]
ENV["Environments"]
REL["Relations"]
UTI["Utilities"]
ENV --> E1["Canvas2D (objects, transforms, z-order, spatial queries)"]
ENV --> E2["Timeline (tracks, clips, keyframes, interpolation)"]
ENV --> E3["Layers (hierarchy, visibility inheritance, reparenting)"]
ENV --> E4["NodeGraph (ports, edges, type matching, topo sort)"]
ENV --> E5["Viewport3D (3D transforms, parent-child, spatial queries)"]
ENV --> E6["Table (rows, columns, cells, merged cells, ordering)"]
ENV --> E7["TextEditor (Unicode positions, ranges, search/replace)"]
REL --> R1["Definitions (endpoints, trigger, propagation, priority)"]
REL --> R2["Evaluation (effect closures, mutable value store)"]
REL --> R3["Propagation (Direct, Cascading, cycle protection)"]
REL --> R4["Batch (accumulate, commit, rollback)"]
REL --> R5["Value Source/Sink (bridge Domain Engine state)"]
UTI --> U1["Filtering (AND/OR/NOT composable predicates)"]
UTI --> U2["Querying (named, reusable queries)"]
UTI --> U3["Tagging (BTreeSet, deterministic order)"]
UTI --> U4["Sorting (criteria-based ordering)"]
UTI --> U5["Grouping (group by key, sorted output)"]
UTI --> U6["Validation (rules, multi-rule, collect failures)"]
UTI --> U7["Batch (multi-object operations)"]
end
end
AE -->|"contracts"| DF
DF -->|"composes"| DE["DOMAIN ENGINE (app meaning)"]
When to Use
✅ Use AppShell when:
- You need managed execution (commands, tasks, scheduling)
- You need undo/redo with transactions
- You need reusable domain models (2D canvas, timeline, layers, node graph, table, text editor)
- You need property propagation between objects (relations)
- You need plugin extensibility with permission control
- You need decoupled communication (events/signals)
- You need resource management with RAII handles
- You need long-lived services with panic isolation
- You need thread-safe managers (Send + Sync)
- You need async execution (optional tokio)
- You need serialization for IPC or persistence (zero deps or serde)
- You want stable contracts between runtime, domain models, and domain logic
❌ Don't use AppShell if:
- Your app is a simple script
- You have no lifecycle/undo/extensibility/domain-model needs
- You don't need domain/UI/API separation
License
Apache-2.0
Contributing
(Not Yet) Pull requests welcome. See the architecture guide before contributing — clean boundaries are the core value proposition.
Guides
App Engine — Core Architecture
| # |
Guide |
Topic |
| 01 |
Overview |
What App Engine is, what it provides, what it doesn't |
| 02 |
Architecture |
Layering, boundaries, subsystem ownership |
| 03 |
Bootstrap & Lifecycle |
Creating, running, shutting down |
| 04 |
Context & State |
Identity hierarchy, state management |
| 05 |
Commands & Tasks |
Intents, managed work, registration, execution |
| 06 |
Scheduling & Communication |
Scheduler, events, signals |
| 07 |
History & Resources |
Undo/redo, RAII handles, dependencies |
| 08 |
Services & Plugins |
Long-lived capabilities, extensions, permissions |
| 09 |
Domain Engine Integration |
How to build your domain layer |
| 10 |
Application Integration |
UI, API, MCP, CLI, headless |
App Engine — Advanced Features
| # |
Guide |
Topic |
| 11 |
Handler Capabilities |
EngineRef, what handlers can access |
| 12 |
Execution Control |
Cancellation, deadlines, progress streaming |
| 13 |
Persistence & Serialization |
Storage backends, Serializer trait, IPC |
| 14 |
Concurrency & Async |
Thread safety, TaskPool, AsyncRuntime |
| 15 |
Communication & Reliability |
Priority, panic isolation, retry |
| 16 |
Testing & Metadata |
Mocks, TestEngine, Version, PayloadSchema |
| 17 |
Plugin Security |
Permissions, PermissionCheckedEngineRef |
App Engine — Reference
Domain Foundation — Overview
| # |
Guide |
Topic |
| 01 |
Overview |
What Domain Foundation is, what it provides, what it doesn't |
| 02 |
Architecture |
Layering, composition rules, dependency direction |
| 03 |
Environments |
All 7 environments at a glance |
Domain Foundation — Environments
| # |
Guide |
Topic |
| 04 |
Canvas2D |
2D spatial: objects, transforms, z-order, spatial queries |
| 05 |
Timeline |
Temporal: tracks, clips, keyframes, interpolation, overlap |
| 06 |
Layers |
Hierarchy: parent-child, visibility, locked, reparenting |
| 07 |
NodeGraph |
Graph: nodes, ports, edges, type matching, topo sort, Canvas2D composition |
| 08 |
Viewport3D |
3D spatial: Euler angles, parent-child, spatial queries |
| 09 |
Table |
Tabular: rows, columns, cells, merged cells, ordering |
| 10 |
TextEditor |
Text: Unicode positions, ranges, search/replace, line/column |
Domain Foundation — Systems
| # |
Guide |
Topic |
| 11 |
Relations |
Definitions, evaluation, propagation, batch, value source/sink |
| 12 |
Utilities |
Filtering, querying, tagging, sorting, grouping, validation, batch |
Domain Foundation — Integration
| # |
Guide |
Topic |
| 13 |
Domain Engine Integration |
How DomainEngine composes all environments + App Engine |
| 14 |
App Engine Integration |
Command handlers, EngineRef, history, events, signals |
| 15 |
Operations & Undo |
Operation pattern, outcomes, undo/redo, transactions, compound ops |
| 16 |
Serialization |
serde feature, round-trip, known limitations |
Domain Foundation — Reference
| # |
Guide |
Topic |
| 17 |
Testing |
Model, operation, integration, composition, acceptance, concurrency, proptest, stress |
| 18 |
Public API Reference |
Complete type/trait listing |
| 19 |
Error Reference |
All error types and variants |
| 20 |
Quick Start |
5-minute getting started with Domain Foundation |
Built with ❤️ in Rust. Zero dependencies. No compromise.