Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion content/docs/api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ ObjectStack exposes a fully typed REST API. All endpoints use JSON request/respo
| :--- | :--- |
| **REST** | ✅ Auto-generated from the protocol (`@objectstack/rest`) — CRUD, query, batch, metadata, packages |
| **Realtime** | ⚠️ In-process pub/sub service (`@objectstack/service-realtime`, single-instance); the `/realtime/*` REST routes and WebSocket/SSE transport are plugin-provided — none ships in the open framework |
| **MCP** | ✅ Non-system objects and their actions exposed as Model Context Protocol tools, gated by the caller's permissions/RLS rather than a per-action opt-in flag ([AI module](/docs/ai)) |
| **MCP** | ✅ Non-system objects exposed automatically as Model Context Protocol tools; actions additionally require the author's `ai.exposed` opt-in — every call is gated by the caller's permissions/RLS ([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 |

Expand Down Expand Up @@ -58,6 +58,23 @@ sequenceDiagram
See [Actions as Tools](/docs/ai/actions-as-tools) for the `run_action` bridge and the
[MCP reference](/docs/references/ai/mcp) for binding external MCP servers into your agents.

## Authentication

Every REST call runs as a principal — anonymous requests only see what your
permission model grants anonymous users. Two ways to authenticate:

- **Session cookie** (browsers, quick local tests): `POST /api/v1/auth/sign-in/email`
with `{ "email": "…", "password": "…" }` sets the session cookie — reuse it with
`curl -c cookies.txt` / `-b cookies.txt`. On a fresh dev database the seeded
admin is `admin@objectos.ai` / `admin123`.
- **API key** (scripts, CI, headless agents): mint one with `POST /api/v1/keys`
(the key is shown once), or from **Setup → Connect an Agent** in the Console.
Send it as `x-api-key: osk_…` or `Authorization: Bearer osk_…`.

See [Authentication](/docs/permissions/authentication) for the full identity
surface (OAuth flows, sessions, providers) and
[Plugin Endpoints](/docs/api/plugin-endpoints) for the auth route catalog.

## What's in this module

- [Data API](/docs/api/data-api) — CRUD, batch operations, record cloning, and analytics queries
Expand Down
2 changes: 1 addition & 1 deletion content/docs/data-modeling/field-type-decision-tree.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ flowchart TD
|:---|:---|:---|
| `number` | Any numeric value (set `precision`/`scale` for decimals) | `price`, `weight`, `quantity` |
| `currency` | Money amounts with currency code | `amount`, `total_price` |
| `percent` | Percentage values (0–100 or 0–1) | `completion`, `discount` |
| `percent` | Percentage values, stored as fractions 0–1 (`0.85` = 85%) | `completion`, `discount` |
| `rating` | Star ratings (typically 1–5) | `satisfaction`, `difficulty` |
| `slider` | Numeric value via slider UI | `volume`, `priority_level` |

Expand Down
4 changes: 2 additions & 2 deletions content/docs/data-modeling/field-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ Reference to a record in another object (foreign key).
| Property | Type | Default | Description |
|:---|:---|:---|:---|
| `reference` | `string` | **required** | Target object name (snake_case) |
| `referenceFilters` | `string[]` | — | Filters applied to lookup dialogs (e.g. `"active = true"`) |
| `referenceFilters` | `string[]` | — | **Legacy** — accepted by the schema but not read by the record-picker (filters nothing). Use structured `lookupFilters` + `dependsOn` instead; see [Relationships](/docs/data-modeling/relationships) |
| `deleteBehavior` | `'restrict' \| 'cascade' \| 'set_null'` | `'set_null'` | Behavior when referenced record is deleted (a *required* lookup left at the default `set_null` is escalated to `restrict`, since a NOT NULL foreign key cannot be cleared) |

```typescript
Expand All @@ -323,7 +323,7 @@ Parent-child relationship (cascading delete by default).
| Property | Type | Default | Description |
|:---|:---|:---|:---|
| `reference` | `string` | **required** | Target (master) object name |
| `referenceFilters` | `string[]` | — | Filters applied to lookup dialogs (e.g. `"active = true"`) |
| `referenceFilters` | `string[]` | — | **Legacy** — accepted by the schema but not read by the record-picker (filters nothing). Use structured `lookupFilters` + `dependsOn` instead; see [Relationships](/docs/data-modeling/relationships) |
| `deleteBehavior` | `'restrict' \| 'cascade' \| 'set_null'` | `'cascade'` | Behavior when parent is deleted (master-detail cascades unless set to `restrict`) |
| `inlineEdit` | `boolean \| 'grid' \| 'form'` | — | Edit child records inline on the parent create/edit form (`true` = auto-pick, `'grid'`, or `'form'`) |
| `inlineColumns` | `array` | — | Optional explicit inline grid columns |
Expand Down
10 changes: 7 additions & 3 deletions content/docs/data-modeling/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ notes: Field.markdown({ label: 'Notes' }),
```typescript
quantity: Field.number({ label: 'Quantity', min: 0, max: 10000, step: 1 }),
price: Field.currency({ label: 'Price', scale: 2, min: 0 }),
discount: Field.percent({ label: 'Discount', scale: 2, min: 0, max: 100 }),
// percent stores a fraction: 0.15 = 15% (the UI renders it as a percentage)
discount: Field.percent({ label: 'Discount', scale: 2, min: 0, max: 1 }),
```

**Currency Configuration:**
Expand Down Expand Up @@ -145,10 +146,13 @@ account: Field.lookup('account', {
required: true,
}),

// Filtered lookup
// Filtered lookup — structured filters, cascading from another field
primary_contact: Field.lookup('contact', {
label: 'Primary Contact',
referenceFilters: ['account = {account}', 'is_active = true'],
dependsOn: ['account'],
lookupFilters: [
{ field: 'is_active', operator: 'eq', value: true },
],
}),

// Master-detail (cascade delete)
Expand Down
2 changes: 1 addition & 1 deletion content/docs/data-modeling/validation-rules.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ These properties apply to **all** field types and are validated by the base `Fie
| Property | Type | Default | Validation Behavior |
|:---|:---|:---|:---|
| `reference` | `string` | — | **Required.** Target object name |
| `referenceFilters` | `string[]` | — | Filter expressions for lookup dialogs |
| `referenceFilters` | `string[]` | — | **Legacy** — schema-accepted but not read by the record-picker; use `lookupFilters` + `dependsOn` |
| `deleteBehavior` | `enum` | `set_null` | `set_null`, `cascade`, or `restrict` |
| `multiple` | `boolean` | `false` | Allow multiple references |

Expand Down
5 changes: 3 additions & 2 deletions content/docs/deployment/backup-restore.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ sqlite3 /srv/data/app.db ".backup '/backups/app-$(date +%F).db'"
The default location when `OS_DATABASE_URL` is unset is
`<home>/data/objectstack.db` under the ObjectStack home directory.

### Turso / libSQL and managed databases
### Managed databases (hosted Postgres, MongoDB Atlas, …)

Use the provider's snapshot, branching, or point-in-time-restore facilities.
Nothing ObjectStack-specific applies.
Nothing ObjectStack-specific applies. (Turso/libSQL applies only to ObjectStack
Cloud-managed environments — the open framework does not ship that driver.)

## Backing up uploaded files

Expand Down
18 changes: 16 additions & 2 deletions content/docs/deployment/self-hosting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ workable default:

| Variable | Why it must be set |
|:---|:---|
| `OS_DATABASE_URL` | Without it, data lands in a SQLite file under the ObjectStack home directory (`~/.objectstack`, or `<cwd>/.objectstack` next to a project config) — fine for one box, wrong for containers. Use `postgres://…`, `libsql://…`, or a mounted `file:…` path. |
| `OS_DATABASE_URL` | Without it, data lands in a SQLite file under the ObjectStack home directory (`~/.objectstack`, or `<cwd>/.objectstack` next to a project config) — fine for one box, wrong for containers. Use `postgres://…`, `mongodb://…`, or a mounted `file:…` path (`libsql://` / Turso is **not** supported by the open framework — that driver ships in ObjectStack Cloud). |
| `OS_AUTH_SECRET` | Session secret for the auth plugin (`AUTH_SECRET` is the legacy alias). Without it, `/api/v1/auth/*` is **silently skipped** — the server runs unauthenticated. |
| `OS_SECRET_KEY` | 32-byte master key encrypting every stored secret (`openssl rand -hex 32`). On a container's ephemeral filesystem the auto-minted key is **lost on restart**, making previously-encrypted secrets undecryptable. |
| `OS_PORT` | `os start` **fails loudly** if the port is busy (it never auto-shifts like `os dev`). Pin it and keep your reverse-proxy upstream in sync. |
Expand Down Expand Up @@ -117,7 +117,7 @@ straight from release storage instead of a mount.)
For a self-contained deployable image, extend it. The Dockerfile below (plus
the compose stack in the next section and a `.dockerignore`) ships
ready-to-copy in
[`examples/docker`](https://github.com/objectstack-ai/framework/tree/main/examples/docker)
[`docker/`](https://github.com/objectstack-ai/framework/tree/main/docker)
— drop the files into your scaffolded project.

```dockerfile title="Dockerfile"
Expand Down Expand Up @@ -314,6 +314,20 @@ decrypt each other's secrets. All replicas must share the same
`OS_SECRET_KEY`, `OS_AUTH_SECRET`, and database. See
[Cluster](/docs/kernel/cluster).

## First boot: create the admin

On a fresh production database there are no users yet. Open the deployment's
root URL and **sign up — the very first account to register becomes the
bootstrap admin** (this works even with `OS_DISABLE_SIGNUP=true`, which only
blocks sign-ups after that first account exists). Do this immediately after
the first deploy, before sharing the URL; then create your real user accounts
and lock sign-up down via `OS_AUTH_SIGNUP_ENABLED` /
[SSO](/docs/permissions/sso) as policy dictates.

Note the production server seeds **no** dev credentials — the
`admin@objectos.ai` / `admin123` account you may know from `os dev` exists only
on empty development databases.

## Go-live

Before pointing real users at the deployment, walk the
Expand Down
7 changes: 7 additions & 0 deletions content/docs/deployment/vercel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ export async function ensureApp(): Promise<Hono> {
}
```

<Callout type="warn">
`InMemoryDriver` keeps this snippet self-contained, but on serverless **all data
is lost on every cold start** — fine for a demo, wrong for anything real. Before
going past a proof of concept, swap the driver for a real database (see
[Database Drivers](/docs/data-modeling/drivers)) and point `OS_DATABASE_URL` at it.
</Callout>

**2. Create the API entrypoint** (`api/index.ts`):

```typescript
Expand Down
12 changes: 9 additions & 3 deletions content/docs/getting-started/build-with-claude-code.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,12 @@ Validation proves the metadata is *well-formed*. It can't prove the app does wha
*you* meant. That's the human half of the loop: **run it and look.**

```bash
os dev --ui
npx os dev --ui
```

Open [http://localhost:3000/_console/](http://localhost:3000/_console/) and drive
the app like a user:
Open [http://localhost:3000/_console/](http://localhost:3000/_console/) and sign in
as the dev admin the server seeds on an empty database
(`admin@objectos.ai` / `admin123`). Then drive the app like a user:

- Create a ticket. Confirm the fields, labels, and picklist colors match intent.
- Check the **Resolve** action shows on an open ticket — and **disappears** once
Expand Down Expand Up @@ -316,7 +317,12 @@ its own OAuth 2.1 authorization server, so interactive clients just open a
browser login (you connect as yourself, no admin-minted credentials):

```bash
# the dev server you just booted in step 5
claude mcp add --transport http support-desk http://localhost:3000/api/v1/mcp

# or a deployed instance
claude mcp add --transport http support-desk https://your-deployment.example.com/api/v1/mcp

# first tool use opens a browser login — you're connected as yourself
```

Expand Down
16 changes: 16 additions & 0 deletions content/docs/getting-started/common-patterns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ This guide covers the most common patterns you will use when building applicatio
**Import:** `import { defineStack, defineView, defineApp, defineFlow, defineAgent } from '@objectstack/spec'`
</Callout>

<Callout type="warn">
**Before you copy:** the examples use short object names (`project`, `order`) for
readability. In a real project, `os validate` additionally enforces your project's
**namespace prefix** on every object name (`my_app_project`) and an explicit
**`sharingModel`** on every custom object (ADR-0090) — the patterns below include
`sharingModel`; remember to add your prefix. See
[Validating Metadata](/docs/getting-started/validating-metadata).
</Callout>

---

## 1. CRUD Object Definition
Expand All @@ -27,6 +36,7 @@ export default defineStack({
{
name: 'project',
label: 'Project',
sharingModel: 'public_read_write',
fields: {
title: { label: 'Title', type: 'text', required: true, maxLength: 200 },
description: { label: 'Description', type: 'textarea' },
Expand All @@ -53,6 +63,7 @@ export default defineStack({
objects: {
project: {
label: 'Project',
sharingModel: 'public_read_write',
fields: {
title: { label: 'Title', type: 'text', required: true, maxLength: 200 },
description: { label: 'Description', type: 'textarea' },
Expand Down Expand Up @@ -85,6 +96,7 @@ Create a parent-child relationship between a master and its detail records. Note
{
name: 'order',
label: 'Order',
sharingModel: 'public_read',
fields: {
order_number: { label: 'Order #', type: 'autonumber', autonumberFormat: 'ORD-{0000}' },
customer: { label: 'Customer', type: 'lookup', reference: 'contact' },
Expand All @@ -106,8 +118,10 @@ Create a parent-child relationship between a master and its detail records. Note
}
},
{
// Master-detail child: access is derived from the parent order record
name: 'order_line',
label: 'Order Line',
sharingModel: 'controlled_by_parent',
fields: {
order: {
label: 'Order',
Expand Down Expand Up @@ -265,6 +279,7 @@ Define a multi-step approval flow for records.
objects: [{
name: 'expense_report',
label: 'Expense Report',
sharingModel: 'private',
fields: {
title: { label: 'Title', type: 'text', required: true },
amount: { label: 'Amount', type: 'currency' },
Expand Down Expand Up @@ -402,6 +417,7 @@ Restrict field visibility and editability based on user profiles.
objects: [{
name: 'employee',
label: 'Employee',
sharingModel: 'private',
fields: {
name: { label: 'Name', type: 'text', required: true },
email: { label: 'Email', type: 'email', required: true },
Expand Down
13 changes: 9 additions & 4 deletions content/docs/getting-started/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,21 @@ Before you start, make sure you have the following installed:
| Tool | Minimum Version | Check Command |
|:---|:---|:---|
| **Node.js** | 18.0.0+ | `node --version` |
| **pnpm** | 8.0.0+ (repo pins 10.x) | `pnpm --version` |
| **TypeScript** | 5.3.0+ (project targets 6.x) | `npx tsc --version` |

That's everything the standard path needs — `npx create-objectstack my-app`
scaffolds a project with its own TypeScript toolchain and npm scripts.

<Callout type="info">
**Why pnpm?** ObjectStack uses pnpm workspaces for monorepo management. Install it with `npm install -g pnpm` or `corepack enable` (corepack will pull the pinned pnpm 10.x from the repo's `packageManager` field).
**pnpm and TypeScript versions only matter for the framework monorepo.** If you
clone [`objectstack-ai/framework`](https://github.com/objectstack-ai/framework)
itself (to contribute, or to run the bundled examples), you'll additionally need
pnpm 8+ (`corepack enable` pulls the pinned 10.x) — and the repo builds against
TypeScript 6.x.
</Callout>

## Common Setup Issues

### pnpm not found
### pnpm not found (framework monorepo only)
```bash
# Install pnpm globally
npm install -g pnpm
Expand Down
4 changes: 2 additions & 2 deletions content/docs/getting-started/quick-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ Runtime environment, logging, jobs, caching, and observability.
| **[Cache](/docs/references/system/cache)** | `cache.zod.ts` | CacheConfig | Caching layer |
| **[Change Management](/docs/references/system/change-management)** | `change-management.zod.ts` | ChangeManagement | Change tracking |
| **[Collaboration](/docs/references/system/collaboration)** | `collaboration.zod.ts` | Collaboration | Real-time collab |
| **[Compliance](/docs/references/system/compliance)** | `compliance.zod.ts` | Compliance | Regulatory controls |
| **Compliance** | `compliance.zod.ts` | Compliance | Regulatory controls |
| **[Encryption](/docs/references/system/encryption)** | `encryption.zod.ts` | Encryption | Encryption & keys |
| **[HTTP Server](/docs/references/system/http-server)** | `http-server.zod.ts` | HTTPServer | HTTP server config |
| **[Job](/docs/references/system/job)** | `job.zod.ts` | Job, JobSchedule | Background job queue |
| **[Logging](/docs/references/system/logging)** | `logging.zod.ts` | LoggingConfig | Structured logging |
| **[Masking](/docs/references/system/masking)** | `masking.zod.ts` | Masking | Data masking |
| **Masking** | `masking.zod.ts` | Masking | Data masking |
| **[Message Queue](/docs/references/system/message-queue)** | `message-queue.zod.ts` | MessageQueue | Message queuing |
| **[Metadata Persistence](/docs/references/system/metadata-persistence)** | `metadata-persistence.zod.ts` | MetadataPersistence | Metadata storage |
| **[Metrics](/docs/references/system/metrics)** | `metrics.zod.ts` | Metrics | Application metrics |
Expand Down
4 changes: 2 additions & 2 deletions content/docs/getting-started/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ Reading the metadata is half of verification; running the app is the other half.
Two commands, both of which the agent runs for you but you can run yourself:

```bash
os validate # the gate — schema + CEL predicates + widget bindings (no artifact)
os dev --ui # boot the app, then open http://localhost:3000/_console/
npx os validate # the gate — schema + CEL predicates + widget bindings (no artifact)
npx os dev --ui # boot the app, then open http://localhost:3000/_console/
```

`os validate` proves the metadata is *well-formed*; the **Console** at `/_console/`
Expand Down
8 changes: 8 additions & 0 deletions content/docs/kernel/services-checklist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ description: Complete inventory of ObjectStack kernel services with protocol met

# Kernel Services Checklist

<Callout type="warn">
**This checklist is out of date.** It predates several packages that have since
shipped — SQL and MongoDB drivers, automation, realtime, storage, and more (see
[Plugins & Packages](/docs/plugins/packages) for the current catalog). Keep it
for the protocol-method inventory; treat the per-service status columns as
historical until this page is regenerated.
</Callout>

The ObjectStack protocol defines **17 kernel services** registered via the `CoreServiceName` enum. Each service maps to a set of protocol methods (57 total), governed by the `ObjectStackProtocol` interface.

**Key architecture principle**: The kernel itself only provides **data** and **metadata** (framework). Everything else — including **auth** and **automation** — is delivered by **plugins**. The current `@objectstack/objectql` package is an example kernel implementation to get the basic API running; production kernels will be rebuilt as new plugins.
Expand Down
3 changes: 2 additions & 1 deletion content/docs/permissions/permission-metadata.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ contextVariables: {

The built-in context variables available in RLS `using`/`check` clauses include
`current_user.id`, `current_user.organization_id`, and `current_user.positions`
(ADR-0068). `roles` is a **string array** — the only canonical role field — so test
(ADR-0068). `positions` is a **string array** — the canonical assignment field
(D3 reserves the word "role"; there is no `current_user.roles`) — so test
membership with CEL: `'org_admin' in current_user.positions` or
`current_user.positions.exists(p, p == 'sales_manager')`. The framework-seeded
built-in position names are `platform_admin`, `org_owner`, `org_admin`, and
Expand Down
Loading
Loading