Introduction to Orb
Orb is a programming language for describing how software behaves. You write a .orb program that defines your data, your state machines, and how they communicate. The compiler turns it into a working application. The runtime executes it directly.
The language borrows from the actor model: isolated units of computation that hold local state, process messages one at a time, and communicate only through events. In Orb, these units are called Orbitals.
Orbital = Entity + Traits + Pages
Application = a set of Orbitals communicating through events
The Three Parts
Entity: what the data looks like
An Entity defines a data shape with typed fields, persistence rules, and defaults.
type OrderStatus = draft | submitted | fulfilled | cancelled
entity Order [persistent: orders] {
id : string!
status : OrderStatus = "draft"
total : number = 0
items : [object]
customerId : string!
}
Persistence controls scope and lifespan:
| Mode | Where | Lifespan | Shared? |
|---|---|---|---|
persistent | Database | Survives restarts | Across all orbitals |
runtime | Memory | Current session only | Isolated per orbital |
singleton | Memory | Global instance | Shared across orbitals |
Trait: how it behaves
A Trait is a state machine. It defines the states an entity can be in, the events that trigger transitions between states, the guards that control whether a transition is allowed, and the effects that execute when a transition fires.
trait OrderFulfillment -> Order [interaction] {
initial: browsing
state browsing {
INIT -> browsing
(fetch Order)
(render-ui main { type: "data-table", entity: "Order", columns: ["status", "total", "customerId"], itemActions: [{ event: "SELECT", label: "View" }] })
SELECT -> reviewing
(render-ui main { type: "detail-card", entity: "Order", fields: ["status", "total", "items"] })
}
state reviewing {
SUBMIT -> submitting
when (and (> (count @entity.items) 0) (= @entity.status "draft"))
(set @entity.status "submitted")
(persist update Order @entity)
(emit ORDER_SUBMITTED)
(notify "Order submitted" success)
}
state submitting {
CONFIRM -> fulfilled
when (= @entity.status "submitted")
(set @entity.status "fulfilled")
(persist update Order @entity)
}
state fulfilled { ... }
}
Here is a shopping cart behavior from the standard library running live. Click the buttons to interact with the state machine:
orbital CartItemOrbital {
entity CartItem [persistent: cartitems] {
id : string
name : string
description : string
status : string
createdAt : string
pendingId : string
}
trait CartItemCartBrowse -> CartItem [interaction] {
initial: browsing
state browsing {
INIT -> browsing
(ref CartItem)
(render-ui main { type: "stack", direction: "vertical", gap: "lg", children: [{ type: "stack", direction: "horizontal", gap: "md", justify: "space-between", children: [{ type: "stack", direction: "horizontal", gap: "md", children: [{ type: "icon", name: "shopping-cart", size: "lg" }, { type: "typography", content: "Shopping Cart", variant: "h2" }] }, { type: "button", label: "Add Item", event: "ADD_ITEM", variant: "primary", icon: "plus" }] }, { type: "divider" }, { type: "simple-grid", columns: 3, children: [{ type: "stat-display", label: "Items", value: ["array/len", "@entity"], icon: "package" }, { type: "stat-display", label: "Subtotal", value: ["array/len", "@entity"], icon: "dollar-sign" }, { type: "stat-display", label: "Total", value: ["array/len", "@entity"], icon: "receipt" }] }, { type: "divider" }, { type: "data-grid", entity: "CartItem", emptyIcon: "inbox", emptyTitle: "Your cart is empty", emptyDescription: "Add items to get started.", itemActions: [{ label: "Remove", event: "REQUEST_REMOVE", variant: "danger", size: "sm" }], columns: [{ name: "name", label: "Name", variant: "h4", icon: "shopping-cart" }, { name: "description", label: "Description", variant: "caption", format: "currency" }, { name: "status", label: "Status", variant: "badge" }] }, { type: "button", label: "Proceed to Checkout", event: "PROCEED_CHECKOUT", variant: "primary", icon: "arrow-right" }] })
PROCEED_CHECKOUT -> checkout
(ref CartItem)
(render-ui main { type: "stack", direction: "vertical", gap: "lg", children: [{ type: "stack", direction: "horizontal", gap: "sm", children: [{ type: "icon", name: "clipboard", size: "lg" }, { type: "typography", content: "Checkout", variant: "h2" }] }, { type: "divider" }, { type: "data-grid", entity: "CartItem", emptyIcon: "inbox", emptyTitle: "Your cart is empty", emptyDescription: "Add items to get started.", itemActions: [{ label: "Remove", event: "REQUEST_REMOVE", variant: "danger", size: "sm" }], columns: [{ name: "name", label: "Name", variant: "h4", icon: "shopping-cart" }, { name: "description", label: "Description", variant: "caption", format: "currency" }, { name: "status", label: "Status", variant: "badge" }] }, { type: "stack", direction: "horizontal", gap: "sm", justify: "end", children: [{ type: "button", label: "Back to Cart", event: "BACK_TO_CART", variant: "ghost", icon: "arrow-left" }, { type: "button", label: "Confirm Order", event: "CONFIRM_ORDER", variant: "primary", icon: "check" }] }] })
}
state checkout {
BACK_TO_CART -> browsing
(ref CartItem)
CONFIRM_ORDER -> browsing
(ref CartItem)
(render-ui main { type: "stack", direction: "vertical", gap: "lg", align: "center", children: [{ type: "icon", name: "check-circle", size: "lg" }, { type: "typography", content: "Order Confirmed", variant: "h2" }, { type: "typography", content: "Your order has been placed successfully.", variant: "body" }, { type: "button", label: "Continue Shopping", event: "INIT", variant: "primary" }] })
}
}
trait CartItemAddItem -> CartItem [interaction] {
initial: closed
state closed {
INIT -> closed
(ref CartItem)
ADD_ITEM -> open
(fetch CartItem)
(render-ui modal { type: "stack", direction: "vertical", gap: "md", children: [{ type: "stack", direction: "horizontal", gap: "sm", children: [{ type: "icon", name: "plus-circle", size: "md" }, { type: "typography", content: "Add Item", variant: "h3" }] }, { type: "divider" }, { type: "form-section", entity: "CartItem", mode: "create", submitEvent: "SAVE", cancelEvent: "CLOSE", fields: ["name", "description", "status"] }] })
}
state open {
CLOSE -> closed
(render-ui modal null)
(notify Cancelled info)
SAVE -> closed
(persist create CartItem @payload.data)
(render-ui modal null)
(notify "CartItem created successfully")
}
}
trait CartItemRemoveConfirm -> CartItem [interaction] {
initial: idle
state idle {
INIT -> idle
(ref CartItem)
REQUEST_REMOVE -> confirming
(set @entity.pendingId @payload.id)
(fetch CartItem { id: "@payload.id" })
(render-ui modal { type: "stack", direction: "vertical", gap: "md", children: [{ type: "stack", direction: "horizontal", gap: "sm", align: "center", children: [{ type: "icon", name: "alert-triangle", size: "md" }, { type: "typography", content: "Remove Item", variant: "h3" }] }, { type: "divider" }, { type: "alert", variant: "danger", message: "Are you sure you want to remove this item from your cart?" }, { type: "stack", direction: "horizontal", gap: "sm", justify: "end", children: [{ type: "button", label: "Cancel", event: "CANCEL", variant: "ghost" }, { type: "button", label: "Remove", event: "CONFIRM_REMOVE", variant: "danger", icon: "check" }] }] })
}
state confirming {
CONFIRM_REMOVE -> idle
(persist delete CartItem @entity.pendingId)
(render-ui modal null)
(ref CartItem)
(notify "CartItem deleted successfully")
CANCEL -> idle
(render-ui modal null)
(ref CartItem)
CLOSE -> idle
(render-ui modal null)
(ref CartItem)
}
}
page "/cart" -> CartItemCartBrowse, CartItemAddItem, CartItemRemoveConfirm
}
Every transition has three stages:
- Guard evaluates. If it returns false, nothing happens. The transition is blocked.
- State changes from the current state to the target state.
- Effects execute in order: update fields, persist to database, render UI, emit events to other orbitals.
Page: where it lives
A Page binds traits to a URL route. When a user navigates to that route, the page initializes its traits and renders their UI.
page "/orders" -> OrderFulfillment
Guards: logic as expressions
All logic in Orb is written as S-expressions — prefix notation where the operator comes first. Guards follow a transition with the when keyword.
WITHDRAW -> active
when (and
(!= ?email "")
(>= @entity.balance @payload.amount)
(or
(= @user.role "admin")
(= @entity.ownerId @user.id)))
(set @entity.balance (- @entity.balance @payload.amount))
Bindings like @entity.field, @payload.data, @user.id, and @state reference live runtime values. Payload parameters use ?fieldName. The compiler verifies that every binding points to a real field.
Orb ships with 280+ operators across modules: math, string, array, object, time, validation, formatting, async, and probability. These aren't decorative. The prob/* operators do Bayesian inference. The async/* operators handle races, debouncing, and parallel execution.
Effects: what happens
Effects are the actions that fire when a transition succeeds. They're tuples.
(render-ui main { type: "data-list", entity: "Task" })
(render-ui modal { type: "form", fields: ["name", "email"] })
(render-ui modal null)
(persist create Order @payload.data)
(persist update Order @entity)
(persist delete Order @entity.id)
(fetch Order { where: (= @field.status "active") })
(set @entity.status "fulfilled")
(emit ORDER_SUBMITTED { orderId: @entity.id })
(call-service stripe createPayment { amount: @entity.total })
(notify "Saved" success)
(navigate "/orders")
Effects execute on both client and server simultaneously. The client handles render-ui and navigate. The server handles persist, fetch, and call-service. Both handle emit and set. The compiler generates the right code for each side.
The Closed Circuit
Every user interaction in Orb flows through the state machine and back to the UI. This is the closed circuit:
User Action (click, submit, type)
|
v
Event (SUBMIT, SELECT, CANCEL)
|
v
Guard (is this allowed?)
|
v
Transition (state A -> state B)
|
v
Effects (persist, render-ui, emit, notify)
|
v
UI updates (new component renders in slot)
|
v
User sees result, takes next action...
No interaction bypasses the state machine. No UI renders without going through a transition. The compiler enforces this: if a state renders a modal, that state must have a CLOSE or CANCEL transition that dismisses it. Orphan states (unreachable from any transition) cause validation errors.
This makes the entire application deterministic and testable. Given a state, an event, and a payload, you can predict exactly what happens. The CLI can prove it:
orb test my-app.orb --execute
# Total: 30 passed, 0 failed, 3 skipped
Cross-Orbital Communication
Orbitals are isolated. They don't call each other's functions or read each other's state directly. They communicate through events, following the actor model.
A trait declares the events it emits:
trait CartCheckout -> Cart [interaction] {
...
state confirming {
CONFIRM -> complete
(persist create Order @payload)
(emit ORDER_PLACED { orderId: @entity.id })
}
emits {
ORDER_PLACED external { orderId: string }
}
}
Another trait, possibly in a different orbital, declares it listens:
trait InventoryTracker -> Inventory [interaction] {
...
listens {
* ORDER_PLACED -> undefined
}
state idle {
ORDER_PLACED -> updating
(persist update Inventory @entity)
}
}
When CartCheckout emits ORDER_PLACED, InventoryTracker receives it as an UPDATE_STOCK event and transitions accordingly. The emitter doesn't know who's listening. The listener doesn't know who emitted. They're decoupled.
The compiler validates that every emits declaration has at least one matching listens somewhere in the application. No dead letters.
Here is a browse/list behavior from the standard library. This pattern renders a data grid with entity records and supports actions on each row:
orbital BrowseItemOrbital {
entity BrowseItem [runtime] {
id : string
name : string
description : string
status : string
createdAt : string
}
trait BrowseItemBrowse -> BrowseItem [interaction] {
state browsing {
INIT -> browsing
(ref BrowseItem)
(render-ui main { type: "stack", direction: "vertical", gap: "lg", className: "max-w-5xl mx-auto w-full", children: [{ type: "stack", direction: "horizontal", gap: "md", justify: "space-between", align: "center", children: [{ type: "stack", direction: "horizontal", gap: "sm", align: "center", children: [{ type: "icon", name: "list", size: "lg" }, { type: "typography", content: "BrowseItems", variant: "h2" }] }] }, { type: "divider" }, { type: "data-grid", entity: "BrowseItem", emptyIcon: "inbox", emptyTitle: "No browseitems yet", emptyDescription: "Create your first browseitem to get started.", columns: [{ name: "name", label: "Name", variant: "h4", icon: "list" }, { name: "description", label: "Description", variant: "badge", colorMap: { active: "success", completed: "success", done: "success", pending: "warning", draft: "warning", scheduled: "warning", inactive: "neutral", archived: "neutral", disabled: "neutral", error: "destructive", cancelled: "destructive", failed: "destructive" } }, { name: "status", label: "Status", variant: "caption" }] }, { type: "floating-action-button", icon: "plus", event: "INIT", label: "Create", tooltip: "Create" }] })
}
}
page "/browseitems" -> BrowseItemBrowse
}
Composition
Orb programs compose following atomic design. Small behaviors (atoms) combine into larger ones (molecules), which combine into full features (organisms).
An atom might be a loading pattern with 4 states: idle -> loading -> success | error. A modal pattern with 2 states: closed -> open. A confirmation dialog with 2 states: idle -> confirming.
A molecule like a shopping cart composes three atoms into one orbital:
CartOrbital
Entity: CartItem (fields from all three atoms merged)
Trait 1: CartBrowse (browse + checkout states)
Trait 2: AddItem (modal: closed/open)
Trait 3: RemoveConfirm (confirmation: idle/confirming)
Page: CartPage (all three traits)
All three traits share the same entity and the same event bus on the page. When CartBrowse fires ADD_ITEM, the AddItem trait opens the modal. When AddItem fires SAVE, the CartBrowse trait refreshes the list. No wiring code. The state machines respond to events they care about and ignore the rest.
Standard Behaviors
Orb ships with a standard library of 103 reusable behaviors in @almadar/std. Each behavior is a tested, composable .orb program that you can use directly or compose into larger ones.
Atoms: std-loading, std-modal, std-confirmation, std-tabs, std-search, std-pagination, std-display.
Molecules: std-cart, std-auth, std-geospatial, std-agent-rag, std-crud.
Organisms: std-dashboard, std-kanban, std-calendar.
Every behavior in the standard library follows the same atomic design rules. You compose them using TypeScript functions in almadar-std/behaviors/functions/ that merge entity fields, extract traits, and wire them onto a shared page. The output is a valid .orb program.
Two Execution Paths
The same .orb program can be compiled or interpreted:
Compiled (orb compile): the Rust compiler generates a complete TypeScript application. State machines become React components. render-ui becomes JSX. persist becomes Firestore calls. fetch becomes React Query hooks. The generated code has zero Orb dependencies. A developer can read, modify, and ship it without knowing Orb exists.
Interpreted (OrbitalServerRuntime): the runtime loads .orb directly and executes state machines in-process. Events go through processOrbitalEvent(). Guards evaluate through @almadar/evaluator. Effects dispatch to context handlers. Used for servers, agents, CLI tools, and live preview.
Zero-install (orb serve): compiles with Hono backend and serves the full application on a single port using the bundled Bun runtime. No Node.js, no package manager, nothing to install.
orb serve my-app.orb
# Compiling...
# Installing dependencies...
# Building client...
# Server running on http://localhost:3030