@vibes.diy/prompts 14.1.33 → 14.1.35
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/llms/access.md +24 -3
- package/llms/backend.md +23 -16
- package/package.json +4 -4
package/llms/access.md
CHANGED
|
@@ -22,9 +22,11 @@ and keep loading and signed-out states distinct from denial.
|
|
|
22
22
|
|
|
23
23
|
`(doc, oldDoc, user: UserContext | null, ctx: Helpers) => AccessDescriptor` where `doc` is the document being written, `oldDoc` is the previous version (null for new documents), `user` is the authenticated user or `null` for anonymous requests, and `ctx` provides server helpers for checking materialized state.
|
|
24
24
|
|
|
25
|
-
**UserContext:** `{ userHandle: string, displayName?: string }` — `userHandle` is stable unique id (use for all auth checks), `displayName` is display only (never use for identity checks).
|
|
25
|
+
**UserContext:** `{ userHandle: string, displayName?: string, backend?: "scheduled" | "onChange" | "fetch" }` — `userHandle` is stable unique id (use for all auth checks), `displayName` is display only (never use for identity checks), and `backend` is present only when the app's own `backend.js` issued this write, naming the handler that did (`scheduled`, `onChange` or `fetch`). Every write from a browser or the CLI arrives with `backend` absent, the app owner's own writes included — it says where a write came from, never who the writer is. Gate on `ctx.requireBackend(...)` rather than throwing on `user.backend` yourself: a bare `throw` on `user.backend` refuses the hand-repair lane admin mode exists for, and returns no descriptor for the write it refused.
|
|
26
26
|
|
|
27
|
-
**Helpers (`ctx`):** Opaque closures over the materialized grant state. They throw or pass — you cannot enumerate channels, list members, or iterate grants.
|
|
27
|
+
**Helpers (`ctx`):** Opaque closures over the materialized grant state. They throw or pass — you cannot enumerate channels, list members, or iterate grants. They also throw when `user` is null: `ctx.requireAccess(channelId)` throws if user is not in the channel, `ctx.requireRole(roleName)` throws if user is not in the role, and `ctx.requireBackend()` throws unless the app's own `backend.js` issued this write — `ctx.requireBackend("scheduled")` narrows that to one handler. All three no-op under admin mode.
|
|
28
|
+
|
|
29
|
+
Express every refusal about **who may act** through the supplied validators — `ctx.requireAccess`, `ctx.requireRole`, `ctx.requireBackend` — so that admin mode passes them and the function still runs, applying your own channels and grants to the repaired document; a refusal about the document's own **shape**, like an unknown type or a missing field, stays a plain `throw` and is right to, while a write-once check sits in between and is worth naming in a comment, since a schema repair has to get past it.
|
|
28
30
|
|
|
29
31
|
The access function's scope is the app's own databases: a platform component's own database (a `media:*` name) carries platform-authored access rules the server evaluates separately, so your access function writes rules only for the databases the app itself creates. Within those, platform-driven writes onto the app's own docs — like an `<ImgGen>` version append (the `ctx.isImgGenVersionAppend` shape) — flow through your function like any other write.
|
|
30
32
|
|
|
@@ -189,6 +191,25 @@ This is especially useful when an app has many databases.
|
|
|
189
191
|
|
|
190
192
|
Use `oldDoc` (the previous version of the document) to enforce invariants across updates: `if (oldDoc === null) { /* create-only logic */ }` for new documents, and `if (oldDoc && doc.version <= oldDoc.version) { throw { forbidden: "version must increase" } }` for monotonic versions.
|
|
191
193
|
|
|
194
|
+
### Documents only the app's own backend writes
|
|
195
|
+
|
|
196
|
+
Some documents belong to the machinery rather than to a person: a scheduled run's state doc, a counter a `scheduled` sweep advances, a notification the backend generates. Gate those on the **origin** with `ctx.requireBackend(...)` — one line, no conditional:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
if (doc.type === "runState") {
|
|
200
|
+
ctx.requireBackend("scheduled");
|
|
201
|
+
return { channels: ["show"] };
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The owner-handle version of this rule looks equivalent and is not. The person holding the owner handle sits in front of the app in a browser, so a rule that admits them admits a hand-written `runState` doc typed from the UI — it keeps strangers out of the run's memory and lets in the one person most likely to be poking at it. `ctx.requireBackend("scheduled")` admits the scheduled run and turns every ordinary client write away.
|
|
206
|
+
|
|
207
|
+
This is not a wall against the app's own maintainer. `ctx.requireBackend` no-ops under admin mode, the same way `ctx.requireAccess` and `ctx.requireRole` do, so the owner or platform support repairing a document by hand passes the gate — and the function still runs, so the repaired doc gets the app's own channels and grants and lands routed. That is the point: a document nobody can hand-fix is a support dead end.
|
|
208
|
+
|
|
209
|
+
Name the handler. `ctx.requireBackend()` with no argument admits any of the three, and one of them is `fetch` — a route any visitor can call. A bare `ctx.requireBackend()` on a run's state doc therefore reads as "only my machinery" while admitting anyone who can reach your `_api` endpoint, so name the handler you mean unless you genuinely accept all three.
|
|
210
|
+
|
|
211
|
+
One trap: applying a release's `seed.json` is the **platform** writing, not the app's `backend.js`, so a seeded document arrives with `backend` absent and `ctx.requireBackend` turns it away. Seed that type under a permissive rule, then tighten it once the app is running — or leave the machinery's own documents out of the seed entirely, which is usually what they want.
|
|
212
|
+
|
|
192
213
|
## When to emit access.js, and where it goes
|
|
193
214
|
|
|
194
215
|
The **placement** of `access.js` depends on which turn shape you are in — one-shot whole-app generation vs. incremental scaffold/follow-up edits. Follow the subsection that matches your turn; the other one's ordering is wrong for you and will strand writes.
|
|
@@ -859,7 +880,7 @@ const addCard = (text) => database.put({ type: "card", boardId: pickedBoardId ||
|
|
|
859
880
|
{canInvite(board._id) && <HandleInput onChange={(h) => addMember(board._id, h)} placeholder="Add a friend…" />}
|
|
860
881
|
```
|
|
861
882
|
|
|
862
|
-
|
|
883
|
+
A new access function has no use for `user.isOwner` at all: a rule about a **person** keys on `user.userHandle` or the object's own creator field, and a rule about the app's **own automation** keys on `user.backend`.
|
|
863
884
|
|
|
864
885
|
#### A person the ask names is let in by a record
|
|
865
886
|
|
package/llms/backend.md
CHANGED
|
@@ -71,19 +71,23 @@ allowed by it for the acting identity. If a `fetch` handler writes, make sure
|
|
|
71
71
|
the access function permits that write for an anonymous caller (or design the
|
|
72
72
|
write to happen in `onChange`/`scheduled`, which carry stronger identities).
|
|
73
73
|
|
|
74
|
-
The
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
The access function can tell where a write came from: `user.backend` names the
|
|
75
|
+
handler that issued it — `"scheduled"`, `"onChange"` or `"fetch"` — and is
|
|
76
|
+
absent for every write from a browser or the CLI, the app owner's own included.
|
|
77
|
+
What it does **not** change is identity. An `onChange` write still acts as the
|
|
78
|
+
triggering user, so anything it may write, that user's own client could write
|
|
79
|
+
too; `onChange` stays **derivation and convenience**, never privilege
|
|
80
|
+
escalation. For documents only the server should control, use `scheduled` and
|
|
81
|
+
gate them on the origin with `ctx.requireBackend("scheduled")`, which admits the
|
|
82
|
+
run and turns every ordinary client write away.
|
|
80
83
|
|
|
81
84
|
`scheduled` always runs in **admin mode**, the same override the owner gets
|
|
82
|
-
from the client's admin toggle: `ctx.requireAccess(...)
|
|
83
|
-
no-op, and its `ctx.db.query` reads are
|
|
84
|
-
**runs** on every write — its returned
|
|
85
|
-
so members keep seeing what the cron
|
|
86
|
-
|
|
85
|
+
from the client's admin toggle: `ctx.requireAccess(...)`, `ctx.requireRole(...)`
|
|
86
|
+
and `ctx.requireBackend(...)` all no-op, and its `ctx.db.query` reads are
|
|
87
|
+
unfiltered. The access function still **runs** on every write — its returned
|
|
88
|
+
channels and grants still route the doc, so members keep seeing what the cron
|
|
89
|
+
writes. `user.backend` is `"scheduled"`, so `ctx.requireBackend("scheduled")`
|
|
90
|
+
passes for the run on its own merits. Two consequences worth designing
|
|
87
91
|
around: a scheduled sweep can read and rewrite **every user's** documents in
|
|
88
92
|
the app (access functions protect users from *each other*, not from the app
|
|
89
93
|
owner — the owner's automation touches all users' data); and because admin mode
|
|
@@ -991,15 +995,18 @@ export async function scheduled(event, ctx) {
|
|
|
991
995
|
}
|
|
992
996
|
```
|
|
993
997
|
|
|
994
|
-
The run's own bookkeeping is the server's, not a user's. `
|
|
995
|
-
|
|
996
|
-
|
|
998
|
+
The run's own bookkeeping is the server's, not a user's. `ctx.requireBackend`
|
|
999
|
+
is what separates a run's write from a client's: it passes only when the app's
|
|
1000
|
+
own `backend.js` issued the write, and refuses every browser and CLI write
|
|
1001
|
+
including the owner's own — while still no-opping under admin mode, so a hand
|
|
1002
|
+
repair runs the function and keeps the routing. `access.js` is where that is
|
|
1003
|
+
said:
|
|
997
1004
|
|
|
998
1005
|
```js
|
|
999
1006
|
// access.js
|
|
1000
|
-
export default function (
|
|
1007
|
+
export default function (doc, oldDoc, user, ctx) {
|
|
1001
1008
|
if (doc.type === "runState" || doc.type === "runStatus") {
|
|
1002
|
-
|
|
1009
|
+
ctx.requireBackend("scheduled");
|
|
1003
1010
|
return { channels: ["show"] };
|
|
1004
1011
|
}
|
|
1005
1012
|
// …the app's own document types…
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.1.
|
|
3
|
+
"version": "14.1.35",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"license": "Apache-2.0",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@adviser/cement": "~0.5.34",
|
|
37
|
-
"@vibes.diy/call-ai-v2": "14.1.
|
|
38
|
-
"@vibes.diy/identity": "14.1.
|
|
39
|
-
"@vibes.diy/use-vibes-types": "14.1.
|
|
37
|
+
"@vibes.diy/call-ai-v2": "14.1.35",
|
|
38
|
+
"@vibes.diy/identity": "14.1.35",
|
|
39
|
+
"@vibes.diy/use-vibes-types": "14.1.35",
|
|
40
40
|
"arktype": "~2.2.3",
|
|
41
41
|
"json-schema-faker": "~0.6.3"
|
|
42
42
|
},
|