okengine 0.18.3 → 0.18.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -3
- package/README.md +5 -5
- package/package.json +2 -2
- package/site/content/docs/ai/index.mdx +25 -6
- package/site/content/docs/ai/mcp.mdx +10 -1
- package/site/content/docs/ai/skills.mdx +9 -9
- package/site/content/docs/elements/ai.mdx +1 -1
- package/site/content/docs/elements/gate.mdx +3 -2
- package/site/content/docs/elements/index.mdx +42 -11
- package/site/content/docs/elements/store.mdx +2 -0
- package/site/content/docs/get-started/basic-usage.mdx +3 -10
- package/site/content/docs/get-started/index.mdx +1 -1
- package/site/content/docs/get-started/installation.mdx +1 -1
- package/site/content/docs/get-started/introduction.mdx +69 -64
- package/site/content/docs/get-started/meta.json +1 -1
- package/site/content/docs/get-started/project-structure.mdx +4 -11
- package/site/content/docs/get-started/why.mdx +64 -114
- package/site/content/docs/index.mdx +69 -15
- package/site/content/docs/meta.json +10 -5
- package/site/content/docs/providers/index.mdx +2 -0
- package/site/content/docs/recipes/index.mdx +2 -0
- package/src/cli/competitor-mention-removal.test.ts +1 -1
- package/src/compiler/extract.test.ts +1 -1
- package/src/console/ui-next/dist/assets/{access-page-DY4N6nnk.js → access-page-3-EFj-2G.js} +1 -1
- package/src/console/ui-next/dist/assets/{flows-page-CsPDMrVM.js → flows-page-cVFnA4HH.js} +1 -1
- package/src/console/ui-next/dist/assets/{index-CcTDXHuz.js → index-CMIgUbD0.js} +3 -3
- package/src/console/ui-next/dist/assets/{observability-page-CKR595wP.js → observability-page-CQ3p34ip.js} +1 -1
- package/src/console/ui-next/dist/assets/{units-page-CpPFFKyE.js → units-page-l8FeKfnP.js} +1 -1
- package/src/console/ui-next/dist/assets/{vault-page-BsMf-9_W.js → vault-page-CL-d_mLE.js} +1 -1
- package/src/console/ui-next/dist/index.html +1 -1
- package/src/index.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# OKE — Agent Contract
|
|
2
2
|
|
|
3
|
+
One law. Eight elements. One contract. The backend model stays small; operational surfaces are derived from it instead of maintained separately.
|
|
4
|
+
|
|
3
5
|
This file is loaded by every later session. It prevents drift. Product documentation lives in `site/content/docs/`. **If the documentation is silent, stop and ask.**
|
|
4
6
|
|
|
5
7
|
## The one law
|
|
@@ -27,13 +29,13 @@ There are no separate species called endpoints, handlers, consumers, jobs, subsc
|
|
|
27
29
|
|
|
28
30
|
An element earns its place only if it has irreducible physics. New infrastructure becomes a new **driver** for an existing element — never a ninth element.
|
|
29
31
|
|
|
30
|
-
##
|
|
32
|
+
## Core programming vocabulary
|
|
31
33
|
|
|
32
34
|
```typescript
|
|
33
35
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
34
36
|
```
|
|
35
37
|
|
|
36
|
-
That is the
|
|
38
|
+
That is the core programming vocabulary. Everything else is derived.
|
|
37
39
|
|
|
38
40
|
## Governing rule
|
|
39
41
|
|
|
@@ -80,7 +82,7 @@ Published packages:
|
|
|
80
82
|
- `okengine` — framework. Subpath exports: `.`, `./client`, `./test`, `./config`, `./auth`, `./plugins`, `./drivers/*`. `"sideEffects": false`. CLI binary: `oke`.
|
|
81
83
|
- `create-oke` — scaffold CLI (`bunx create-oke@latest <name>`). Lives in `packages/create-oke` and ships Notes starters from `packages/create-oke/templates/{standard,advanced}`.
|
|
82
84
|
|
|
83
|
-
Engine: Bun `>=1.
|
|
85
|
+
Engine: Bun `>=1.4.0`.
|
|
84
86
|
|
|
85
87
|
## Documentation authority
|
|
86
88
|
|
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
<picture>
|
|
3
3
|
<source
|
|
4
4
|
media="(prefers-color-scheme: dark)"
|
|
5
|
-
srcset="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+
|
|
5
|
+
srcset="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+One+contract.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-W.svg&theme=zinc&size=banner&mode=dark&font=geist&align=left"
|
|
6
6
|
/>
|
|
7
7
|
<img
|
|
8
|
-
alt="okengine — One law. Eight elements.
|
|
9
|
-
src="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+
|
|
8
|
+
alt="okengine — One law. Eight elements. One contract."
|
|
9
|
+
src="https://shieldcn.dev/header/grid.svg?title=okengine&subtitle=One+law.+Eight+elements.+One+contract.&logo=https://raw.githubusercontent.com/omqkhafi/okengine/main/site/public/logo/OKE-B.svg&theme=zinc&size=banner&mode=light&font=geist&align=left"
|
|
10
10
|
width="750"
|
|
11
11
|
/>
|
|
12
12
|
</picture>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
24
|
<p align="center">
|
|
25
|
-
<em>
|
|
25
|
+
<em>Modern backends became a pile of disconnected systems. One law collapses them — client, Console, and infra derived from one contract. Yours to host.</em>
|
|
26
26
|
</p>
|
|
27
27
|
|
|
28
28
|
<p align="center">
|
|
@@ -104,7 +104,7 @@ Every backend behavior is a Flow: `on(Trigger) → Effects`. Endpoints, jobs, co
|
|
|
104
104
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
That is the
|
|
107
|
+
That is the core programming vocabulary. World access goes through `fx` — effects are inferred, so the Manifest can derive the typed client, Console, and MCP without a second source of truth.
|
|
108
108
|
|
|
109
109
|
| Element | Essence |
|
|
110
110
|
| ----------- | ----------------------------- |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "okengine",
|
|
3
|
-
"version": "0.18.
|
|
4
|
-
"description": "One law. Eight elements.
|
|
3
|
+
"version": "0.18.5",
|
|
4
|
+
"description": "One law. Eight elements. One contract. The backend model stays small; operational surfaces are derived from it instead of maintained separately.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -5,20 +5,39 @@ icon: "Bot"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Resources for AI agents that
|
|
8
|
+
Resources for AI agents that inspect, understand, or operate an OKEngine application.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
<Callout title="Two directions, one model">
|
|
11
|
+
**MCP is a surface of the model, not the model itself.** Gate governs agents calling in; the AI
|
|
12
|
+
element governs applications calling out to models.
|
|
13
|
+
</Callout>
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
External Agents → MCP (:6535) → Gate Check → Flow (inbound)
|
|
17
|
+
Flow → fx.ask / fx.stream → AI Runtime → Models & External Tools (outbound)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## AI & Agent Surfaces
|
|
11
21
|
|
|
12
22
|
<Cards>
|
|
13
|
-
<Card title="MCP" description="Runtime MCP on :6535; docs MCP on :6536." href="/docs/ai/mcp" />
|
|
14
23
|
<Card
|
|
15
|
-
title="
|
|
16
|
-
description="
|
|
24
|
+
title="MCP"
|
|
25
|
+
description="Runtime MCP on :6535 for running apps; Docs MCP on :6536 for handbook search."
|
|
26
|
+
href="/docs/ai/mcp"
|
|
27
|
+
/>
|
|
28
|
+
<Card
|
|
29
|
+
title="Agent Skills"
|
|
30
|
+
description="AGENTS.md contract and autonomous maintenance skills."
|
|
17
31
|
href="/docs/ai/skills"
|
|
18
32
|
/>
|
|
19
33
|
<Card
|
|
20
34
|
title="llms.txt"
|
|
21
|
-
description="
|
|
35
|
+
description="Standardized machine-readable feeds: /llms.txt, /llms.json, /llms-full.txt, and markdown twins."
|
|
22
36
|
href="/docs/ai/llms-txt"
|
|
23
37
|
/>
|
|
38
|
+
<Card
|
|
39
|
+
title="AI Element"
|
|
40
|
+
description="Calling out: models, prompts, agents, and RAG inside your application."
|
|
41
|
+
href="/docs/elements/ai"
|
|
42
|
+
/>
|
|
24
43
|
</Cards>
|
|
@@ -5,7 +5,16 @@ icon: "Plug"
|
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
Two directions, same
|
|
8
|
+
## Two directions, same execution model
|
|
9
|
+
|
|
10
|
+
> **MCP is a surface of the model, not the model itself.**
|
|
11
|
+
|
|
12
|
+
Two genuinely distinct things share the protocol in OKE:
|
|
13
|
+
|
|
14
|
+
1. **Gate Element (MCP Provider role):** Your app exposes Flows as MCP tools on port **6535** (or via `mcp.tool()` + OAuth 2.1 Authorization Server) so external AI agents (Claude, ChatGPT) can read the Manifest and call declared Flows.
|
|
15
|
+
2. **AI Element (MCP Client role):** Your app consumes _external_ MCP tool servers via `ai.mcpServer(...)`, routing tool calls through `fx.call` inside prompts and agents.
|
|
16
|
+
|
|
17
|
+
OKE does not create separate security models for users, operators, and agents. They enter the same execution model through different triggers and planes.
|
|
9
18
|
|
|
10
19
|
The server on **6535** speaks JSON-RPC over HTTP (MCP protocol `2024-11-05`), requires a Bearer token **even on localhost**, and never forwards that token upstream — adapters receive structured operator ids instead.
|
|
11
20
|
|
|
@@ -9,18 +9,18 @@ Tools alone don't make a good operator — an agent also needs to know the _voca
|
|
|
9
9
|
|
|
10
10
|
## The layers
|
|
11
11
|
|
|
12
|
-
| Layer | Path | Loaded when | Teaches
|
|
13
|
-
| ------------------ | ---------------------------- | ------------------------------------------------- |
|
|
14
|
-
| **Agent contract** | `AGENTS.md` (repo root) | Every agent session, automatically | The one law, eight elements,
|
|
15
|
-
| **Element skill** | `.agents/skills/oke/` | Building or changing an okengine app | The element contract in depth — declaration patterns per element
|
|
16
|
-
| **Docs skill** | `.agents/skills/oke-docs/` | Writing or editing docs under `site/content/docs` | The documentation information-architecture standard and its gates
|
|
17
|
-
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under `## Unreleased` + docs sync via `oke-docs`
|
|
18
|
-
| **Deps skill** | `.agents/skills/oke-deps/` | Updating `package.json` dependencies | Scoped bumps, Bun install, pins (Drizzle RC, fumadocs alias, …)
|
|
19
|
-
| **Images skill** | `.agents/skills/oke-images/` | Updating Compose image pins | Registry probe, pin style, catalog + recipes + Keel + docs lockstep
|
|
12
|
+
| Layer | Path | Loaded when | Teaches |
|
|
13
|
+
| ------------------ | ---------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
14
|
+
| **Agent contract** | `AGENTS.md` (repo root) | Every agent session, automatically | The one law, eight elements, one contract, the fx rule, ports, budgets |
|
|
15
|
+
| **Element skill** | `.agents/skills/oke/` | Building or changing an okengine app | The element contract in depth — declaration patterns per element |
|
|
16
|
+
| **Docs skill** | `.agents/skills/oke-docs/` | Writing or editing docs under `site/content/docs` | The documentation information-architecture standard and its gates |
|
|
17
|
+
| **Ship skill** | `.agents/skills/oke-ship/` | After any implementation, before claiming done | Changelog under `## Unreleased` + docs sync via `oke-docs` |
|
|
18
|
+
| **Deps skill** | `.agents/skills/oke-deps/` | Updating `package.json` dependencies | Scoped bumps, Bun install, pins (Drizzle RC, fumadocs alias, …) |
|
|
19
|
+
| **Images skill** | `.agents/skills/oke-images/` | Updating Compose image pins | Registry probe, pin style, catalog + recipes + Keel + docs lockstep |
|
|
20
20
|
|
|
21
21
|
## AGENTS.md — the root contract
|
|
22
22
|
|
|
23
|
-
Every OKE app's repo carries an `AGENTS.md` that agents (Cursor, Claude Code, and peers) read at session start. It is deliberately short and absolute: every backend behavior is a Flow (`on(Trigger) → Effects`), there are eight elements
|
|
23
|
+
Every OKE app's repo carries an `AGENTS.md` that agents (Cursor, Claude Code, and peers) read at session start. It is deliberately short and absolute: every backend behavior is a Flow (`on(Trigger) → Effects`), there are eight elements bound to one contract, all world access goes through `fx`, drivers are named after protocols, and the ports/budgets are fixed. Its closing rule is the one that keeps agents honest: **if the documentation is silent, stop and ask** — never invent the API.
|
|
24
24
|
|
|
25
25
|
## Skills — installable know-how
|
|
26
26
|
|
|
@@ -129,7 +129,7 @@ const result = await fx.ask(triage, input, {
|
|
|
129
129
|
});
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
External MCP tools take the same path. Declare a server with a **required** allowlist, then pass `server.tool("…")` — never whatever `tools/list` happens to expose:
|
|
132
|
+
External MCP tools take the same path. In OKE, **MCP is a surface of the model, not the model itself** — `ai.mcpServer` models the client role (consuming external tools), while `mcp.tool()` + Gate models the provider role (exposing your flows to agents). Declare a server with a **required** allowlist, then pass `server.tool("…")` — never whatever `tools/list` happens to expose:
|
|
133
133
|
|
|
134
134
|
```typescript
|
|
135
135
|
export const github = ai.mcpServer("github", {
|
|
@@ -9,6 +9,8 @@ Gate answers **"may this happen?"** before it happens: is this a verified member
|
|
|
9
9
|
burned its minute quota? Attach gates to the HTTP trigger — the pipeline checks them before a
|
|
10
10
|
store write, emit, or channel send runs.
|
|
11
11
|
|
|
12
|
+
> **Same Semantics Across Planes:** OKE does not create separate security models for users, operators, and agents. They enter the same execution model through different triggers and planes: users via sessions, operators via Console policies, and agents via `mcp.tool()` + OAuth 2.1 AS.
|
|
13
|
+
|
|
12
14
|
<Callout title="The one rule">
|
|
13
15
|
Every HTTP trigger declares posture: attach a real gate, or attach `.public()`. Omitting both
|
|
14
16
|
fails boot. Denial is a typed error value — never a thrown exception mid-`do`.
|
|
@@ -63,8 +65,7 @@ import { member, canBook, fair } from "../../gates";
|
|
|
63
65
|
export const health = on(
|
|
64
66
|
http.get("/health").public(),
|
|
65
67
|
flow("health.check", {
|
|
66
|
-
|
|
67
|
-
do: () => ({ ok: true as const }),
|
|
68
|
+
do: () => ({ ok: true }),
|
|
68
69
|
}),
|
|
69
70
|
);
|
|
70
71
|
|
|
@@ -1,45 +1,76 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Elements"
|
|
3
|
-
description: "The eight
|
|
3
|
+
description: "The eight closed semantic primitives of the OKEngine backend model."
|
|
4
4
|
icon: "Boxes"
|
|
5
5
|
source: "docs/spec/unified-theory.md"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Forty backend concerns collapse into eight semantic primitives. An element earns its place only with **irreducible physics**.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
<Callout title="Closed primitive set">
|
|
11
|
+
There is no ninth element. New infrastructure creates a protocol driver or plugin, preserving a
|
|
12
|
+
small mental model.
|
|
13
|
+
</Callout>
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Elements define the model.
|
|
17
|
+
Plugins extend the model's capabilities.
|
|
18
|
+
Drivers connect the model to infrastructure.
|
|
19
|
+
Providers and Recipes choose where that infrastructure runs.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## The Eight Primitives
|
|
11
23
|
|
|
12
24
|
<Cards>
|
|
13
|
-
<Card
|
|
25
|
+
<Card
|
|
26
|
+
title="Flow"
|
|
27
|
+
description="Behavior — on(Trigger) → Effects via fx."
|
|
28
|
+
href="/docs/elements/flow"
|
|
29
|
+
/>
|
|
14
30
|
<Card
|
|
15
31
|
title="Signal"
|
|
16
|
-
description="Data in motion —
|
|
32
|
+
description="Data in motion — delivery physics as a property (once, broadcast, live)."
|
|
17
33
|
href="/docs/elements/signal"
|
|
18
34
|
/>
|
|
19
35
|
<Card
|
|
20
36
|
title="Store"
|
|
21
|
-
description="Data at rest —
|
|
37
|
+
description="Data at rest — SQL, KV, files with image transforms, and search."
|
|
22
38
|
href="/docs/elements/store"
|
|
23
39
|
/>
|
|
24
40
|
<Card
|
|
25
41
|
title="Clock"
|
|
26
|
-
description="Time — schedules, intervals, durable sleeps."
|
|
42
|
+
description="Time — schedules, intervals, durable sleeps, and time travel."
|
|
27
43
|
href="/docs/elements/clock"
|
|
28
44
|
/>
|
|
29
45
|
<Card
|
|
30
46
|
title="Gate"
|
|
31
|
-
description="Permission to act — auth and rate limits."
|
|
47
|
+
description="Permission to act — auth, tenancy, ABAC, and rate limits at the trigger."
|
|
32
48
|
href="/docs/elements/gate"
|
|
33
49
|
/>
|
|
34
50
|
<Card
|
|
35
51
|
title="Vault"
|
|
36
|
-
description="Protected knowledge —
|
|
52
|
+
description="Protected knowledge — fail-loud secret contracts and redacted values."
|
|
37
53
|
href="/docs/elements/vault"
|
|
38
54
|
/>
|
|
39
55
|
<Card
|
|
40
56
|
title="Channel"
|
|
41
|
-
description="Reaching humans — email, SMS, push."
|
|
57
|
+
description="Reaching humans — email, SMS, WhatsApp, and push with built-in consent."
|
|
42
58
|
href="/docs/elements/channel"
|
|
43
59
|
/>
|
|
44
|
-
<Card
|
|
60
|
+
<Card
|
|
61
|
+
title="AI"
|
|
62
|
+
description="Reaching machine intelligence — models, prompts, agents, and RAG."
|
|
63
|
+
href="/docs/elements/ai"
|
|
64
|
+
/>
|
|
45
65
|
</Cards>
|
|
66
|
+
|
|
67
|
+
## See How They Compose
|
|
68
|
+
|
|
69
|
+
The closed elements compose into full backend capabilities:
|
|
70
|
+
|
|
71
|
+
| Composition Target | Formula | How It Works |
|
|
72
|
+
| ------------------ | ----------------------------------------------- | --------------------------------------------------------- |
|
|
73
|
+
| **Realtime** | Store + Gate + Signal + Flow → Live Query | CDC changes re-checked against Gate RLS, streamed as SSE |
|
|
74
|
+
| **Security** | Gate + Store/RLS + Tenant + fx → Secure runtime | Tenant-isolated queries with least-privilege capability |
|
|
75
|
+
| **Agents** | MCP + Gate + OAuth + Flow → Agent-ready backend | Declared Flows exposed on :6535 behind auth confirmation |
|
|
76
|
+
| **Operations** | Manifest + Effects + Runs → Inspectable backend | Inferred effects feed Console (:6533) and wide-event logs |
|
|
@@ -129,6 +129,8 @@ A request that filters on a forbidden column — `?secret=eq.x` — fails with *
|
|
|
129
129
|
|
|
130
130
|
### Live queries — `live: true` and `.live(table)`
|
|
131
131
|
|
|
132
|
+
> **Realtime did not introduce a new subsystem. It composed existing Store, Signal, Gate/RLS, Flow, and Manifest semantics.**
|
|
133
|
+
|
|
132
134
|
Pass `live: true` to also mount `GET <path>/live` — an SSE feed of per-subscriber classified events (`upsert` / `revoked` / `delete`), gated the same way as the CRUD verbs:
|
|
133
135
|
|
|
134
136
|
```typescript
|
|
@@ -46,16 +46,9 @@ lists HTTP routes — static GET paths are links. `curl` still gets `{ data, err
|
|
|
46
46
|
The standard starter already exports a named health Flow:
|
|
47
47
|
|
|
48
48
|
```typescript title="flows/main/health.ts"
|
|
49
|
-
import { on, flow, http } from "okengine";
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export const health = on(
|
|
53
|
-
http.get().public(),
|
|
54
|
-
flow({
|
|
55
|
-
out: z.object({ ok: z.literal(true) }),
|
|
56
|
-
do: () => ({ ok: true as const }),
|
|
57
|
-
}),
|
|
58
|
-
);
|
|
49
|
+
import { on, flow, http } from "okengine/http";
|
|
50
|
+
|
|
51
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
59
52
|
```
|
|
60
53
|
|
|
61
54
|
`main` omits the URL prefix, so this file is `GET /health` named `main.health`. See [Project structure](/docs/get-started/project-structure).
|
|
@@ -12,7 +12,7 @@ Learn the shape once, install on Bun, then write your first Flow from the standa
|
|
|
12
12
|
<Cards>
|
|
13
13
|
<Card
|
|
14
14
|
title="Introduction"
|
|
15
|
-
description="One law, eight elements,
|
|
15
|
+
description="One law, eight elements, one contract."
|
|
16
16
|
href="/docs/get-started/introduction"
|
|
17
17
|
/>
|
|
18
18
|
<Card
|
|
@@ -209,7 +209,7 @@ Rename the env keys in `oke.config.ts` to `dev` / `test` / `prod`.
|
|
|
209
209
|
/>
|
|
210
210
|
<Card
|
|
211
211
|
title="Introduction"
|
|
212
|
-
description="The one law, eight elements,
|
|
212
|
+
description="The one law, eight elements, one contract."
|
|
213
213
|
href="/docs/get-started/introduction"
|
|
214
214
|
/>
|
|
215
215
|
<Card
|
|
@@ -1,41 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Introduction
|
|
3
|
-
description:
|
|
3
|
+
description: The Law, the eight elements, the fx rule, and the compiled Manifest contract.
|
|
4
4
|
source: docs/spec/unified-theory.md
|
|
5
5
|
icon: BookOpen
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
## The One
|
|
8
|
+
## The One Law
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
> **The one rule**
|
|
13
|
-
> Every backend behavior is a **Flow**: `on(Trigger) → Effects`. There are no separate species called endpoints, handlers, consumers, jobs, or workflows.
|
|
14
|
-
|
|
15
|
-
## Quick Start
|
|
16
|
-
|
|
17
|
-
### Write One Flow
|
|
18
|
-
|
|
19
|
-
Four contracts plus a `do`. This is the standard starter's health check:
|
|
10
|
+
Every backend behavior is a Flow: `on(Trigger) → Effects`.
|
|
20
11
|
|
|
21
12
|
```typescript
|
|
22
|
-
import { on, flow, http } from "okengine";
|
|
23
|
-
import { z } from "zod";
|
|
13
|
+
import { on, flow, http } from "okengine/http";
|
|
24
14
|
|
|
25
|
-
export const health = on(
|
|
26
|
-
http.get().public(),
|
|
27
|
-
flow({
|
|
28
|
-
out: z.object({ ok: z.literal(true) }),
|
|
29
|
-
do: () => ({ ok: true as const }),
|
|
30
|
-
}),
|
|
31
|
-
);
|
|
15
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
32
16
|
```
|
|
33
17
|
|
|
34
|
-
|
|
18
|
+
An endpoint, a nightly cleanup cron, a queue consumer, a row-change CDC hook — these are not four frameworks. They are **one species** with one shape. Only the trigger changes.
|
|
35
19
|
|
|
36
|
-
|
|
20
|
+
## Changing Only the Trigger
|
|
37
21
|
|
|
38
|
-
Triggers
|
|
22
|
+
Triggers determine what starts the flow while preserving identical anatomy and effect tracking:
|
|
39
23
|
|
|
40
24
|
| Trigger | Starts when | Replaces |
|
|
41
25
|
| ---------------------------------- | ------------------ | ------------------ |
|
|
@@ -53,9 +37,9 @@ on(orderPlaced, sendReceipt);
|
|
|
53
37
|
on(db.table(users).changed("email"), reverify);
|
|
54
38
|
```
|
|
55
39
|
|
|
56
|
-
## Anatomy
|
|
40
|
+
## Anatomy of a Flow
|
|
57
41
|
|
|
58
|
-
|
|
42
|
+
Every Flow consists of four clear sections:
|
|
59
43
|
|
|
60
44
|
| Piece | Role |
|
|
61
45
|
| ------------- | -------------------------------------------------------------- |
|
|
@@ -64,35 +48,53 @@ One pipeline. Only the trigger changes between an endpoint, a job, a consumer, a
|
|
|
64
48
|
| **`do`** | The body — every read, write, emit, and call goes through `fx` |
|
|
65
49
|
| **Effects** | Inferred from those `fx` calls — not hand-annotated |
|
|
66
50
|
|
|
67
|
-
|
|
51
|
+
```typescript
|
|
52
|
+
export const createOrder = on(
|
|
53
|
+
http.post("/orders"),
|
|
54
|
+
flow("orders.create", {
|
|
55
|
+
in: OrderInput,
|
|
56
|
+
out: OrderResult,
|
|
57
|
+
do: async (input, fx) => {
|
|
58
|
+
const order = await fx.store(db).insert(orders).values(input);
|
|
59
|
+
await fx.emit(orderPlaced, { orderId: order.id });
|
|
60
|
+
return order;
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
```
|
|
68
65
|
|
|
69
66
|
## The `fx` Rule
|
|
70
67
|
|
|
71
|
-
**All world access goes through `fx`.**
|
|
68
|
+
**All world access goes through `fx`.**
|
|
69
|
+
|
|
70
|
+
<Callout title="Single door to the world">
|
|
71
|
+
Nothing in user flows imports `node:fs`, calls global `fetch`, or calls `Date.now()`. Every effect
|
|
72
|
+
is recorded on the effect ledger.
|
|
73
|
+
</Callout>
|
|
72
74
|
|
|
73
|
-
| Inferred
|
|
74
|
-
| ----------------------- |
|
|
75
|
-
| Cache invalidation keys |
|
|
76
|
-
| Live queries |
|
|
77
|
-
| Least-privilege tokens |
|
|
78
|
-
| Deterministic tests |
|
|
79
|
-
| Manifest Diff |
|
|
75
|
+
| Inferred from `fx` | What it powers |
|
|
76
|
+
| ----------------------- | ------------------------------------ |
|
|
77
|
+
| Cache invalidation keys | Invalidation follows the flow writes |
|
|
78
|
+
| Live queries | Realtime CDC subscriptions |
|
|
79
|
+
| Least-privilege tokens | Compiler-derived capability matrix |
|
|
80
|
+
| Deterministic tests | Injected time, clock, and stores |
|
|
81
|
+
| Manifest Diff | Blast-radius checks on every save |
|
|
80
82
|
|
|
81
|
-
##
|
|
83
|
+
## The Compiled Manifest
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
The source of truth is the backend model expressed in code. The **Manifest** is the compiled, versioned contract representing that model.
|
|
84
86
|
|
|
85
|
-
| Surface | Port
|
|
87
|
+
| Surface | Port | You Maintain? |
|
|
86
88
|
| --------------------------------- | ------------------ | ----------------------------- |
|
|
87
|
-
| Typed client (`okengine/client`) | your app code | No
|
|
88
|
-
| Console panels, traces, explorers | `:6533` | No
|
|
89
|
-
| MCP for agents | `:6535` | No
|
|
89
|
+
| Typed client (`okengine/client`) | your app code | No — derived from Manifest |
|
|
90
|
+
| Console panels, traces, explorers | `:6533` | No — reads the Manifest |
|
|
91
|
+
| MCP for agents | `:6535` | No — reads the Manifest |
|
|
90
92
|
| Architecture diagram | Console | No — it _is_ the effect graph |
|
|
91
|
-
| Capability matrix + cache keys | compiler / runtime | No — from `fx`
|
|
93
|
+
| Capability matrix + cache keys | compiler / runtime | No — inferred from `fx` |
|
|
92
94
|
|
|
93
95
|
## The Eight Elements
|
|
94
96
|
|
|
95
|
-
An element earns its place only if it has **irreducible physics**.
|
|
97
|
+
An element earns its place only if it has **irreducible physics**.
|
|
96
98
|
|
|
97
99
|
| Element | Essence | Replaces the Zoo |
|
|
98
100
|
| ----------- | ----------------------------- | --------------------------------------------------------- |
|
|
@@ -105,35 +107,38 @@ An element earns its place only if it has **irreducible physics**. New infrastru
|
|
|
105
107
|
| **Channel** | Reaching humans | email · SMS · WhatsApp · push |
|
|
106
108
|
| **AI** | Reaching machine intelligence | model calls · prompts · embeddings · agents · RAG |
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
New infrastructure becomes a **driver** for an existing element, never a ninth.
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
## Core Programming Vocabulary
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
The core programming vocabulary is ten exported names:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
113
117
|
import { on, flow, signal, store, clock, gate, vault, channel, ai, plugin } from "okengine";
|
|
114
118
|
```
|
|
115
119
|
|
|
116
|
-
For HTTP
|
|
120
|
+
For HTTP runtime essentials, use the slim entry `okengine/http`:
|
|
117
121
|
|
|
118
|
-
```
|
|
122
|
+
```typescript
|
|
119
123
|
import { on, flow, http, gate, oke, createBunRuntime } from "okengine/http";
|
|
120
124
|
```
|
|
121
125
|
|
|
122
|
-
Heavy surfaces (runs, journal factories, compiler extract, the legacy mega-barrel) live on `okengine/runs`, `okengine/journal`, `okengine/compiler`, and `okengine/full`.
|
|
123
|
-
|
|
124
|
-
## Learn More
|
|
125
|
-
|
|
126
|
-
| Topic | Page |
|
|
127
|
-
| ------- | ------------------------------------------------------------------------------- |
|
|
128
|
-
| Why OKE | [Why OKE](/docs/get-started/why) |
|
|
129
|
-
| Folders | [Project structure](/docs/get-started/project-structure) |
|
|
130
|
-
| Flow | [Flow](/docs/elements/flow) |
|
|
131
|
-
| `fx` | [fx](/docs/reference/fx) |
|
|
132
|
-
| Agents | [MCP](/docs/ai/mcp) · [Skills](/docs/ai/skills) · [llms.txt](/docs/ai/llms-txt) |
|
|
133
|
-
|
|
134
126
|
## Next Steps
|
|
135
127
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
128
|
+
<Cards>
|
|
129
|
+
<Card
|
|
130
|
+
title="Why OKE"
|
|
131
|
+
description="Explore the problem of seams and the smaller model."
|
|
132
|
+
href="/docs/get-started/why"
|
|
133
|
+
/>
|
|
134
|
+
<Card
|
|
135
|
+
title="Installation"
|
|
136
|
+
description="Scaffold a new OKEngine project with create-oke."
|
|
137
|
+
href="/docs/get-started/installation"
|
|
138
|
+
/>
|
|
139
|
+
<Card
|
|
140
|
+
title="Basic Usage"
|
|
141
|
+
description="Write your first flow and verify it with bun test."
|
|
142
|
+
href="/docs/get-started/basic-usage"
|
|
143
|
+
/>
|
|
144
|
+
</Cards>
|
|
@@ -561,10 +561,7 @@ export const archive = on(
|
|
|
561
561
|
<Tab value="(public)">
|
|
562
562
|
|
|
563
563
|
```typescript title="flows/main/(public)/health.ts"
|
|
564
|
-
export const health = on(
|
|
565
|
-
http.get().public(),
|
|
566
|
-
flow({ out: z.object({ ok: z.literal(true) }), do: () => ({ ok: true as const }) }),
|
|
567
|
-
);
|
|
564
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
568
565
|
// still GET /health named main.health
|
|
569
566
|
```
|
|
570
567
|
|
|
@@ -635,13 +632,9 @@ Do not put `index.ts` next to `health.ts` — that mix is a generate error. Use
|
|
|
635
632
|
<Tab value="health.ts">
|
|
636
633
|
|
|
637
634
|
```typescript title="flows/main/health.ts"
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
out: z.object({ ok: z.literal(true) }),
|
|
642
|
-
do: () => ({ ok: true as const }),
|
|
643
|
-
}),
|
|
644
|
-
);
|
|
635
|
+
import { on, flow, http } from "okengine/http";
|
|
636
|
+
|
|
637
|
+
export const health = on(http.get().public(), flow({ do: () => ({ ok: true }) }));
|
|
645
638
|
// GET /health name main.health
|
|
646
639
|
```
|
|
647
640
|
|