diff --git a/content/docs/api/index.mdx b/content/docs/api/index.mdx
index 5b02446b0..4bd6c4939 100644
--- a/content/docs/api/index.mdx
+++ b/content/docs/api/index.mdx
@@ -9,6 +9,16 @@ ObjectStack generates its entire API surface — REST endpoints, realtime protoc
ObjectStack exposes a fully typed REST API. All endpoints use JSON request/response bodies. The API is **service-driven** — routes are only available when the corresponding plugin is installed. Use the [Discovery endpoint](#discovery) to determine what services are available at runtime.
+### Surfaces at a glance
+
+| Surface | Status in this repo |
+| :--- | :--- |
+| **REST** | ✅ Auto-generated from the protocol (`@objectstack/rest`) — CRUD, query, batch, metadata, packages |
+| **Realtime** | ✅ WebSocket subscriptions plus SSE streaming (`@objectstack/service-realtime`) |
+| **MCP** | ✅ Objects and opted-in actions exposed as Model Context Protocol tools ([AI module](/docs/ai)) |
+| **GraphQL** | ⚠️ Route is wired but **bring-your-own service**: `/graphql` returns 501 unless an implementation of the `IGraphQLService` contract is registered — none ships in the open framework |
+| **OData** | ⚠️ Vocabulary only: REST list endpoints accept OData-style operators (e.g. `$top`), but there is no standalone OData endpoint |
+
**Base URL**: Configurable, defaults to `/api/v1`. All paths below are relative to the base URL.
diff --git a/content/docs/automation/index.mdx b/content/docs/automation/index.mdx
index fa739732f..46e863b4e 100644
--- a/content/docs/automation/index.mdx
+++ b/content/docs/automation/index.mdx
@@ -1,31 +1,50 @@
---
title: Automation
-description: Hooks, flows, workflows, approvals, and webhooks — the process engine that reacts to data changes and drives business processes.
+description: 18 hook events, 20+ extensible flow node types, scheduled flows, approvals, and durable webhooks — the process engine that reacts to your data.
---
# Automation
-Automation is ObjectStack's process engine: it lets you attach business logic to your data model **declaratively** — as metadata — instead of scattering it through application code. It is a cross-protocol capability: automations are declared in the Automation Protocol namespace and executed by the ObjectOS runtime against ObjectQL data.
+Automation is ObjectStack's process engine: you attach business logic to the data model **declaratively** — as metadata — instead of scattering it through application code. It is a cross-protocol capability: automations are declared in the Automation Protocol namespace and executed by the ObjectOS runtime against ObjectQL data.
+
+The smallest useful automation is a hook (from the CRM example app):
+
+```typescript
+import type { Hook, HookContext } from '@objectstack/spec/data';
+
+export const OpportunityStageHook: Hook = {
+ name: 'opportunity_stage_probability',
+ object: 'crm_opportunity',
+ events: ['beforeInsert', 'beforeUpdate'],
+ priority: 100,
+ handler: async (ctx: HookContext) => {
+ const input = ctx.input as { stage?: string; probability?: number };
+ if (input.stage === 'closed_won') input.probability = 100;
+ if (input.stage === 'closed_lost') input.probability = 0;
+ },
+};
+```
## The building blocks
-- **Hooks** run custom logic before/after record operations (create, update, delete) — the workhorse for validation-with-side-effects, denormalization, and integrations.
-- **Flows** are node-based visual logic (screen flows, autolaunched flows, scheduled flows).
-- **Workflows** model a record's lifecycle as a **finite state machine**: states, transitions, and guards.
-- **Approvals** add human sign-off steps to a process.
-- **Webhooks** deliver events to external systems over HTTP with retry semantics.
+- **Hooks** intercept **18 lifecycle events** — before/after each of find, findOne, count, aggregate, insert, update, delete, updateMany, deleteMany. Hooks support priority ordering, fire-and-forget `async` mode, CEL `condition` guards, and a `retryPolicy` with backoff ([Hooks](/docs/automation/hooks)).
+- **Flows** are node-based processes with **20 built-in node types** (decision, loop, create/update/delete record, http, notify, script, screen, wait, subflow, parallel/join gateways, …) — and the set is **registry-extensible**: plugins can contribute new node types via `registerNodeExecutor`. Flows launch on record changes, on a schedule, from a screen, or via API ([Flows](/docs/automation/flows)).
+- **Workflows** model a record's lifecycle as a **finite state machine**: states, transitions, and guards ([Workflows](/docs/automation/workflows)).
+- **Approvals** are flow nodes with approver resolution, approve/reject decisions, and escalation ([Approvals](/docs/automation/approvals)).
+- **Webhooks** deliver events to external systems through a **durable outbox** — exponential/linear/fixed retry with dead-lettering, HMAC signing, and an admin redeliver endpoint ([Webhook Delivery](/docs/automation/webhooks)).
+- **Scheduled jobs** run on `setInterval` or cron via the job service, alongside `schedule`-type flows.
Rule of thumb: model *state* with workflows, model *steps* with flows, use hooks for *code-level* reactions, and webhooks to *notify the outside world*.
## What's in this module
-
+
-
+
-
-
+
+
## Related
diff --git a/content/docs/data-modeling/field-types.mdx b/content/docs/data-modeling/field-types.mdx
index 3d6bd7507..a4861246b 100644
--- a/content/docs/data-modeling/field-types.mdx
+++ b/content/docs/data-modeling/field-types.mdx
@@ -5,7 +5,7 @@ description: Complete reference for all 48 ObjectStack field types with per-type
# Field Type Gallery
-ObjectStack provides **48 field types** covering every data modeling need — from basic text and numbers to AI vectors and rich media. This guide organizes them by category with per-type configuration details.
+ObjectStack provides **49 field types** covering every data modeling need — from basic text and numbers to AI vectors and rich media. This guide organizes them by category with per-type configuration details.
**Source:** `packages/spec/src/data/field.zod.ts`
@@ -647,7 +647,7 @@ These properties are available on **all** field types:
| `name` | `string` | **required** | Machine name (snake_case) |
| `label` | `string` | — | Human-readable display label |
| `description` | `string` | — | Field description / help text |
-| `type` | `FieldType` | **required** | One of the 48 field types |
+| `type` | `FieldType` | **required** | One of the 49 field types |
| `required` | `boolean` | `false` | Whether the field is required |
| `unique` | `boolean` | `false` | Enforce uniqueness |
| `multiple` | `boolean` | `false` | Allow array of values |
diff --git a/content/docs/data-modeling/fields.mdx b/content/docs/data-modeling/fields.mdx
index 717aa58af..fdeb4bdd6 100644
--- a/content/docs/data-modeling/fields.mdx
+++ b/content/docs/data-modeling/fields.mdx
@@ -1,11 +1,11 @@
---
title: Field Metadata
-description: Configure field types and properties — 40+ field types for text, numbers, dates, relationships, and more
+description: Configure field types and properties — 49 field types for text, numbers, dates, relationships, and more
---
# Field Metadata
-A **Field** defines an individual property within an Object. ObjectStack provides 40+ field types covering text, numbers, dates, selections, relationships, files, calculations, and specialized types like vectors and QR codes.
+A **Field** defines an individual property within an Object. ObjectStack provides 49 field types covering text, numbers, dates, selections, relationships, files, calculations, and specialized types like vectors and QR codes.
## Basic Usage
diff --git a/content/docs/data-modeling/index.mdx b/content/docs/data-modeling/index.mdx
index 6db673790..3e0d63dd3 100644
--- a/content/docs/data-modeling/index.mdx
+++ b/content/docs/data-modeling/index.mdx
@@ -1,18 +1,42 @@
---
title: Data Modeling
-description: Define objects, fields, relationships, validation, formulas, and queries — the ObjectQL layer that every other module builds on.
+description: Objects, 49 field types, relationships, validation, CEL formulas, and a compiled query AST — the ObjectQL layer every other module builds on.
---
# Data Modeling
-Data modeling is where every ObjectStack application starts: you declare **objects** (business entities), their **fields**, **relationships**, and **validation rules** as metadata, and the platform derives the database schema, APIs, permissions surface, and UI from that single definition. This module is the practical documentation for the **ObjectQL layer** — the Data Protocol described normatively in the [ObjectQL spec](/docs/protocol/objectql).
+Every ObjectStack application starts here: you declare **objects** (business entities), their **fields**, **relationships**, and **validation rules** as typed metadata, and the platform derives the database schema, REST API, permission surface, and UI from that single definition. This module is the practical documentation for the **ObjectQL layer** — the Data Protocol specified normatively in the [ObjectQL spec](/docs/protocol/objectql).
-## How it fits together
+A real object definition looks like this (from the CRM example app):
-- An **Object** is the unit of modeling (a table/collection with fields, actions, triggers, and permissions attached).
-- Queries are expressed as a JSON **AST** that the runtime validates and a **driver** compiles into SQL/NoSQL for the underlying store — the same model runs on Postgres, MySQL, SQLite, or MongoDB.
-- Validation and **formula (CEL) expressions** live in the metadata, so they are enforced identically in the API, UI, and automation layers.
-- Data that lives outside ObjectStack can join the model through **external datasources** (federation).
+```typescript
+import { ObjectSchema, Field } from '@objectstack/spec/data';
+
+export const Lead = ObjectSchema.create({
+ name: 'crm_lead',
+ label: 'Lead',
+ pluralLabel: 'Leads',
+ icon: 'funnel',
+ description: 'An inbound prospect not yet qualified as an opportunity.',
+ fields: {
+ name: Field.text({ label: 'Lead Name', required: true, searchable: true, maxLength: 200 }),
+ email: Field.email({ label: 'Email', searchable: true }),
+ phone: Field.phone({ label: 'Phone' }),
+ company: Field.text({ label: 'Company', searchable: true, maxLength: 200 }),
+ },
+});
+```
+
+That one definition is enough to get a persisted table, CRUD + query endpoints, permission checks, and generated list views and forms.
+
+## What the data layer actually gives you
+
+- **49 field types** — from `text`, `currency`, and `lookup`/`master_detail` relationships to `formula`, `summary`, `signature`, `qrcode`, and `vector` (with HNSW/IVFFlat index config for AI embeddings). See the [Field Types gallery](/docs/data-modeling/field-types).
+- **Validation as metadata** — required/format rules, CEL script validation with access to `previous.`, uniqueness via indexes, and severity levels — enforced identically in API, UI, and automation.
+- **CEL expressions** — formula fields and computed defaults share one expression language ([Expressions](/docs/data-modeling/formulas)).
+- **A compiled query AST** — queries are JSON documents validated against the protocol, then compiled by a driver into native queries with joins, aggregations, window functions, HAVING, and subqueries. The [query cheat sheet](/docs/data-modeling/queries) covers the syntax; the [spec](/docs/protocol/objectql/query-syntax) is normative.
+- **Four database drivers in this repo** — `driver-sql` (PostgreSQL / MySQL / SQLite via Knex), `driver-mongodb`, `driver-memory` (in-memory, for tests and demos), and `driver-sqlite-wasm` (SQLite in the browser / WebContainers). The same model runs unchanged on any of them.
+- **External datasource federation** — introspect an existing external database, import selected tables into the catalog, and query them alongside native objects ([External Datasources](/docs/data-modeling/external-datasources)).
## What's in this module
@@ -20,15 +44,15 @@ Data modeling is where every ObjectStack application starts: you declare **objec
-
+
-
+
-
+
diff --git a/content/docs/getting-started/quick-reference.mdx b/content/docs/getting-started/quick-reference.mdx
index a826f54e0..552e99e71 100644
--- a/content/docs/getting-started/quick-reference.mdx
+++ b/content/docs/getting-started/quick-reference.mdx
@@ -18,7 +18,7 @@ Core business logic and data modeling schemas.
| Protocol | Source File | Key Schemas | Purpose |
|:---------|:-----------|:------------|:--------|
-| **[Field](/docs/references/data/field)** | `field.zod.ts` | Field, FieldType, SelectOption | 44 field types for data modeling |
+| **[Field](/docs/references/data/field)** | `field.zod.ts` | Field, FieldType, SelectOption | 49 field types for data modeling |
| **[Object](/docs/references/data/object)** | `object.zod.ts` | Object, ObjectCapabilities | Object/table definitions |
| **[Query](/docs/references/data/query)** | `query.zod.ts` | Query, QueryOptions | Query AST with joins, aggregations |
| **[Filter](/docs/references/data/filter)** | `filter.zod.ts` | QueryFilter, FilterCondition | Advanced filtering operators |
diff --git a/content/docs/index.mdx b/content/docs/index.mdx
index d268a1ee8..a9fb7a0fc 100644
--- a/content/docs/index.mdx
+++ b/content/docs/index.mdx
@@ -5,7 +5,9 @@ description: Technical documentation for ObjectStack.
# ObjectStack Documentation
-ObjectStack is an AI-native business backend protocol for structured, auditable business applications. You declare your application — data model, logic, permissions, and UI — as metadata, and the platform derives the database, APIs, automation, and interface from that single artifact.
+ObjectStack is an AI-native business backend protocol for structured, auditable business applications. You author your application — data model, logic, permissions, and UI — as typed metadata in TypeScript (`defineStack()`), compile it to a self-contained artifact (`objectstack.json`), and the runtime derives everything else: the database schema across four interchangeable drivers, a generated REST + realtime API, permission-checked automation, and server-driven UI. AI agents act through the same typed, permission-aware surface — never through raw SQL or scraped UI.
+
+One protocol definition, 15 namespaces, 49 field types, one artifact — every engine (SQL, React, MCP) is a consumer of the same source of truth.
## Start here
diff --git a/content/docs/kernel/index.mdx b/content/docs/kernel/index.mdx
index 0e6d5f807..948fd02cf 100644
--- a/content/docs/kernel/index.mdx
+++ b/content/docs/kernel/index.mdx
@@ -1,19 +1,30 @@
---
title: Kernel & Services
-description: The ObjectKernel runtime — plugin host, event bus, service registry, and the services.* APIs your hooks and plugins call.
+description: The ObjectKernel runtime — plugin host, event bus, service registry, and the versioned services.* APIs your hooks and plugins call.
---
# Kernel & Services
-The kernel is ObjectStack's runtime: it loads your metadata artifact, hosts plugins, wires up services, and enforces the lifecycle that keeps the system stable. This module documents the **ObjectOS layer** in practice — the [System Protocol](/docs/protocol/objectos) is its normative spec. If you write hooks or plugins, this is where the APIs you call (`services.data`, `services.email`, `services.queue`, …) and the contracts they implement are documented.
+The kernel is ObjectStack's runtime: it loads your metadata artifact, hosts plugins, wires up services, and enforces the lifecycle that keeps the system stable. This module documents the **ObjectOS layer** in practice — the [System Protocol](/docs/protocol/objectos) is its normative spec. If you write hooks or plugins, this is where the APIs you call and the contracts they implement are documented.
## How it fits together
- The **ObjectKernel** boots through a strict lifecycle and exposes a **service registry**; everything else — data engine, auth, storage — is a service registered by a plugin.
- **Events & hooks** are the kernel-level extension points plugins use to react to system activity.
-- **Runtime services** (`services.*`) are the stable, versioned API surface exposed to hook and action bodies.
-- **Service contracts** (`IDataEngine`, `IAuthService`, `ICacheService`, `IMetadataService`, `IStorageService`) define what any implementation must provide, so services are swappable.
-- **Cluster semantics** describe how the runtime behaves when scaled beyond a single node.
+- **Runtime services** (`services.*`) are the stable, versioned API surface exposed to hook and action bodies:
+
+| API | Stability | What it does |
+| :--- | :--- | :--- |
+| [`services.data`](/docs/kernel/runtime-services/data-service) | stable | CRUD and queries with the caller's permission context |
+| [`services.sharing`](/docs/kernel/runtime-services/sharing-service) | stable | `buildReadFilter`, `canEdit`, `grant`/`revoke`, `listShares` |
+| [`services.email`](/docs/kernel/runtime-services/email-service) | stable | `send`, `sendTemplate` |
+| [`services.queue`](/docs/kernel/runtime-services/queue-service) | stable | Background work and queues |
+| [`services.settings`](/docs/kernel/runtime-services/settings-service) | stable | App/environment settings |
+| [`services.storage`](/docs/kernel/runtime-services/storage-service) | stable | File storage |
+| [`services.audit`](/docs/kernel/runtime-services/audit-service) | experimental | Append-only audit sink (query history via `services.data`) |
+
+- **Service contracts** (28 interface files in `packages/spec/src/contracts/` — `IDataEngine`, `IAuthService`, `ICacheService`, `IMetadataService`, `IStorageService`, …) define what any implementation must provide, so services are swappable. Fifteen runtime service packages ship in this repo (cache, cluster, datasource, i18n, job, knowledge, messaging, realtime, …).
+- **Cluster semantics** define how the runtime behaves beyond a single node — including the per-partition locking the webhook outbox relies on.
## What's in this module
@@ -22,7 +33,7 @@ The kernel is ObjectStack's runtime: it loads your metadata artifact, hosts plug
-
+
diff --git a/content/docs/permissions/index.mdx b/content/docs/permissions/index.mdx
index 6fab4973b..4691c2efa 100644
--- a/content/docs/permissions/index.mdx
+++ b/content/docs/permissions/index.mdx
@@ -9,7 +9,34 @@ This module covers authentication, authorization, and record- and field-level
access control. It is a cross-protocol capability: enforced by the ObjectOS
runtime, declared as ObjectQL security metadata.
-> **Implementation status — Phase-1 RBAC is live.** REST → ObjectQL now propagates a populated `ExecutionContext` (userId, tenantId, roles, permissions) into the SecurityPlugin middleware, so CRUD / FLS / RLS checks actually fire on every authenticated request. The default `member_default` permission set ships a wildcard RLS rule `organization_id == current_user.organization_id` plus explicit per-object overrides `sys_organization_self` (`id == current_user.organization_id`) and `sys_user_self` (`id == current_user.id`) for the two global tables that lack an `organization_id` column. RLS expressions, the physical column, and `RLSUserContext.organization_id` all use the same canonical name — there is no `tenantField` rewrite indirection (schemas with a different physical tenant column should fork the defaults). The legacy `objectql.registerTenantMiddleware` has been removed; SecurityPlugin is the sole authority for tenant isolation. Analytics also reuses the same read scope: `@objectstack/service-analytics` auto-bridges to `security.getReadFilter(object, context)` when the security service is registered, so dataset-bound dashboards/reports do not bypass RLS. End-to-end verified on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic is denied by default** (the ADR-0056 D2 default-deny flip landed: `requireAuth` defaults to `true`); an explicit `requireAuth: false` opt-out logs a boot-time warning, and public forms self-authorize via a declaration-derived `publicFormGrant` (ADR-0056 Option A — see [Public Forms](/docs/ui/forms)). Organization-Wide Defaults (`private` / `public_read` / `public_read_write` / `controlled_by_parent`) and Sharing Rules (owner + criteria, with `role_and_subordinates` hierarchy widening) are live and dogfood-proven (ADR-0056); the Studio RLS visual editor, per-user×org permission cache, and audit UI for denied access are queued. See `CHANGELOG.md` and `concepts/implementation-status.mdx` for the latest matrix.
+The model has five layers, each with schema **and** runtime support: **profiles**
+(one per user) and additive **permission sets** for object/field rights, a **role
+hierarchy** for reporting structure, **sharing rules + Organization-Wide Defaults**
+for record visibility, **row-level security** (compiled into every query), and
+**field-level security** (masked server-side, fail-closed). Object access scopes
+go from `own` through `own_and_reports`, `unit`, `unit_and_below`, to `org`.
+
+Access rules are metadata like everything else — this is a real sharing rule from
+the CRM example app:
+
+```typescript
+export const HighValueOpportunitySharingRule = defineSharingRule({
+ type: 'criteria',
+ name: 'share_high_value_opps_with_managers',
+ label: 'High-Value Deals → Sales Managers',
+ description: 'Automatically share opportunities over $100,000 with all Sales Managers.',
+ object: 'crm_opportunity',
+ condition: 'record.amount > 100000',
+ accessLevel: 'edit',
+ sharedWith: { type: 'role', value: 'sales_manager' },
+ active: true,
+});
+```
+
+Because AI agents act through the same permission-aware surface, these rules bound
+agent access exactly as they bound users ([Actions as Tools](/docs/ai/actions-as-tools)).
+
+> **Implementation status — Phase-1 RBAC is live.** REST → ObjectQL now propagates a populated `ExecutionContext` (userId, tenantId, roles, permissions) into the SecurityPlugin middleware, so CRUD / FLS / RLS checks actually fire on every authenticated request. The default `member_default` permission set ships a wildcard RLS rule `organization_id == current_user.organization_id` plus explicit per-object overrides `sys_organization_self` (`id == current_user.organization_id`) and `sys_user_self` (`id == current_user.id`) for the two global tables that lack an `organization_id` column. RLS expressions, the physical column, and `RLSUserContext.organization_id` all use the same canonical name — there is no `tenantField` rewrite indirection (schemas with a different physical tenant column should fork the defaults). The legacy `objectql.registerTenantMiddleware` has been removed; SecurityPlugin is the sole authority for tenant isolation. Analytics also reuses the same read scope: `@objectstack/service-analytics` auto-bridges to `security.getReadFilter(object, context)` when the security service is registered, so dataset-bound dashboards/reports do not bypass RLS. End-to-end verified on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic is denied by default** (the ADR-0056 D2 default-deny flip landed: `requireAuth` defaults to `true`); an explicit `requireAuth: false` opt-out logs a boot-time warning, and public forms self-authorize via a declaration-derived `publicFormGrant` (ADR-0056 Option A — see [Public Forms](/docs/ui/forms)). Organization-Wide Defaults (`private` / `public_read` / `public_read_write` / `controlled_by_parent`) and Sharing Rules (owner + criteria, with `role_and_subordinates` hierarchy widening) are live and dogfood-proven (ADR-0056); the Studio RLS visual editor, per-user×org permission cache, and audit UI for denied access are queued. See `CHANGELOG.md` and [Implementation Status](/docs/releases/implementation-status) for the latest matrix.
## What's in this module
diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx
index ec4659b95..ece83adb1 100644
--- a/content/docs/references/index.mdx
+++ b/content/docs/references/index.mdx
@@ -43,7 +43,7 @@ Defines the "Shape of Data" and business logic.
| File | Schema | Purpose |
| :--- | :--- | :--- |
-| `field.zod.ts` | `FieldSchema` | Field definitions with 44 types (text, number, select, lookup, formula, vector, location, etc.) |
+| `field.zod.ts` | `FieldSchema` | Field definitions with 49 types (text, number, select, lookup, formula, vector, location, etc.) |
| `object.zod.ts` | `ObjectSchema` | Object/table definitions with fields, indexes, and capabilities |
| `query.zod.ts` | `QuerySchema` | Abstract query AST supporting window functions, HAVING, DISTINCT, subqueries |
| `validation.zod.ts` | `ValidationRuleSchema` | Validation rules for data integrity |
@@ -63,7 +63,7 @@ Defines the "Shape of Data" and business logic.
| `driver/mongo.zod.ts` | `MongoConfigSchema` | MongoDB driver configuration |
**Key Features:**
-- 44 field types including AI/ML vectors and GPS locations
+- 49 field types including AI/ML vectors and GPS locations
- Advanced query capabilities (window functions, HAVING, DISTINCT, subqueries)
- Validation rules and formulas
- Lifecycle hooks for business logic
diff --git a/content/docs/ui/index.mdx b/content/docs/ui/index.mdx
index b512e1951..ecae436da 100644
--- a/content/docs/ui/index.mdx
+++ b/content/docs/ui/index.mdx
@@ -1,27 +1,47 @@
---
title: UI Engine
-description: Apps, pages, views, dashboards, and forms — server-driven UI declared as metadata and rendered by the ObjectUI runtime.
+description: Apps, 9 view render types, dashboards, themes, and public portals — server-driven UI declared as metadata and rendered by the ObjectUI runtime.
---
# UI Engine
-The UI engine turns metadata into user interfaces: you declare **apps**, **pages**, **views**, and **dashboards** as data, and any compliant renderer draws them. This module is the practical documentation for the **ObjectUI layer** — the UI Protocol described normatively in the [ObjectUI spec](/docs/protocol/objectui). Because the UI is data, it ships inside the same artifact as your schema, respects the same permissions, and can be generated or modified by AI agents through the same typed surface.
+The UI engine turns metadata into user interfaces: you declare **apps**, **views**, **pages**, and **dashboards** as data, and any compliant renderer draws them. This module is the practical documentation for the **ObjectUI layer** — the UI Protocol specified normatively in the [ObjectUI spec](/docs/protocol/objectui). Because the UI is data, it ships inside the same artifact as your schema, respects the same permissions, and can be generated or modified by AI agents through the same typed surface.
+
+An app is a navigation shell over your objects (from the CRM example app):
+
+```typescript
+import { App } from '@objectstack/spec/ui';
+
+export const CrmApp = App.create({
+ name: 'crm_app',
+ label: 'CRM',
+ icon: 'briefcase',
+ branding: { primaryColor: '#2563EB' },
+ navigation: [
+ { id: 'group_sales', type: 'group', label: 'Sales', icon: 'briefcase', children: [
+ { id: 'nav_leads', type: 'object', objectName: 'crm_lead', label: 'Leads', icon: 'funnel' },
+ { id: 'nav_accounts', type: 'object', objectName: 'crm_account', label: 'Accounts', icon: 'building' },
+ ]},
+ ],
+});
+```
## The building blocks
-- An **App** groups navigation, branding, and entry points for one audience.
-- **Views** present object records as list, form, kanban, or calendar; **Pages** compose free-form layouts from widgets.
-- **Dashboards** combine charts and datasets for analytics.
-- **Forms** capture data — both internal record forms and public, unauthenticated collection forms.
-- The **Setup App** is the platform's built-in administration UI, itself rendered from the same protocol.
+- **Apps** group navigation, branding, and entry points for one audience.
+- **Views** present object records in **9 render types**: `grid` (the standard data table), `kanban`, `gallery`, `calendar`, `timeline`, `gantt`, `map`, `chart`, and `tree`. Forms are their own view kind with per-mode layouts ([Views](/docs/ui/views)).
+- **Pages** compose free-form layouts from widgets; **Dashboards** combine charts, reports, and datasets for analytics.
+- **Themes** define palettes, typography, and spacing as metadata — the CRM example ships light and dark theme variants.
+- **Portals** open a scoped slice of the app to external audiences, including **anonymous entry routes** for public data collection ([Forms](/docs/ui/forms), [public data collection](/docs/ui/public-data-collection)).
+- The **Setup App** — the platform's built-in administration UI — is itself rendered from the same protocol ([Setup App](/docs/ui/setup-app)).
## What's in this module
+
-
-
+
@@ -33,7 +53,7 @@ The UI engine turns metadata into user interfaces: you declare **apps**, **pages
-
+
## Related