In Kael, every piece of mutable application state lives inside an entity. Rather than scattering state across thread-locals or global singletons, you give ownership of your data to theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Augani/kael/llms.txt
Use this file to discover all available pages before exploring further.
App — the single root object that owns all entity state. You then interact with that state through typed handles and context references that enforce safe, structured access. This model makes it easy to wire up observers, subscriptions, and async tasks without fighting the borrow checker, because the App owns the data and contexts provide a controlled gateway to it.
The App and entity ownership
When you start a Kael application, the entry point hands you a mutable reference to the App. This object is the authoritative owner of every entity in the program. You create entities by calling cx.new(), which accepts a closure that constructs your value:
Entity<Counter> returned is a lightweight, reference-counted handle — not the actual data. Like an Rc, it can be cloned freely and dropped to release its slot, but it grants no direct access to the inner Counter without also holding a reference to the app.
Entity<T> handles
An Entity<T> is an inert identifier plus a compile-time type tag. It carries a reference-counted pointer back to the slot the App owns, but you cannot read or write the inner value through the handle alone. To access state, you call .update() or .read() on the handle and pass in the current context:
App owns the underlying data, cloning a handle never duplicates state.
Context<T>
A Context<T> is a wrapper around App enriched with the identity of a specific entity of type T. Whenever you update an entity — whether through cx.new(), .update(), or a render callback — Kael passes you a Context<T> alongside a &mut T. The context provides both the full surface of AppContext methods and a set of entity-specific operations.
Context<T> dereferences to App, so any method available on App is also available on Context<T>.
The AppContext trait
The AppContext trait is the shared interface that App, Context<T>, and async contexts all implement. Its key methods are:
| Method | Description |
|---|---|
cx.new(build) | Create a new entity, returning Entity<T> |
cx.update_entity(handle, update) | Mutably update an entity by handle |
cx.read_entity(handle, read) | Read from an entity immutably |
cx.background_spawn(future) | Spawn a Send future on the background executor |
cx.read_global::<G, _>(callback) | Read a global value |
The VisualContext trait
For entities that are associated with a window — such as views that implement Render — Kael provides the VisualContext trait. It extends AppContext with window-aware operations:
VisualContext methods when your entity needs access to the Window — for example, when creating a view that paints to the screen.
The EventEmitter trait
Entities can emit typed events to communicate with subscribers. To opt in, implement the EventEmitter marker trait for your entity, parameterized by the event type:
EventEmitter<E>, you can call cx.emit(event) from within a Context<Counter> to broadcast the event to all subscribers.
Observing and subscribing
Kael provides two patterns for reacting to entity changes:cx.observe(entity, callback)— fires when the observed entity callscx.notify(). Your callback receives the changed entity’s handle and your own context.cx.subscribe(entity, callback)— fires when the observed entity callscx.emit(event). Your callback receives the entity handle, the event value, and your context.
Subscription. Dropping the subscription cancels it automatically. Call .detach() to keep the subscription alive for as long as both entities exist.
Global state
For application-wide singletons — things like themes, settings, or connection pools — Kael provides a typed global store. Any type that implements theGlobal marker trait can be stored and retrieved by type:
Globals are stored by type identity. Only one value of each type can exist at a time. Use
update_default_global to initialize a global with its Default implementation if it hasn’t been set yet.Reserving entity slots
If you need theEntityId of an entity before it is fully constructed — for example, to store a self-referential handle — use the two-step reserve_entity / insert_entity API: