You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -5,42 +5,153 @@ description: Practical examples for flow nodes, hooks, and plugin event subscrip
5
5
6
6
# Runtime Service Examples
7
7
8
-
## 1) Flow custom node: read related records
8
+
<Callouttype="warn"title="Which data channel each surface actually gets">
9
+
10
+
These pages document the `services.*`**contract surface** — the signatures, not a
11
+
binding every surface receives (see the [binding note](/docs/kernel/runtime-services)).
12
+
The examples below therefore use the channel each runtime surface is really handed:
13
+
14
+
| Surface | Data channel |
15
+
|:--|:--|
16
+
| Data hook (`beforeInsert`, `beforeUpdate`, …) |`ctx.api` — the scoped cross-object API the engine binds per operation (`buildHookApi`, `packages/objectql/src/engine.ts`) |
17
+
| Flow `script` function | none — a function is **pure** by contract (`handlerContract: 'pure'`), so its record I/O lives on the flow graph |
18
+
| Plugin | the plugin context (`ctx.hook`, the kernel service registry) |
19
+
20
+
A hook context is built key by key by the engine and carries **no `services` key**, so
21
+
`ctx.services?.sharing?.canEdit(…)` there evaluates to `undefined` — and a guard written on
22
+
it (`if (!ok) throw new Error('PERMISSION_DENIED')`) rejects **every** write instead of
Copy file name to clipboardExpand all lines: content/docs/kernel/runtime-services/sharing-service.mdx
+37-8Lines changed: 37 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,15 +56,44 @@ mask AND-ed with object CRUD, not a fourth `access_level`.
56
56
-`CONFLICT` (409) — `revoke` on a rule-materialised share (`source != 'manual'`); the next rule reconciliation would silently re-grant it. Deactivate or edit the sharing rule instead.
57
57
-`SHARING_NOT_ENABLED` (422) — `grant` on an object the sharing gates never consult (public sharing model, no `owner_id` field, a bypass object, or `controlled_by_parent`).
58
58
59
+
## Enforcement is automatic — do not re-check it in a hook
60
+
61
+
With `@objectstack/plugin-sharing` installed, the gates run **inside the engine**: its
62
+
middleware picks the gate by verb — `canEdit` before a by-id update, `canDelete` before a
63
+
delete — and throws `FORBIDDEN` before the hook chain could ask anything. A hook that
64
+
re-checks adds nothing, and it cannot ask this service at all: a hook context is built key
65
+
by key by the engine (`object` / `event` / `input` / `session` / `provenance` / `user` /
66
+
`api` / `transaction` / `ql`) and carries **no `services` key**, so
67
+
`ctx.services?.sharing?.canEdit(…)` is `undefined` there and `if (!ok) throw …` rejects
68
+
every write ([#5720](https://github.com/objectstack-ai/objectstack/issues/5720)). A hook's
69
+
own channel is `ctx.api` — use it for *business* rules
0 commit comments