@cat-factory/gatekeeper-worker 0.5.0 → 0.6.0
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/README.md +43 -2
- package/dist/arguments.d.ts +18 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +75 -0
- package/dist/arguments.js.map +1 -0
- package/dist/capability.d.ts +10 -0
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +39 -1
- package/dist/capability.js.map +1 -1
- package/dist/env.d.ts +10 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +12 -0
- package/dist/env.js.map +1 -1
- package/dist/errors.d.ts +20 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -1
- package/dist/gatekeeper.d.ts +31 -1
- package/dist/gatekeeper.d.ts.map +1 -1
- package/dist/gatekeeper.js +38 -8
- package/dist/gatekeeper.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/os/discoverability.d.ts +13 -1
- package/dist/os/discoverability.d.ts.map +1 -1
- package/dist/os/discoverability.js +23 -6
- package/dist/os/discoverability.js.map +1 -1
- package/dist/os/exports.d.ts +27 -0
- package/dist/os/exports.d.ts.map +1 -1
- package/dist/os/exports.js +25 -0
- package/dist/os/exports.js.map +1 -1
- package/dist/os/hook-controller.d.ts +14 -0
- package/dist/os/hook-controller.d.ts.map +1 -0
- package/dist/os/hook-controller.js +53 -0
- package/dist/os/hook-controller.js.map +1 -0
- package/dist/os/hooks.d.ts +200 -0
- package/dist/os/hooks.d.ts.map +1 -0
- package/dist/os/hooks.js +253 -0
- package/dist/os/hooks.js.map +1 -0
- package/dist/os/protocol.d.ts +43 -0
- package/dist/os/protocol.d.ts.map +1 -1
- package/dist/os/queue.d.ts +9 -0
- package/dist/os/queue.d.ts.map +1 -1
- package/dist/os/queue.js +44 -1
- package/dist/os/queue.js.map +1 -1
- package/dist/os/resource-core.d.ts +25 -10
- package/dist/os/resource-core.d.ts.map +1 -1
- package/dist/os/resource-core.js +31 -14
- package/dist/os/resource-core.js.map +1 -1
- package/dist/os/resource.d.ts.map +1 -1
- package/dist/os/resource.js +7 -1
- package/dist/os/resource.js.map +1 -1
- package/dist/os/session-types.d.ts.map +1 -1
- package/dist/os/session-types.js +28 -0
- package/dist/os/session-types.js.map +1 -1
- package/dist/os/sharing.d.ts +35 -0
- package/dist/os/sharing.d.ts.map +1 -0
- package/dist/os/sharing.js +135 -0
- package/dist/os/sharing.js.map +1 -0
- package/dist/os/vendor.d.ts +1 -1
- package/dist/os/vendor.d.ts.map +1 -1
- package/dist/os/vendor.js +8 -1
- package/dist/os/vendor.js.map +1 -1
- package/dist/state.d.ts +80 -1
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +234 -6
- package/dist/state.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +4 -1
- package/dist/worker.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -123,6 +123,26 @@ version has to be the consumer's.
|
|
|
123
123
|
caller's own key.
|
|
124
124
|
- **Run lifecycle without polling.** `run.started` / `run.completed` / `run.failed` land as a
|
|
125
125
|
`runs_watched()` projection, and a terminal event settles that run's open cards.
|
|
126
|
+
- **Hooks, so the workspace is pushed rather than asked.** A session binds a callback through the
|
|
127
|
+
workspace's own `bindHook`, the workspace enables it when it is ready to, and each card or run
|
|
128
|
+
transition is then pushed: a fresh callback per delivery, authorized as the observation it is.
|
|
129
|
+
What a hook pushes is exactly what `approvals_list()` and `runs_watched()` answer, which stay the
|
|
130
|
+
truth: a delivery a hook missed is still readable there, and `hooks_bound()` reports the miss
|
|
131
|
+
rather than leaving it to be inferred from a quiet inbox. The fan-out runs BEHIND the delivery's
|
|
132
|
+
acknowledgement and each push is bounded by its own deadline, so a workspace that hangs costs a
|
|
133
|
+
notification and never the platform's retry budget.
|
|
134
|
+
- **Sharing that is verified, not assumed.** A workspace user shared onto a bound resource is
|
|
135
|
+
admitted only when their OWN account's tier reaches everything the resource's tier reaches, and
|
|
136
|
+
masks no more; a tier that can read captured agent text is never shareable at all. The observer
|
|
137
|
+
has to hold an account THIS deployment minted, checked before any tier is resolved: an unknown id
|
|
138
|
+
would otherwise resolve to the auto-provisioned tier, which is the tier every account here holds,
|
|
139
|
+
so a viewer from another vendor entirely measured up as identical to the owner. Anything the
|
|
140
|
+
Gatekeeper cannot answer (an observer with no verifier, an account it never minted) is a refusal
|
|
141
|
+
that says which it is.
|
|
142
|
+
- **Arguments checked against what each operation declares.** An argument no operation reads used
|
|
143
|
+
to be dropped on the way through, so a filter nobody applied came back as an answer shaped like a
|
|
144
|
+
filtered one. It is now a refusal naming what the operation does take, made before a key is
|
|
145
|
+
minted or an approval is spent.
|
|
126
146
|
- **Self-enrolment and offboarding.** The endpoint registers itself under a caller-chosen webhook
|
|
127
147
|
id, hourly and idempotently. `POST /admin/retire?actorId=…` revokes every key minted for one
|
|
128
148
|
person, upstream first and then here.
|
|
@@ -182,9 +202,14 @@ workspace that finds either missing does not get an error anyone monitors; it ne
|
|
|
182
202
|
installing. So a green response carries the answer beside `ok`:
|
|
183
203
|
|
|
184
204
|
```json
|
|
185
|
-
{ "ok": true, "os": { "discoverable": true, "blockers": [] } }
|
|
205
|
+
{ "ok": true, "os": { "discoverable": true, "blockers": [], "limitations": [] } }
|
|
186
206
|
```
|
|
187
207
|
|
|
208
|
+
`limitations` is the same report for what does NOT stop an install. `CatFactoryHookController` is
|
|
209
|
+
reached only when a session binds a hook, so a deployment missing it installs, serves and answers
|
|
210
|
+
exactly as before and refuses `approvals_subscribe()`; calling that a blocker would turn a working
|
|
211
|
+
deployment red, and omitting it would leave the gap to be discovered from an agent's failed call.
|
|
212
|
+
|
|
188
213
|
It is REPORTED, never folded into the status, because a Gatekeeper serving `/rpc` and nothing else
|
|
189
214
|
is a supported deployment: this package promises that door to consumers that are not a Cloudflare
|
|
190
215
|
OS, and turning their liveness red on a version bump would be this route answering a question
|
|
@@ -237,7 +262,7 @@ path that matches nothing is not an error, because result shapes legitimately va
|
|
|
237
262
|
`connect({ actorId, label? })` on the `/rpc` session resolves the actor's tier and returns the
|
|
238
263
|
capability. `actorId` is the OS's own authenticated identity for the person, and it is the ONLY
|
|
239
264
|
claim the Gatekeeper trusts: nothing the caller sends picks a tier. Beyond the granted operation
|
|
240
|
-
methods, every capability carries
|
|
265
|
+
methods, every capability carries ten reserved methods:
|
|
241
266
|
|
|
242
267
|
- `tier()`: who the caller is acting as (actor, tier name, description, key scope).
|
|
243
268
|
- `bindings()`: the granted operations, each with its scope floor, consequence (cautious default
|
|
@@ -251,6 +276,22 @@ methods, every capability carries seven reserved methods:
|
|
|
251
276
|
- `approvals_list()`, `approvals_inspect(cardId)`, `approvals_answer(cardId, input)`: the
|
|
252
277
|
approval inbox; see the template README for the flow and the three answer outcomes.
|
|
253
278
|
- `runs_watched()`: the run-lifecycle projection built from the `run.*` webhook events.
|
|
279
|
+
- `approvals_subscribe(callback)`, `runs_subscribe(callback)`: bind a hook, so the two projections
|
|
280
|
+
above are PUSHED. The callback needs one method (`onApprovalCard(card)` / `onRunEvent(state)`),
|
|
281
|
+
and a card is pushed on every transition it makes, settlement by a terminal run event included.
|
|
282
|
+
Both refuse on the `/rpc` door, which brings no approval queue to register a hook with and
|
|
283
|
+
nothing to authorize a delivery against; the refusal names the two reads that answer the same
|
|
284
|
+
question. A workspace that does not take the binding (its queue serves no hooks, or a person
|
|
285
|
+
declined) is a `hook_bind_refused` carrying the cause verbatim, because those two need opposite
|
|
286
|
+
fixes and nothing at this seam can tell them apart.
|
|
287
|
+
- `hooks_bound()`: what this account has enabled, with what each hook has been pushed
|
|
288
|
+
(`deliveries`), what it could not be pushed (`missed`), what the workspace refused (`failures`)
|
|
289
|
+
and whether it is still `live`. A hook goes quiet when the durable object is evicted between
|
|
290
|
+
deliveries, because the workspace's callback source is a stub and cannot be stored; that reads as
|
|
291
|
+
`live: false` with a rising `missed`, and the remedy is to bind again. Re-binding from the same
|
|
292
|
+
gadget RE-ARMS the same registration rather than adding a second: a hook is identified by where
|
|
293
|
+
its deliveries land, and its counters carry over, since they are the history that prompted the
|
|
294
|
+
re-arm.
|
|
254
295
|
|
|
255
296
|
Refusals from a live Gatekeeper are `GatekeeperError`s carrying a machine-readable `reason`
|
|
256
297
|
(`unknown_actor`, `card_not_found`, `ambiguous_park`, …), the same role the platform's own
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GatekeeperBinding } from '@cat-factory/gatekeeper-bindings';
|
|
2
|
+
/**
|
|
3
|
+
* Every argument name an operation reads, in the order a caller would learn them.
|
|
4
|
+
*
|
|
5
|
+
* Exported because `approvals_inspect` and the session types describe the same set from the same
|
|
6
|
+
* binding, and a second derivation of "what may I pass" is how a published shape and an enforced
|
|
7
|
+
* one drift.
|
|
8
|
+
*/
|
|
9
|
+
export declare function declaredArguments(binding: GatekeeperBinding): string[];
|
|
10
|
+
/**
|
|
11
|
+
* Refuse a call whose arguments the operation does not declare, or that omits one it requires.
|
|
12
|
+
*
|
|
13
|
+
* Returns the bag so the call site reads as a narrowing rather than as a statement with a side
|
|
14
|
+
* effect. An `undefined` value is treated as absent throughout, which is what a caller building a
|
|
15
|
+
* bag from optional fields produces and what the SDK's own forwarding already does with it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkedArguments(binding: GatekeeperBinding, args: Record<string, unknown>): Record<string, unknown>;
|
|
18
|
+
//# sourceMappingURL=arguments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arguments.d.ts","sourceRoot":"","sources":["../src/arguments.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AAMzE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAMtE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmCzB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Checking a call's arguments against what the operation DECLARES, before anything happens.
|
|
2
|
+
//
|
|
3
|
+
// The published contract asks a gatekeeper to validate RPC arguments against the declared
|
|
4
|
+
// signature and reject a call carrying one the receiver does not declare. The reference
|
|
5
|
+
// implementations generate that check from their own signatures; ours is generated already, one
|
|
6
|
+
// layer down: every binding in `@cat-factory/gatekeeper-bindings` carries its path parameters, its
|
|
7
|
+
// query parameters with which are required, and whether it takes a body. So the check is derived
|
|
8
|
+
// from the same table the session's `.d.ts` and the policy compiler read, and an operation whose
|
|
9
|
+
// shape changes brings its own validation with it.
|
|
10
|
+
//
|
|
11
|
+
// The reason to have it at all is the SILENT DROP. `invoke` forwards the arguments it recognises
|
|
12
|
+
// and ignores the rest, so an agent asking `tasks_list_by_service` to filter by a parameter that
|
|
13
|
+
// operation does not take used to get an unfiltered list and no word about it: a wrong answer
|
|
14
|
+
// shaped exactly like a right one. A near-miss spelling is the same failure with a plainer cause.
|
|
15
|
+
//
|
|
16
|
+
// It runs on BOTH doors, ahead of the approval queue and ahead of the key broker. That order is
|
|
17
|
+
// the point: a call that cannot succeed should not mint a credential, should not spend a
|
|
18
|
+
// workspace's approval on an action that would 400, and should not reach the deployment at all.
|
|
19
|
+
// Refusing it is a behaviour change for a caller that was sending an argument nothing read, which
|
|
20
|
+
// is exactly the caller this check exists for.
|
|
21
|
+
//
|
|
22
|
+
// What it does NOT check is what a value may BE. That is the deployment's judgement, it is stated
|
|
23
|
+
// in the spec these bindings are generated from, and its refusal already names the field; a second
|
|
24
|
+
// copy of it here would be free to disagree with the first.
|
|
25
|
+
import { GatekeeperError } from './errors.js';
|
|
26
|
+
/** The one argument key that is never a parameter: the request body `invoke` forwards. */
|
|
27
|
+
const BODY = 'body';
|
|
28
|
+
/**
|
|
29
|
+
* Every argument name an operation reads, in the order a caller would learn them.
|
|
30
|
+
*
|
|
31
|
+
* Exported because `approvals_inspect` and the session types describe the same set from the same
|
|
32
|
+
* binding, and a second derivation of "what may I pass" is how a published shape and an enforced
|
|
33
|
+
* one drift.
|
|
34
|
+
*/
|
|
35
|
+
export function declaredArguments(binding) {
|
|
36
|
+
return [
|
|
37
|
+
...binding.pathParams,
|
|
38
|
+
...binding.queryParams.map((param) => param.name),
|
|
39
|
+
...(binding.hasBody ? [BODY] : []),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Refuse a call whose arguments the operation does not declare, or that omits one it requires.
|
|
44
|
+
*
|
|
45
|
+
* Returns the bag so the call site reads as a narrowing rather than as a statement with a side
|
|
46
|
+
* effect. An `undefined` value is treated as absent throughout, which is what a caller building a
|
|
47
|
+
* bag from optional fields produces and what the SDK's own forwarding already does with it.
|
|
48
|
+
*/
|
|
49
|
+
export function checkedArguments(binding, args) {
|
|
50
|
+
const declared = declaredArguments(binding);
|
|
51
|
+
const supplied = Object.entries(args).filter(([, value]) => value !== undefined);
|
|
52
|
+
const undeclared = supplied.map(([name]) => name).filter((name) => !declared.includes(name));
|
|
53
|
+
if (undeclared.length > 0) {
|
|
54
|
+
throw new GatekeeperError('undeclared_argument', `'${binding.name}' does not take ${quoted(undeclared)}, so ${undeclared.length === 1 ? 'it would be' : 'they would be'} ` +
|
|
55
|
+
`dropped rather than sent. It takes ${declared.length === 0 ? 'no arguments' : quoted(declared)}.`);
|
|
56
|
+
}
|
|
57
|
+
for (const name of binding.pathParams) {
|
|
58
|
+
const value = args[name];
|
|
59
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
60
|
+
throw new GatekeeperError('missing_argument', `'${binding.name}' needs '${name}': it is part of the route (\`${binding.path}\`), so ` +
|
|
61
|
+
'there is no call to make without it.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const param of binding.queryParams) {
|
|
65
|
+
if (param.required && args[param.name] === undefined) {
|
|
66
|
+
throw new GatekeeperError('missing_argument', `'${binding.name}' needs '${param.name}': the deployment refuses the call without it, ` +
|
|
67
|
+
'because it says which question is being asked rather than narrowing the answer.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return args;
|
|
71
|
+
}
|
|
72
|
+
function quoted(names) {
|
|
73
|
+
return names.map((name) => `'${name}'`).join(', ');
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=arguments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arguments.js","sourceRoot":"","sources":["../src/arguments.ts"],"names":[],"mappings":"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"}
|
package/dist/capability.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RpcTarget } from 'capnweb';
|
|
2
2
|
import type { GatekeeperBinding } from '@cat-factory/gatekeeper-bindings';
|
|
3
3
|
import type { Actor, KeyBroker } from './keys.js';
|
|
4
|
+
import { type HookTopic } from './os/hooks.js';
|
|
4
5
|
import { type CompiledTier } from './policy/compile.js';
|
|
5
6
|
import type { ApprovalCard, GatekeeperState } from './state.js';
|
|
6
7
|
/**
|
|
@@ -36,6 +37,15 @@ export interface SessionGovernance {
|
|
|
36
37
|
* instead of returning, so a refused action cannot read as a call that returned nothing.
|
|
37
38
|
*/
|
|
38
39
|
act<T>(binding: GatekeeperBinding, args: Record<string, unknown>, perform: () => Promise<T>): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Register a callback the workspace will be pushed to, once it has approved the registration.
|
|
42
|
+
*
|
|
43
|
+
* On the seam rather than beside it because binding a hook is a request to the SAME queue every
|
|
44
|
+
* call passes through, and because the door with no queue is exactly the door with no hooks: a
|
|
45
|
+
* `/rpc` session has nowhere to hand a callback and nothing to authorize the deliveries against,
|
|
46
|
+
* so it refuses and names the projection it would have been accelerating.
|
|
47
|
+
*/
|
|
48
|
+
subscribe(topic: HookTopic, callback: unknown): Promise<void>;
|
|
39
49
|
/**
|
|
40
50
|
* The session is gone: release everything it was holding on the workspace's behalf.
|
|
41
51
|
*
|
package/dist/capability.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AAYzE,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAEjD,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,eAAe,CAAA;AAElF,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAY,MAAM,YAAY,CAAA;AAuBzE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjF;;;;;;OAMG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1D;;;;;OAKG;IACH,GAAG,CAAC,CAAC,EACH,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,CAAC,CAAC,CAAA;IACb;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7D;;;;;;OAMG;IACH,KAAK,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,KAAK,CAAA;IACZ,IAAI,EAAE,YAAY,CAAA;IAClB,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAA;IACzC,wFAAwF;IACxF,UAAU,CAAC,EAAE,iBAAiB,CAAA;CAC/B;AAED,4FAA4F;AAC5F,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAA;IAClB,2FAA2F;IAC3F,SAAS,EAAE,OAAO,CAAA;IAClB,0FAA0F;IAC1F,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE;YACP,MAAM,EAAE,MAAM,CAAA;YACd,OAAO,EAAE,MAAM,CAAA;YACf,OAAO,EAAE,MAAM,CAAA;YACf,oGAAoG;YACpG,OAAO,EAAE,OAAO,CAAA;YAChB,MAAM,EAAE,SAAS;gBACf,IAAI,EAAE,MAAM,CAAA;gBACZ,QAAQ,EAAE,OAAO,CAAA;gBACjB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;gBAC3B,MAAM,EAAE,MAAM,CAAA;aACf,EAAE,CAAA;SACJ,EAAE,CAAA;KACJ,EAAE,CAAA;IACH,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,SAAS,CAgNpE"}
|
package/dist/capability.js
CHANGED
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
// base classes, a `DataCloneError` on the first bind) is the more obvious one.
|
|
20
20
|
import { RpcTarget } from 'capnweb';
|
|
21
21
|
import { answerCard, assertAnswerable, pendingParks, describeStale, } from './approvals.js';
|
|
22
|
+
import { checkedArguments } from './arguments.js';
|
|
22
23
|
import { GatekeeperError, PolicyError } from './errors.js';
|
|
23
24
|
import { applyMask } from './masking.js';
|
|
25
|
+
import { HOOK_TOPICS } from './os/hooks.js';
|
|
24
26
|
import { fenced } from './markdown.js';
|
|
25
27
|
import { describeBinding } from './policy/compile.js';
|
|
26
28
|
/**
|
|
@@ -38,7 +40,10 @@ const RESERVED_METHODS = [
|
|
|
38
40
|
'approvals_list',
|
|
39
41
|
'approvals_inspect',
|
|
40
42
|
'approvals_answer',
|
|
43
|
+
'approvals_subscribe',
|
|
41
44
|
'runs_watched',
|
|
45
|
+
'runs_subscribe',
|
|
46
|
+
'hooks_bound',
|
|
42
47
|
];
|
|
43
48
|
/**
|
|
44
49
|
* Build the capability for one connected actor.
|
|
@@ -57,11 +62,16 @@ export function buildCapability(deps) {
|
|
|
57
62
|
'method in capability.ts; a granted operation must never be shadowed by one.');
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
|
-
const invoke = async (name,
|
|
65
|
+
const invoke = async (name, bag) => {
|
|
61
66
|
const binding = granted.get(name);
|
|
62
67
|
if (binding === undefined) {
|
|
63
68
|
throw new GatekeeperError('binding_not_granted', `Tier '${tier.name}' does not grant '${name}'.`);
|
|
64
69
|
}
|
|
70
|
+
// Ahead of the key broker and ahead of the queue, because a call that cannot succeed should
|
|
71
|
+
// mint no credential and spend no approval. An argument the operation does not declare is a
|
|
72
|
+
// REFUSAL rather than a drop: forwarded silently, it is a filter nobody applied and an answer
|
|
73
|
+
// that looks exactly like the right one.
|
|
74
|
+
const args = checkedArguments(binding, bag);
|
|
65
75
|
const perform = async () => await deps.keys.run(deps.actor, tier.keyScope, (client) => binding.invoke(client, args));
|
|
66
76
|
const governance = deps.governance;
|
|
67
77
|
if (governance === undefined)
|
|
@@ -118,6 +128,34 @@ export function buildCapability(deps) {
|
|
|
118
128
|
// `withheld()` is the degrade-loudly half: an agent that cannot tell "your policy hides this"
|
|
119
129
|
// from "this deployment does not have it" reports the wrong one to whoever has to fix it.
|
|
120
130
|
proto.withheld = () => tier.withheld;
|
|
131
|
+
/**
|
|
132
|
+
* Bind a callback for one topic, or refuse in the terms of the door the caller came in by.
|
|
133
|
+
*
|
|
134
|
+
* The refusal is the interesting half. `/rpc` brings no approval queue, so there is nothing to
|
|
135
|
+
* register a hook with and nothing to authorize its deliveries against; answering with a
|
|
136
|
+
* silently-never-fires registration would be the worst available outcome, and answering
|
|
137
|
+
* "unsupported" without naming `approvals_list()` would send a caller looking for a feature
|
|
138
|
+
* rather than for the two methods that already answer the same question.
|
|
139
|
+
*/
|
|
140
|
+
const subscribe = async (topic, callback) => {
|
|
141
|
+
const governance = deps.governance;
|
|
142
|
+
if (governance === undefined) {
|
|
143
|
+
throw new GatekeeperError('hooks_unavailable', `This session was opened on a door with no approval queue, so ${HOOK_TOPICS[topic].sessionMethod}() ` +
|
|
144
|
+
'has nowhere to register a callback and no way to authorize what it would push. Read ' +
|
|
145
|
+
'`approvals_list()` and `runs_watched()` instead: they carry exactly what a hook pushes.');
|
|
146
|
+
}
|
|
147
|
+
await governance.subscribe(topic, callback);
|
|
148
|
+
};
|
|
149
|
+
proto.approvals_subscribe = async (callback) => subscribe('approval_card', callback);
|
|
150
|
+
proto.runs_subscribe = async (callback) => subscribe('run_event', callback);
|
|
151
|
+
// What this account has enabled, and whether each one's live half survived. A hook that stopped
|
|
152
|
+
// firing is otherwise indistinguishable from a deployment where nothing happened, which is the
|
|
153
|
+
// reading a workspace must never be left to make on its own.
|
|
154
|
+
proto.hooks_bound = async () => {
|
|
155
|
+
const hooks = await deps.state.listHooks(deps.actor.id);
|
|
156
|
+
await observeLocal('List the hooks bound for this account', `${hooks.length} hook(s) this workspace enabled, with what each has been pushed and what it missed.`);
|
|
157
|
+
return hooks;
|
|
158
|
+
};
|
|
121
159
|
proto.approvals_list = async () => {
|
|
122
160
|
const cards = await deps.state.listCards();
|
|
123
161
|
await observeLocal('List the open approval cards', `${cards.length} card(s) the paired deployment raised and delivered to this Gatekeeper.`);
|
package/dist/capability.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,gGAAgG;AAChG,8FAA8F;AAC9F,+FAA+F;AAC/F,4DAA4D;AAC5D,EAAE;AACF,iGAAiG;AACjG,6FAA6F;AAC7F,8EAA8E;AAC9E,EAAE;AACF,6FAA6F;AAC7F,+FAA+F;AAC/F,8FAA8F;AAC9F,mGAAmG;AACnG,iGAAiG;AACjG,+EAA+E;AAE/E,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAEnC,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,GAId,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,eAAe,EAAqB,MAAM,qBAAqB,CAAA;AAGxE;;;;;;;GAOG;AACH,MAAM,gBAAgB,GAAG;IACvB,MAAM;IACN,UAAU;IACV,UAAU;IACV,gBAAgB;IAChB,mBAAmB;IACnB,kBAAkB;IAClB,cAAc;
|
|
1
|
+
{"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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"}
|
package/dist/env.d.ts
CHANGED
|
@@ -39,6 +39,16 @@ export declare function requireVar(env: GatekeeperEnv, binding: StringBinding):
|
|
|
39
39
|
* reads as a healthy Gatekeeper.
|
|
40
40
|
*/
|
|
41
41
|
export declare function missingBindings(env: GatekeeperEnv): Binding[];
|
|
42
|
+
/**
|
|
43
|
+
* The durable object holding everything this pairing knows, addressed the ONE way.
|
|
44
|
+
*
|
|
45
|
+
* Named for the deployment it is paired with, so pointing this Worker at a different cat-factory
|
|
46
|
+
* gets a different object rather than inheriting the previous one's cards, hooks and minted keys.
|
|
47
|
+
* It is a function rather than a line inside `Gatekeeper` because the hook controller reaches the
|
|
48
|
+
* same object without assembling a Gatekeeper at all, and a second spelling of the name would be
|
|
49
|
+
* a second object holding half the state.
|
|
50
|
+
*/
|
|
51
|
+
export declare function stateFor(env: GatekeeperEnv): DurableObjectStub<GatekeeperState>;
|
|
42
52
|
/** The refusal an operator can act on: every unset binding, each with how to set it. */
|
|
43
53
|
export declare function describeMissingBindings(missing: readonly Binding[]): string;
|
|
44
54
|
export {};
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAA;IAClB,kGAAkG;IAClG,gBAAgB,EAAE,MAAM,CAAA;IACxB,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAA;IACtB,uFAAuF;IACvF,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,sBAAsB,CAAC,eAAe,CAAC,CAAA;CAC/C;AAED,4CAA4C;AAC5C,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,gBAAgB,CAAA;AAsB7D,KAAK,OAAO,GAAG,MAAM,aAAa,CAAA;AAgBlC,2DAA2D;AAC3D,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IAEzB,YAAY,OAAO,EAAE,OAAO,EAI3B;CACF;AAED,KAAK,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAE9C,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAI7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,EAAE,CAG7D;AAYD,wFAAwF;AACxF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAG3E"}
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAA;IAClB,kGAAkG;IAClG,gBAAgB,EAAE,MAAM,CAAA;IACxB,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAA;IACtB,uFAAuF;IACvF,eAAe,EAAE,MAAM,CAAA;IACvB,KAAK,EAAE,sBAAsB,CAAC,eAAe,CAAC,CAAA;CAC/C;AAED,4CAA4C;AAC5C,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,gBAAgB,CAAA;AAsB7D,KAAK,OAAO,GAAG,MAAM,aAAa,CAAA;AAgBlC,2DAA2D;AAC3D,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IAEzB,YAAY,OAAO,EAAE,OAAO,EAI3B;CACF;AAED,KAAK,aAAa,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAE9C,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAI7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,EAAE,CAG7D;AAYD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAE/E;AAED,wFAAwF;AACxF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAG3E"}
|
package/dist/env.js
CHANGED
|
@@ -82,6 +82,18 @@ function isSet(env, binding) {
|
|
|
82
82
|
const value = env[binding];
|
|
83
83
|
return typeof value === 'string' && value.length > 0;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* The durable object holding everything this pairing knows, addressed the ONE way.
|
|
87
|
+
*
|
|
88
|
+
* Named for the deployment it is paired with, so pointing this Worker at a different cat-factory
|
|
89
|
+
* gets a different object rather than inheriting the previous one's cards, hooks and minted keys.
|
|
90
|
+
* It is a function rather than a line inside `Gatekeeper` because the hook controller reaches the
|
|
91
|
+
* same object without assembling a Gatekeeper at all, and a second spelling of the name would be
|
|
92
|
+
* a second object holding half the state.
|
|
93
|
+
*/
|
|
94
|
+
export function stateFor(env) {
|
|
95
|
+
return env.STATE.get(env.STATE.idFromName(requireVar(env, 'CAT_FACTORY_BASE_URL')));
|
|
96
|
+
}
|
|
85
97
|
/** The refusal an operator can act on: every unset binding, each with how to set it. */
|
|
86
98
|
export function describeMissingBindings(missing) {
|
|
87
99
|
const remedies = missing.map((binding) => `${binding} (${howToSet(binding)})`).join('; ');
|
package/dist/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,mDAAmD;AACnD,EAAE;AACF,kGAAkG;AAClG,kGAAkG;AAClG,8FAA8F;AAC9F,6FAA6F;AAC7F,2FAA2F;AAC3F,mDAAmD;AACnD,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,+EAA+E;AA0B/E;;;;;;;;;GASG;AACH,MAAM,aAAa,GAAG;IACpB,oBAAoB,EAAE,KAAK;IAC3B,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,KAAK;IACjB,gBAAgB,EAAE,QAAQ;IAC1B,cAAc,EAAE,QAAQ;IACxB,eAAe,EAAE,QAAQ;IACzB,KAAK,EAAE,gBAAgB;CACoC,CAAA;AAI7D,mFAAmF;AACnF,SAAS,QAAQ,CAAC,OAAgB;IAChC,gGAAgG;IAChG,2DAA2D;IAC3D,QAAQ,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,KAAK,KAAK;YACR,OAAO,kCAAkC,CAAA;QAC3C,KAAK,QAAQ;YACX,OAAO,qCAAqC,OAAO,4BAA4B,CAAA;QACjF,KAAK,gBAAgB;YACnB,OAAO,oFAAoF,CAAA;IAC/F,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,OAAO,CAAS;IAEzB,YAAY,OAAgB;QAC1B,KAAK,CAAC,GAAG,OAAO,0CAA0C,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC/E,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;CACF;AAID,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,GAAkB,EAAE,OAAsB;IACnE,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAA;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,CAAA;IACnF,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,GAAkB;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAc,CAAA;IACxD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,KAAK,CAAC,GAAkB,EAAE,OAAgB;IACjD,IAAI,aAAa,CAAC,OAAO,CAAC,KAAK,gBAAgB,EAAE,CAAC;QAChD,sFAAsF;QACtF,+FAA+F;QAC/F,OAAO,OAAO,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAA;IACpD,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAA;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;AACtD,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,uBAAuB,CAAC,OAA2B;IACjE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzF,OAAO,gFAAgF,QAAQ,GAAG,CAAA;AACpG,CAAC"}
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -22,8 +22,18 @@ export type GatekeeperReason = 'unknown_actor' | 'unknown_tier' | 'binding_not_g
|
|
|
22
22
|
| 'session_ended'
|
|
23
23
|
/** The URL the workspace asked for is not one this Gatekeeper serves. */
|
|
24
24
|
| 'no_such_resource'
|
|
25
|
-
/** Sharing an observation onward was refused
|
|
25
|
+
/** Sharing an observation onward was refused: the observer could not read all of it directly. */
|
|
26
26
|
| 'sharing_refused'
|
|
27
|
+
/** Sharing was refused because the observer could not be identified at all. */
|
|
28
|
+
| 'sharing_unverifiable'
|
|
29
|
+
/** The call carries an argument the operation does not declare, so it would have been dropped. */
|
|
30
|
+
| 'undeclared_argument'
|
|
31
|
+
/** The call omits an argument the operation requires. */
|
|
32
|
+
| 'missing_argument'
|
|
33
|
+
/** This door serves no hooks: it has no approval queue to register one with. */
|
|
34
|
+
| 'hooks_unavailable'
|
|
35
|
+
/** The workspace's own side did not take a hook binding, and said why. */
|
|
36
|
+
| 'hook_bind_refused'
|
|
27
37
|
/** The deployment's entry module does not export something the OS object model needs. */
|
|
28
38
|
| 'missing_export';
|
|
29
39
|
/** A refusal the caller (or the OS deployment operating it) acts on. */
|
|
@@ -31,4 +41,13 @@ export declare class GatekeeperError extends Error {
|
|
|
31
41
|
readonly reason: GatekeeperReason;
|
|
32
42
|
constructor(reason: GatekeeperReason, message: string);
|
|
33
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* A failure as a refusal or a record carries it: one line, never an object nothing can render.
|
|
46
|
+
*
|
|
47
|
+
* Here rather than beside each caller because three of them (a hook push, a bind the workspace did
|
|
48
|
+
* not take, a verifier that could not be questioned) fold a cause into prose a person reads, and
|
|
49
|
+
* three copies of the same two lines is how they drift into disagreeing about what an
|
|
50
|
+
* `AggregateError` or a thrown string looks like.
|
|
51
|
+
*/
|
|
52
|
+
export declare function describeError(error: unknown): string;
|
|
34
53
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAcA,uEAAuE;AACvE,qBAAa,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,cAAc,GACd,qBAAqB,GACrB,gBAAgB,GAChB,uBAAuB,GACvB,oBAAoB;AACtB,mFAAmF;GACjF,gBAAgB;AAClB,sEAAsE;GACpE,cAAc;AAChB,0EAA0E;GACxE,gBAAgB;AAClB,uFAAuF;GACrF,oBAAoB;AACtB,kFAAkF;GAChF,qBAAqB;AACvB,mFAAmF;GACjF,iBAAiB;AACnB,gGAAgG;GAC9F,gBAAgB;AAClB,sFAAsF;GACpF,eAAe;AACjB,yEAAyE;GACvE,kBAAkB;AACpB,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAcA,uEAAuE;AACvE,qBAAa,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,mEAAmE;AACnE,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,cAAc,GACd,qBAAqB,GACrB,gBAAgB,GAChB,uBAAuB,GACvB,oBAAoB;AACtB,mFAAmF;GACjF,gBAAgB;AAClB,sEAAsE;GACpE,cAAc;AAChB,0EAA0E;GACxE,gBAAgB;AAClB,uFAAuF;GACrF,oBAAoB;AACtB,kFAAkF;GAChF,qBAAqB;AACvB,mFAAmF;GACjF,iBAAiB;AACnB,gGAAgG;GAC9F,gBAAgB;AAClB,sFAAsF;GACpF,eAAe;AACjB,yEAAyE;GACvE,kBAAkB;AACpB,iGAAiG;GAC/F,iBAAiB;AACnB,+EAA+E;GAC7E,sBAAsB;AACxB,kGAAkG;GAChG,qBAAqB;AACvB,yDAAyD;GACvD,kBAAkB;AACpB,gFAAgF;GAC9E,mBAAmB;AACrB,0EAA0E;GACxE,mBAAmB;AACrB,yFAAyF;GACvF,gBAAgB,CAAA;AAEpB,wEAAwE;AACxE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;IAEjC,YAAY,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAIpD;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
|
package/dist/errors.js
CHANGED
|
@@ -27,4 +27,15 @@ export class GatekeeperError extends Error {
|
|
|
27
27
|
this.reason = reason;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* A failure as a refusal or a record carries it: one line, never an object nothing can render.
|
|
32
|
+
*
|
|
33
|
+
* Here rather than beside each caller because three of them (a hook push, a bind the workspace did
|
|
34
|
+
* not take, a verifier that could not be questioned) fold a cause into prose a person reads, and
|
|
35
|
+
* three copies of the same two lines is how they drift into disagreeing about what an
|
|
36
|
+
* `AggregateError` or a thrown string looks like.
|
|
37
|
+
*/
|
|
38
|
+
export function describeError(error) {
|
|
39
|
+
return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
40
|
+
}
|
|
30
41
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,EAAE;AACF,4FAA4F;AAC5F,+FAA+F;AAC/F,6FAA6F;AAC7F,mGAAmG;AACnG,mGAAmG;AACnG,2EAA2E;AAC3E,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,iGAAiG;AACjG,oEAAoE;AAEpE,uEAAuE;AACvE,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;IAC3B,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,EAAE;AACF,4FAA4F;AAC5F,+FAA+F;AAC/F,6FAA6F;AAC7F,mGAAmG;AACnG,mGAAmG;AACnG,2EAA2E;AAC3E,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,iGAAiG;AACjG,oEAAoE;AAEpE,uEAAuE;AACvE,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;IAC3B,CAAC;CACF;AA2CD,wEAAwE;AACxE,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,MAAM,CAAkB;IAEjC,YAAY,MAAwB,EAAE,OAAe;QACnD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACnF,CAAC"}
|
package/dist/gatekeeper.d.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
import { type SessionGovernance } from './capability.js';
|
|
2
2
|
import { type GatekeeperEnv } from './env.js';
|
|
3
3
|
import { type Actor } from './keys.js';
|
|
4
|
+
import type { HookTopic } from './os/hooks.js';
|
|
4
5
|
import { type CompiledTier, type GatekeeperPolicy } from './policy/compile.js';
|
|
5
6
|
import { type VerificationResult } from './webhook/signature.js';
|
|
6
7
|
/** Why a delivery was refused, drawn from the verifier so the two cannot drift. */
|
|
7
8
|
type RejectionReason = Extract<VerificationResult, {
|
|
8
9
|
ok: false;
|
|
9
10
|
}>['reason'];
|
|
11
|
+
/**
|
|
12
|
+
* What the receiver handed to the hook fan-out, which is DISPATCH and never delivery.
|
|
13
|
+
*
|
|
14
|
+
* The distinction is the whole reason this shape changed. The fan-out runs behind the
|
|
15
|
+
* acknowledgement, so at the moment this is reported no push has been made and any count of them
|
|
16
|
+
* would be a zero standing in for a number nobody has yet: the one reading a receiver must never
|
|
17
|
+
* offer, because it is indistinguishable from a delivery no hook took. What each push DID land on
|
|
18
|
+
* its own registration's counters, which `hooks_bound()` publishes.
|
|
19
|
+
*/
|
|
20
|
+
export interface HookDispatchReceipt {
|
|
21
|
+
/** How many payloads this delivery produced. Zero when no topic covers it. */
|
|
22
|
+
pushes: number;
|
|
23
|
+
/** The distinct topics they carry, so the platform's delivery log names what was dispatched. */
|
|
24
|
+
topics: HookTopic[];
|
|
25
|
+
}
|
|
10
26
|
/** What taking delivery of one webhook POST did. */
|
|
11
27
|
export type DeliveryOutcome = {
|
|
12
28
|
handled: 'rejected';
|
|
@@ -20,6 +36,7 @@ export type DeliveryOutcome = {
|
|
|
20
36
|
handled: 'accepted';
|
|
21
37
|
deliveryId: string;
|
|
22
38
|
effect: 'opened' | 'superseded' | 'run-event' | 'none';
|
|
39
|
+
hooks: HookDispatchReceipt;
|
|
23
40
|
};
|
|
24
41
|
export declare class Gatekeeper {
|
|
25
42
|
#private;
|
|
@@ -84,6 +101,15 @@ export declare class Gatekeeper {
|
|
|
84
101
|
* would be the wrong shape there: not having opted in is a state, not a fault.
|
|
85
102
|
*/
|
|
86
103
|
get autoProvisionedTierName(): string | null;
|
|
104
|
+
/**
|
|
105
|
+
* Whether this deployment minted the named account.
|
|
106
|
+
*
|
|
107
|
+
* Asked by the ONE caller that is handed an account id from outside (`addObserver`, over an
|
|
108
|
+
* observer's verifier) and has to resolve a tier for it. Every other account id in this package
|
|
109
|
+
* arrives on `ctx.props` of a stub this Gatekeeper handed the workspace, so it was minted here
|
|
110
|
+
* by construction and asking again would be a lookup with no question behind it.
|
|
111
|
+
*/
|
|
112
|
+
recognizesAccount(accountId: string): Promise<boolean>;
|
|
87
113
|
/** The origin of the cat-factory deployment this Gatekeeper is paired with. */
|
|
88
114
|
get deployment(): string;
|
|
89
115
|
/**
|
|
@@ -116,8 +142,12 @@ export declare class Gatekeeper {
|
|
|
116
142
|
* Deduping in a call of its own would be worse than useless: a marker committed before the card
|
|
117
143
|
* write turns a failed write into a `duplicate` on the platform's retry, so the approval never
|
|
118
144
|
* reaches the inbox and nothing anywhere reports that it did not.
|
|
145
|
+
*
|
|
146
|
+
* `defer` is how the fan-out is kept OFF that path, and it is a parameter rather than something
|
|
147
|
+
* read here because the lifetime being extended is the Worker invocation's, which only the
|
|
148
|
+
* handler holds.
|
|
119
149
|
*/
|
|
120
|
-
takeDelivery(request: Request, now: number): Promise<DeliveryOutcome>;
|
|
150
|
+
takeDelivery(request: Request, now: number, defer: (work: Promise<unknown>) => void): Promise<DeliveryOutcome>;
|
|
121
151
|
}
|
|
122
152
|
export {};
|
|
123
153
|
//# sourceMappingURL=gatekeeper.d.ts.map
|
package/dist/gatekeeper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gatekeeper.d.ts","sourceRoot":"","sources":["../src/gatekeeper.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,
|
|
1
|
+
{"version":3,"file":"gatekeeper.d.ts","sourceRoot":"","sources":["../src/gatekeeper.ts"],"names":[],"mappings":"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"}
|
package/dist/gatekeeper.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// forking it.
|
|
10
10
|
import { CatFactoryClient } from '@cat-factory/sdk';
|
|
11
11
|
import { buildCapability } from './capability.js';
|
|
12
|
-
import { requireVar } from './env.js';
|
|
12
|
+
import { requireVar, stateFor } from './env.js';
|
|
13
13
|
import { GatekeeperError } from './errors.js';
|
|
14
14
|
import { KeyBroker } from './keys.js';
|
|
15
15
|
import { autoProvisionedTier, compilePolicy, tierForAccount, tierForActor, } from './policy/compile.js';
|
|
@@ -36,10 +36,7 @@ export class Gatekeeper {
|
|
|
36
36
|
this.#policy = policy;
|
|
37
37
|
const baseUrl = requireVar(env, 'CAT_FACTORY_BASE_URL');
|
|
38
38
|
const clientFor = (apiKey) => new CatFactoryClient({ baseUrl, apiKey, userAgent: USER_AGENT });
|
|
39
|
-
|
|
40
|
-
// is the pairing's own identity, so pointing this Worker at a different cat-factory gets a
|
|
41
|
-
// different object rather than inheriting the previous one's cards and minted keys.
|
|
42
|
-
this.#state = env.STATE.get(env.STATE.idFromName(baseUrl));
|
|
39
|
+
this.#state = stateFor(env);
|
|
43
40
|
this.#keys = new KeyBroker({
|
|
44
41
|
state: this.#state,
|
|
45
42
|
provisioning: clientFor(requireVar(env, 'PROVISIONING_KEY')),
|
|
@@ -149,6 +146,17 @@ export class Gatekeeper {
|
|
|
149
146
|
get autoProvisionedTierName() {
|
|
150
147
|
return this.#policy.autoProvisionedTier;
|
|
151
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Whether this deployment minted the named account.
|
|
151
|
+
*
|
|
152
|
+
* Asked by the ONE caller that is handed an account id from outside (`addObserver`, over an
|
|
153
|
+
* observer's verifier) and has to resolve a tier for it. Every other account id in this package
|
|
154
|
+
* arrives on `ctx.props` of a stub this Gatekeeper handed the workspace, so it was minted here
|
|
155
|
+
* by construction and asking again would be a lookup with no question behind it.
|
|
156
|
+
*/
|
|
157
|
+
async recognizesAccount(accountId) {
|
|
158
|
+
return await this.#state.hasAccount(accountId);
|
|
159
|
+
}
|
|
152
160
|
/** The origin of the cat-factory deployment this Gatekeeper is paired with. */
|
|
153
161
|
get deployment() {
|
|
154
162
|
return requireVar(this.#env, 'CAT_FACTORY_BASE_URL');
|
|
@@ -206,8 +214,12 @@ export class Gatekeeper {
|
|
|
206
214
|
* Deduping in a call of its own would be worse than useless: a marker committed before the card
|
|
207
215
|
* write turns a failed write into a `duplicate` on the platform's retry, so the approval never
|
|
208
216
|
* reaches the inbox and nothing anywhere reports that it did not.
|
|
217
|
+
*
|
|
218
|
+
* `defer` is how the fan-out is kept OFF that path, and it is a parameter rather than something
|
|
219
|
+
* read here because the lifetime being extended is the Worker invocation's, which only the
|
|
220
|
+
* handler holds.
|
|
209
221
|
*/
|
|
210
|
-
async takeDelivery(request, now) {
|
|
222
|
+
async takeDelivery(request, now, defer) {
|
|
211
223
|
const rawBody = await request.text();
|
|
212
224
|
const verdict = await verifyDelivery(request.headers, rawBody, requireVar(this.#env, 'WEBHOOK_SECRET'), now);
|
|
213
225
|
if (!verdict.ok)
|
|
@@ -222,12 +234,30 @@ export class Gatekeeper {
|
|
|
222
234
|
const delivery = readDelivery(parsed);
|
|
223
235
|
if (delivery === null)
|
|
224
236
|
return { handled: 'unparseable' };
|
|
225
|
-
const
|
|
237
|
+
const effect = cardEffectOf(delivery);
|
|
238
|
+
const applied = await this.#state.applyDelivery(delivery.deliveryId, effect, now);
|
|
226
239
|
if (!applied.applied)
|
|
227
240
|
return { handled: 'duplicate', deliveryId: delivery.deliveryId };
|
|
228
|
-
|
|
241
|
+
// AFTER the write and BESIDE the acknowledgement, never in front of it. The card is the
|
|
242
|
+
// durable truth and the push is the accelerator over it, so a workspace that hangs or refuses
|
|
243
|
+
// must cost a notification and never the record. Awaiting the fan-out here made it cost both:
|
|
244
|
+
// one slow workspace times the delivery out, the platform retries to protect a write that had
|
|
245
|
+
// already committed, and the retry is deduped into pushing nothing at all, so the notification
|
|
246
|
+
// the wait was supposed to guarantee is the one thing certain to be lost.
|
|
247
|
+
if (applied.pushes.length > 0)
|
|
248
|
+
defer(this.#state.dispatchHooks(applied.pushes, now));
|
|
249
|
+
return {
|
|
250
|
+
handled: 'accepted',
|
|
251
|
+
deliveryId: delivery.deliveryId,
|
|
252
|
+
effect: applied.effect,
|
|
253
|
+
hooks: receiptFor(applied.pushes),
|
|
254
|
+
};
|
|
229
255
|
}
|
|
230
256
|
}
|
|
257
|
+
/** What was handed to the fan-out, summarised for the platform's own delivery log. */
|
|
258
|
+
function receiptFor(pushes) {
|
|
259
|
+
return { pushes: pushes.length, topics: [...new Set(pushes.map((push) => push.topic))] };
|
|
260
|
+
}
|
|
231
261
|
/**
|
|
232
262
|
* Compare two secrets without an early exit on the first differing character.
|
|
233
263
|
*
|