@cat-factory/gatekeeper-worker 0.4.3 → 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 +100 -13
- 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 +55 -0
- package/dist/capability.d.ts.map +1 -1
- package/dist/capability.js +99 -5
- 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 +32 -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 +78 -3
- package/dist/gatekeeper.d.ts.map +1 -1
- package/dist/gatekeeper.js +127 -13
- package/dist/gatekeeper.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/markdown.d.ts +12 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +28 -0
- package/dist/markdown.js.map +1 -0
- package/dist/os/account.d.ts +27 -0
- package/dist/os/account.d.ts.map +1 -0
- package/dist/os/account.js +130 -0
- package/dist/os/account.js.map +1 -0
- package/dist/os/descriptions.d.ts +43 -0
- package/dist/os/descriptions.d.ts.map +1 -0
- package/dist/os/descriptions.js +100 -0
- package/dist/os/descriptions.js.map +1 -0
- package/dist/os/discoverability.d.ts +42 -0
- package/dist/os/discoverability.d.ts.map +1 -0
- package/dist/os/discoverability.js +63 -0
- package/dist/os/discoverability.js.map +1 -0
- package/dist/os/exports.d.ts +55 -0
- package/dist/os/exports.d.ts.map +1 -0
- package/dist/os/exports.js +93 -0
- package/dist/os/exports.js.map +1 -0
- 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 +193 -0
- package/dist/os/protocol.d.ts.map +1 -0
- package/dist/os/protocol.js +22 -0
- package/dist/os/protocol.js.map +1 -0
- package/dist/os/queue.d.ts +99 -0
- package/dist/os/queue.d.ts.map +1 -0
- package/dist/os/queue.js +240 -0
- package/dist/os/queue.js.map +1 -0
- package/dist/os/resource-core.d.ts +115 -0
- package/dist/os/resource-core.d.ts.map +1 -0
- package/dist/os/resource-core.js +172 -0
- package/dist/os/resource-core.js.map +1 -0
- package/dist/os/resource.d.ts +16 -0
- package/dist/os/resource.d.ts.map +1 -0
- package/dist/os/resource.js +70 -0
- package/dist/os/resource.js.map +1 -0
- package/dist/os/resources.d.ts +11 -0
- package/dist/os/resources.d.ts.map +1 -0
- package/dist/os/resources.js +26 -0
- package/dist/os/resources.js.map +1 -0
- package/dist/os/session-types.d.ts +6 -0
- package/dist/os/session-types.d.ts.map +1 -0
- package/dist/os/session-types.js +110 -0
- package/dist/os/session-types.js.map +1 -0
- 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 +14 -0
- package/dist/os/vendor.d.ts.map +1 -0
- package/dist/os/vendor.js +102 -0
- package/dist/os/vendor.js.map +1 -0
- package/dist/policy/compile.d.ts +38 -0
- package/dist/policy/compile.d.ts.map +1 -1
- package/dist/policy/compile.js +42 -4
- package/dist/policy/compile.js.map +1 -1
- package/dist/policy/index.d.ts +1 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +1 -1
- package/dist/policy/index.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 +28 -6
- package/dist/worker.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,10 +3,23 @@
|
|
|
3
3
|
## What it is
|
|
4
4
|
|
|
5
5
|
The Cloudflare Worker machinery behind a cat-factory **Gatekeeper**: a credential-holding front
|
|
6
|
-
end that lets a [Cloudflare OS](https://github.com/cloudflare/cloudflare-os) workspace
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
end that lets a [Cloudflare OS](https://github.com/cloudflare/cloudflare-os) workspace drive
|
|
7
|
+
cat-factory without an agent ever seeing a credential. Agents hold an object-capability whose
|
|
8
|
+
methods are exactly what policy granted; the keys stay in Worker secrets and Durable Object
|
|
9
|
+
storage.
|
|
10
|
+
|
|
11
|
+
It serves TWO doors onto the same rooms, and which one a caller comes in by decides how it is
|
|
12
|
+
authorized:
|
|
13
|
+
|
|
14
|
+
| Door | Who comes in by it | Authorization |
|
|
15
|
+
| --------------------------------- | ---------------------------------------------------------------- | ------------------- |
|
|
16
|
+
| The `GatekeeperVendor` entrypoint | A Cloudflare OS workspace, over a `GATEKEEPER_*` service binding | Holding the binding |
|
|
17
|
+
| `ALL /rpc` (Cap'n Web over HTTP) | Any other agent runtime that speaks Cap'n Web | `OS_SHARED_TOKEN` |
|
|
18
|
+
|
|
19
|
+
The published Cloudflare OS contract reaches a gatekeeper over native Workers RPC to a
|
|
20
|
+
`WorkerEntrypoint` export named `GatekeeperVendor`; Cap'n Web is that workspace's browser-to-backend
|
|
21
|
+
and gadget-side protocol, which shares the semantics and not the wire. So `/rpc` is the door for
|
|
22
|
+
everything that is NOT a Cloudflare OS deployment, and nothing here is Cloudflare-OS-only.
|
|
10
23
|
|
|
11
24
|
It is the machinery half of the Gatekeeper family:
|
|
12
25
|
|
|
@@ -83,9 +96,20 @@ version has to be the consumer's.
|
|
|
83
96
|
|
|
84
97
|
## What it does
|
|
85
98
|
|
|
86
|
-
- **Object-capability bindings
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
- **Object-capability bindings.** An agent holds an object whose METHODS are the operations policy
|
|
100
|
+
granted it. There is no allow-list consulted per call, because there is nothing to consult: an
|
|
101
|
+
operation the tier does not carry is not a method that refuses, it is absent.
|
|
102
|
+
- **The Cloudflare OS object model, as a facade over all of it.** `GatekeeperVendor` →
|
|
103
|
+
`CatFactoryAccount` → `CatFactoryResource` → session, with `describe()`,
|
|
104
|
+
`getSupportedResources()` and `getTypeScriptTypes()` all PROJECTIONS of the operation table
|
|
105
|
+
rather than transcriptions of it. A resource is the paired cat-factory workspace, named by a
|
|
106
|
+
URLPattern over the deployment origin: one Gatekeeper serves one workspace, because the
|
|
107
|
+
provisioning key it holds is scoped to one.
|
|
108
|
+
- **The workspace's approval queue, in front of every call.** On the entrypoint path each read is
|
|
109
|
+
authorized before it is MADE, and each write is SUBMITTED and performed only when
|
|
110
|
+
the workspace applies it. Reads that serve captured agent text are marked unshareable, actions
|
|
111
|
+
carry the consequence the table states, and nothing is offered for unattended auto-approval while
|
|
112
|
+
the surface annotates no write as safe. The tier policy underneath stays the floor.
|
|
89
113
|
- **Per-actor credentials.** Each caller gets their own cat-factory key, minted through
|
|
90
114
|
`POST /api/v1/keys` at the tier's scope and stamped with your identity for that person
|
|
91
115
|
(`externalIdentity`), so a run traces back to a human and role-scoped merge policy stays real.
|
|
@@ -99,6 +123,26 @@ version has to be the consumer's.
|
|
|
99
123
|
caller's own key.
|
|
100
124
|
- **Run lifecycle without polling.** `run.started` / `run.completed` / `run.failed` land as a
|
|
101
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.
|
|
102
146
|
- **Self-enrolment and offboarding.** The endpoint registers itself under a caller-chosen webhook
|
|
103
147
|
id, hourly and idempotently. `POST /admin/retire?actorId=…` revokes every key minted for one
|
|
104
148
|
person, upstream first and then here.
|
|
@@ -138,14 +182,41 @@ The Worker serves five routes:
|
|
|
138
182
|
| Route | Auth | What it is |
|
|
139
183
|
| ------------------------------ | ----------------- | ------------------------------------------------------------- |
|
|
140
184
|
| `POST /webhook` | delivery HMAC | The platform's outbound deliveries. Verified over raw bytes. |
|
|
141
|
-
| `ALL /rpc` | `OS_SHARED_TOKEN` |
|
|
185
|
+
| `ALL /rpc` | `OS_SHARED_TOKEN` | Cap'n Web, for an agent runtime that is not a Cloudflare OS. |
|
|
142
186
|
| `POST /admin/enroll` | `OS_SHARED_TOKEN` | Re-assert the webhook registration. Also runs hourly on cron. |
|
|
143
187
|
| `POST /admin/retire?actorId=…` | `OS_SHARED_TOKEN` | Offboarding: revoke every key minted for one OS user. |
|
|
144
|
-
| `GET /health` | none | Green
|
|
188
|
+
| `GET /health` | none | Green when every binding is set and the policy compiles. |
|
|
189
|
+
|
|
190
|
+
`/rpc` is bearer-gated because a Worker with a route attached is reachable by anyone who finds it,
|
|
191
|
+
and a capability surface whose only defence is obscurity is not one. The Cloudflare OS path does not
|
|
192
|
+
come through here at all: it arrives on a service binding, which never traverses the internet and
|
|
193
|
+
which only that deployment's operator can write, so holding it IS the authorization and a second
|
|
194
|
+
secret in front of it would protect nothing.
|
|
195
|
+
|
|
196
|
+
### `/health` also reports whether a Cloudflare OS could install this
|
|
197
|
+
|
|
198
|
+
Two things decide that, and neither has a request path of its own: the entry module must export the
|
|
199
|
+
four names the object model resolves (`GatekeeperVendor`, `CatFactoryAccount`,
|
|
200
|
+
`CatFactoryResource`, `CatFactoryVerifier`), and the policy must name an `autoProvisionedTier`. A
|
|
201
|
+
workspace that finds either missing does not get an error anyone monitors; it never finishes
|
|
202
|
+
installing. So a green response carries the answer beside `ok`:
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{ "ok": true, "os": { "discoverable": true, "blockers": [], "limitations": [] } }
|
|
206
|
+
```
|
|
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.
|
|
145
212
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
213
|
+
It is REPORTED, never folded into the status, because a Gatekeeper serving `/rpc` and nothing else
|
|
214
|
+
is a supported deployment: this package promises that door to consumers that are not a Cloudflare
|
|
215
|
+
OS, and turning their liveness red on a version bump would be this route answering a question
|
|
216
|
+
nobody asked it. A deployment that wants discovery keys a monitor on `os.discoverable`; each entry
|
|
217
|
+
in `blockers` carries a `reason` (`missing_exports`, `no_auto_provisioned_tier`) and a `detail`
|
|
218
|
+
naming the fix, and both are reported in one pass so a half-wired deployment is not wired one
|
|
219
|
+
redeploy at a time.
|
|
149
220
|
|
|
150
221
|
## What to customize: the policy
|
|
151
222
|
|
|
@@ -191,7 +262,7 @@ path that matches nothing is not an error, because result shapes legitimately va
|
|
|
191
262
|
`connect({ actorId, label? })` on the `/rpc` session resolves the actor's tier and returns the
|
|
192
263
|
capability. `actorId` is the OS's own authenticated identity for the person, and it is the ONLY
|
|
193
264
|
claim the Gatekeeper trusts: nothing the caller sends picks a tier. Beyond the granted operation
|
|
194
|
-
methods, every capability carries
|
|
265
|
+
methods, every capability carries ten reserved methods:
|
|
195
266
|
|
|
196
267
|
- `tier()`: who the caller is acting as (actor, tier name, description, key scope).
|
|
197
268
|
- `bindings()`: the granted operations, each with its scope floor, consequence (cautious default
|
|
@@ -205,6 +276,22 @@ methods, every capability carries seven reserved methods:
|
|
|
205
276
|
- `approvals_list()`, `approvals_inspect(cardId)`, `approvals_answer(cardId, input)`: the
|
|
206
277
|
approval inbox; see the template README for the flow and the three answer outcomes.
|
|
207
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.
|
|
208
295
|
|
|
209
296
|
Refusals from a live Gatekeeper are `GatekeeperError`s carrying a machine-readable `reason`
|
|
210
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,12 +1,67 @@
|
|
|
1
1
|
import { RpcTarget } from 'capnweb';
|
|
2
|
+
import type { GatekeeperBinding } from '@cat-factory/gatekeeper-bindings';
|
|
2
3
|
import type { Actor, KeyBroker } from './keys.js';
|
|
4
|
+
import { type HookTopic } from './os/hooks.js';
|
|
3
5
|
import { type CompiledTier } from './policy/compile.js';
|
|
4
6
|
import type { ApprovalCard, GatekeeperState } from './state.js';
|
|
7
|
+
/**
|
|
8
|
+
* The governance a session's calls pass through, when the door it arrived by has one.
|
|
9
|
+
*
|
|
10
|
+
* This is the ONE seam the Cloudflare OS approval queue is threaded onto, and it is one seam
|
|
11
|
+
* because `invoke` below is one closure: every granted operation, and every reserved method that
|
|
12
|
+
* reads platform data, funnels through it. A door with no governance passes `undefined` and gets
|
|
13
|
+
* byte-for-byte the prior behaviour, which is what keeps the `/rpc` surface a promise this package
|
|
14
|
+
* can go on making.
|
|
15
|
+
*
|
|
16
|
+
* The tier policy is the FLOOR underneath it, never a substitute for it: an operation policy never
|
|
17
|
+
* granted is absent from the object, so governance is only ever asked about calls that exist.
|
|
18
|
+
*/
|
|
19
|
+
export interface SessionGovernance {
|
|
20
|
+
/**
|
|
21
|
+
* Authorize a read, BEFORE it is made. Throws to refuse, and a refusal means the upstream call
|
|
22
|
+
* never happened rather than happening and being withheld.
|
|
23
|
+
*/
|
|
24
|
+
observe(binding: GatekeeperBinding, args: Record<string, unknown>): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Authorize a read served from this Gatekeeper's own durable projection of delivered data.
|
|
27
|
+
*
|
|
28
|
+
* Separate from {@link observe} because there is no binding behind it, not because it is a
|
|
29
|
+
* lesser read: the cards and run states came from the paired deployment over the webhook, so
|
|
30
|
+
* showing them is an observation of that deployment.
|
|
31
|
+
*/
|
|
32
|
+
observeLocal(title: string, detail: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Submit a side effect and perform it only if it is approved.
|
|
35
|
+
*
|
|
36
|
+
* `perform` is the real call, invoked at most once and only after approval; a refusal throws
|
|
37
|
+
* instead of returning, so a refused action cannot read as a call that returned nothing.
|
|
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>;
|
|
49
|
+
/**
|
|
50
|
+
* The session is gone: release everything it was holding on the workspace's behalf.
|
|
51
|
+
*
|
|
52
|
+
* Called from the capability's own disposer, which the runtime runs when the last stub pointing
|
|
53
|
+
* at it is dropped. Without it a session's undecided actions and its hold on the workspace's
|
|
54
|
+
* queue would both outlive the only thing that could ever settle them.
|
|
55
|
+
*/
|
|
56
|
+
close(): void;
|
|
57
|
+
}
|
|
5
58
|
export interface SessionDependencies {
|
|
6
59
|
actor: Actor;
|
|
7
60
|
tier: CompiledTier;
|
|
8
61
|
keys: KeyBroker;
|
|
9
62
|
state: DurableObjectStub<GatekeeperState>;
|
|
63
|
+
/** Present on the OS entrypoint path; absent on `/rpc`, which governs by tier alone. */
|
|
64
|
+
governance?: SessionGovernance;
|
|
10
65
|
}
|
|
11
66
|
/** What `tier()` answers: enough for an OS Gadget to render who the caller is acting as. */
|
|
12
67
|
export interface TierSummary {
|
package/dist/capability.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"
|
|
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
|
@@ -10,10 +10,20 @@
|
|
|
10
10
|
// Methods live on a per-session PROTOTYPE, never on the instance. Cap'n Web deliberately refuses
|
|
11
11
|
// to serve an RpcTarget's own instance properties (they would leak private internals), so an
|
|
12
12
|
// instance-property capability would be a set of methods no caller can reach.
|
|
13
|
+
//
|
|
14
|
+
// ONE object serves both doors, which looks like it should not work and does. `RpcTarget` is
|
|
15
|
+
// imported from `capnweb` while the Cloudflare OS door returns this across NATIVE Workers RPC,
|
|
16
|
+
// which serializes only `cloudflare:workers` targets. They are the same class: inside workerd
|
|
17
|
+
// capnweb re-exports the runtime's own `RpcTarget` rather than declaring one, which is what lets a
|
|
18
|
+
// session cross either wire. It is stated here because the alternative reading (two incompatible
|
|
19
|
+
// base classes, a `DataCloneError` on the first bind) is the more obvious one.
|
|
13
20
|
import { RpcTarget } from 'capnweb';
|
|
14
21
|
import { answerCard, assertAnswerable, pendingParks, describeStale, } from './approvals.js';
|
|
22
|
+
import { checkedArguments } from './arguments.js';
|
|
15
23
|
import { GatekeeperError, PolicyError } from './errors.js';
|
|
16
24
|
import { applyMask } from './masking.js';
|
|
25
|
+
import { HOOK_TOPICS } from './os/hooks.js';
|
|
26
|
+
import { fenced } from './markdown.js';
|
|
17
27
|
import { describeBinding } from './policy/compile.js';
|
|
18
28
|
/**
|
|
19
29
|
* The methods a capability carries beyond its granted bindings.
|
|
@@ -30,7 +40,10 @@ const RESERVED_METHODS = [
|
|
|
30
40
|
'approvals_list',
|
|
31
41
|
'approvals_inspect',
|
|
32
42
|
'approvals_answer',
|
|
43
|
+
'approvals_subscribe',
|
|
33
44
|
'runs_watched',
|
|
45
|
+
'runs_subscribe',
|
|
46
|
+
'hooks_bound',
|
|
34
47
|
];
|
|
35
48
|
/**
|
|
36
49
|
* Build the capability for one connected actor.
|
|
@@ -49,17 +62,56 @@ export function buildCapability(deps) {
|
|
|
49
62
|
'method in capability.ts; a granted operation must never be shadowed by one.');
|
|
50
63
|
}
|
|
51
64
|
}
|
|
52
|
-
const invoke = async (name,
|
|
65
|
+
const invoke = async (name, bag) => {
|
|
53
66
|
const binding = granted.get(name);
|
|
54
67
|
if (binding === undefined) {
|
|
55
68
|
throw new GatekeeperError('binding_not_granted', `Tier '${tier.name}' does not grant '${name}'.`);
|
|
56
69
|
}
|
|
57
|
-
|
|
58
|
-
|
|
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);
|
|
75
|
+
const perform = async () => await deps.keys.run(deps.actor, tier.keyScope, (client) => binding.invoke(client, args));
|
|
76
|
+
const governance = deps.governance;
|
|
77
|
+
if (governance === undefined)
|
|
78
|
+
return applyMask(await perform(), tier.mask);
|
|
79
|
+
// A read is authorized BEFORE it is performed. The contract permits the other order (the
|
|
80
|
+
// result may be in hand when the authorizer is asked), and it buys nothing here while costing
|
|
81
|
+
// something real. It buys nothing because the description is DERIVED from the operation table:
|
|
82
|
+
// the route, the scope floor, the argument bag and whether the read serves captured agent text
|
|
83
|
+
// are all known before the call, and handing an approver the rows themselves would put the
|
|
84
|
+
// very bytes `prohibitAllSharing` exists to withhold into an approval prompt. It costs
|
|
85
|
+
// something because performing first means a refused read has already been made: a real
|
|
86
|
+
// request, with a key minted for the occasion, pulling a telemetry sink's captured prompts into
|
|
87
|
+
// this Worker in order to ask whether the workspace wanted them read.
|
|
88
|
+
if (binding.readOnly) {
|
|
89
|
+
await governance.observe(binding, args);
|
|
90
|
+
return applyMask(await perform(), tier.mask);
|
|
91
|
+
}
|
|
92
|
+
return applyMask(await governance.act(binding, args, perform), tier.mask);
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Authorize a read this Gatekeeper serves from its OWN durable projection.
|
|
96
|
+
*
|
|
97
|
+
* The cards and the run states were delivered by the platform, so serving them is an observation
|
|
98
|
+
* of the paired deployment exactly as a live read is; that the bytes are local is an
|
|
99
|
+
* implementation detail of how they arrived. Governing them keeps "everything the session shows
|
|
100
|
+
* you passed the authorizer" true, which is the property the OS is relying on.
|
|
101
|
+
*/
|
|
102
|
+
const observeLocal = async (title, detail) => {
|
|
103
|
+
await deps.governance?.observeLocal(title, detail);
|
|
59
104
|
};
|
|
60
105
|
class Capability extends RpcTarget {
|
|
61
106
|
}
|
|
62
107
|
const proto = Capability.prototype;
|
|
108
|
+
// The end of the session, in the one place the runtime signals it. Both doors dispose an
|
|
109
|
+
// `RpcTarget` whose stubs have all been dropped, and both call this on the way; a `/rpc` session
|
|
110
|
+
// brings no governance and so has nothing to release. It is keyed by SYMBOL, which is also why
|
|
111
|
+
// it cannot collide with a binding or be reached over RPC as a method.
|
|
112
|
+
proto[Symbol.dispose] = () => {
|
|
113
|
+
deps.governance?.close();
|
|
114
|
+
};
|
|
63
115
|
for (const binding of tier.granted) {
|
|
64
116
|
proto[binding.name] = (args = {}) => invoke(binding.name, args);
|
|
65
117
|
}
|
|
@@ -76,17 +128,59 @@ export function buildCapability(deps) {
|
|
|
76
128
|
// `withheld()` is the degrade-loudly half: an agent that cannot tell "your policy hides this"
|
|
77
129
|
// from "this deployment does not have it" reports the wrong one to whoever has to fix it.
|
|
78
130
|
proto.withheld = () => tier.withheld;
|
|
79
|
-
|
|
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
|
+
};
|
|
159
|
+
proto.approvals_list = async () => {
|
|
160
|
+
const cards = await deps.state.listCards();
|
|
161
|
+
await observeLocal('List the open approval cards', `${cards.length} card(s) the paired deployment raised and delivered to this Gatekeeper.`);
|
|
162
|
+
return cards;
|
|
163
|
+
};
|
|
80
164
|
// The lifecycle projection the `run.*` subscription feeds. Without it those deliveries would be
|
|
81
165
|
// verified, deduped and dropped, and a status Gadget would be back to polling `tasks_get_run`
|
|
82
166
|
// for a transition the platform already pushed.
|
|
83
|
-
proto.runs_watched = () =>
|
|
167
|
+
proto.runs_watched = async () => {
|
|
168
|
+
// Annotated rather than inferred: the Durable Object stub's serialization type narrows a
|
|
169
|
+
// `Record<string, unknown>` field to `never`, so the inferred result carries no `length`.
|
|
170
|
+
const runs = await deps.state.listRunStates();
|
|
171
|
+
await observeLocal('List the watched runs', `${runs.length} run(s) the paired deployment has pushed lifecycle events for.`);
|
|
172
|
+
return runs;
|
|
173
|
+
};
|
|
84
174
|
proto.approvals_inspect = async (cardId) => {
|
|
85
175
|
const card = await deps.state.getCard(cardId);
|
|
86
176
|
if (card === null) {
|
|
87
177
|
throw new GatekeeperError('card_not_found', `No approval card '${cardId}'. It may have been raised against a different paired ` +
|
|
88
178
|
'workspace, or predate this Gatekeeper.');
|
|
89
179
|
}
|
|
180
|
+
// The card's own title is agent- and user-authored: it came off a task somebody filed, rode
|
|
181
|
+
// the delivery in, and is about to be interpolated into Markdown a person reads in order to
|
|
182
|
+
// decide something. It gets the same fence the argument bag gets, for the same reason.
|
|
183
|
+
await observeLocal(`Inspect approval card ${cardId}`, `The card raised for run ${card.runId}, titled:\n\n${fenced(card.title, '')}`);
|
|
90
184
|
const list = (await invoke('decisions_list', { runId: card.runId }));
|
|
91
185
|
const parks = pendingParks(list).map((park) => ({
|
|
92
186
|
kind: park.kind,
|
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;
|
|
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,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE1D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,WAAW,EAAyC,MAAM,eAAe,CAAA;AAClF,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,qBAAqB;IACrB,cAAc;IACd,gBAAgB;IAChB,aAAa;CACL,CAAA;AA8GV;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAyB;IACvD,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;IACrB,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CACvD,CAAA;IAED,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,WAAW,CACnB,YAAY,QAAQ,oEAAoE;gBACtF,6EAA6E,CAChF,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,EAAE,IAAY,EAAE,GAA4B,EAAoB,EAAE;QACpF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,eAAe,CACvB,qBAAqB,EACrB,SAAS,IAAI,CAAC,IAAI,qBAAqB,IAAI,IAAI,CAChD,CAAA;QACH,CAAC;QACD,4FAA4F;QAC5F,4FAA4F;QAC5F,8FAA8F;QAC9F,yCAAyC;QACzC,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE,CACzB,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;QAE1F,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QAClC,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC,MAAM,OAAO,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAE1E,yFAAyF;QACzF,8FAA8F;QAC9F,+FAA+F;QAC/F,+FAA+F;QAC/F,2FAA2F;QAC3F,uFAAuF;QACvF,wFAAwF;QACxF,gGAAgG;QAChG,sEAAsE;QACtE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACvC,OAAO,SAAS,CAAC,MAAM,OAAO,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAC9C,CAAC;QACD,OAAO,SAAS,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAA;IAED;;;;;;;OAOG;IACH,MAAM,YAAY,GAAG,KAAK,EAAE,KAAa,EAAE,MAAc,EAAiB,EAAE;QAC1E,MAAM,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IACpD,CAAC,CAAA;IAED,MAAM,UAAW,SAAQ,SAAS;KAAG;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,SAAwD,CAAA;IAEjF,yFAAyF;IACzF,iGAAiG;IACjG,+FAA+F;IAC/F,uEAAuE;IACvE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,GAAS,EAAE;QACjC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,CAAA;IAC1B,CAAC,CAAA;IAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACnC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAA4B,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC1F,CAAC;IAED,KAAK,CAAC,IAAI,GAAG,GAAgB,EAAE,CAAC,CAAC;QAC/B,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;QACtB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAA;IAEF,+FAA+F;IAC/F,2FAA2F;IAC3F,gFAAgF;IAChF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAExD,8FAA8F;IAC9F,0FAA0F;IAC1F,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAA;IAEpC;;;;;;;;OAQG;IACH,MAAM,SAAS,GAAG,KAAK,EAAE,KAAgB,EAAE,QAAiB,EAAiB,EAAE;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,eAAe,CACvB,mBAAmB,EACnB,gEAAgE,WAAW,CAAC,KAAK,CAAC,CAAC,aAAa,KAAK;gBACnG,sFAAsF;gBACtF,yFAAyF,CAC5F,CAAA;QACH,CAAC;QACD,MAAM,UAAU,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC7C,CAAC,CAAA;IAED,KAAK,CAAC,mBAAmB,GAAG,KAAK,EAAE,QAAiB,EAAiB,EAAE,CACrE,SAAS,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;IAEtC,KAAK,CAAC,cAAc,GAAG,KAAK,EAAE,QAAiB,EAAiB,EAAE,CAChE,SAAS,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IAElC,gGAAgG;IAChG,+FAA+F;IAC/F,6DAA6D;IAC7D,KAAK,CAAC,WAAW,GAAG,KAAK,IAAiC,EAAE;QAC1D,MAAM,KAAK,GAAuB,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC3E,MAAM,YAAY,CAChB,uCAAuC,EACvC,GAAG,KAAK,CAAC,MAAM,qFAAqF,CACrG,CAAA;QACD,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,KAAK,CAAC,cAAc,GAAG,KAAK,IAA6B,EAAE;QACzD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAA;QAC1C,MAAM,YAAY,CAChB,8BAA8B,EAC9B,GAAG,KAAK,CAAC,MAAM,yEAAyE,CACzF,CAAA;QACD,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,gGAAgG;IAChG,8FAA8F;IAC9F,gDAAgD;IAChD,KAAK,CAAC,YAAY,GAAG,KAAK,IAAyB,EAAE;QACnD,yFAAyF;QACzF,0FAA0F;QAC1F,MAAM,IAAI,GAAe,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAA;QACzD,MAAM,YAAY,CAChB,uBAAuB,EACvB,GAAG,IAAI,CAAC,MAAM,gEAAgE,CAC/E,CAAA;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,KAAK,CAAC,iBAAiB,GAAG,KAAK,EAAE,MAAc,EAA2B,EAAE;QAC1E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAC7C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,eAAe,CACvB,gBAAgB,EAChB,qBAAqB,MAAM,wDAAwD;gBACjF,wCAAwC,CAC3C,CAAA;QACH,CAAC;QACD,4FAA4F;QAC5F,4FAA4F;QAC5F,uFAAuF;QACvF,MAAM,YAAY,CAChB,yBAAyB,MAAM,EAAE,EACjC,2BAA2B,IAAI,CAAC,KAAK,gBAAgB,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAC9E,CAAA;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAsB,CAAA;QACzF,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACjC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;gBAClC,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;SACJ,CAAC,CAAC,CAAA;QACH,OAAO;YACL,IAAI;YACJ,SAAS,EAAE,IAAI;YACf,KAAK;YACL,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAA;IACH,CAAC,CAAA;IAED,KAAK,CAAC,gBAAgB,GAAG,KAAK,EAAE,MAAc,EAAE,KAAkB,EAA0B,EAAE;QAC5F,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;QACvE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACrD,8FAA8F;QAC9F,4FAA4F;QAC5F,8FAA8F;QAC9F,8FAA8F;QAC9F,wFAAwF;QACxF,0CAA0C;QAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAClC,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;QACvF,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IAED,OAAO,IAAI,UAAU,EAAE,CAAA;AACzB,CAAC"}
|
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
|
@@ -13,10 +13,41 @@ export type GatekeeperReason = 'unknown_actor' | 'unknown_tier' | 'binding_not_g
|
|
|
13
13
|
/** The platform sent a parked decision without the id every answer to it addresses. */
|
|
14
14
|
| 'malformed_decision'
|
|
15
15
|
/** A minted credential was refused upstream and re-minting it was refused too. */
|
|
16
|
-
| 'credential_rejected'
|
|
16
|
+
| 'credential_rejected'
|
|
17
|
+
/** The workspace decided against a submitted action, so it was never performed. */
|
|
18
|
+
| 'action_rejected'
|
|
19
|
+
/** A decision named an action id this resource is not holding: already settled, or not ours. */
|
|
20
|
+
| 'unknown_action'
|
|
21
|
+
/** The session that submitted an action went away before the workspace decided it. */
|
|
22
|
+
| 'session_ended'
|
|
23
|
+
/** The URL the workspace asked for is not one this Gatekeeper serves. */
|
|
24
|
+
| 'no_such_resource'
|
|
25
|
+
/** Sharing an observation onward was refused: the observer could not read all of it directly. */
|
|
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'
|
|
37
|
+
/** The deployment's entry module does not export something the OS object model needs. */
|
|
38
|
+
| 'missing_export';
|
|
17
39
|
/** A refusal the caller (or the OS deployment operating it) acts on. */
|
|
18
40
|
export declare class GatekeeperError extends Error {
|
|
19
41
|
readonly reason: GatekeeperReason;
|
|
20
42
|
constructor(reason: GatekeeperReason, message: string);
|
|
21
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;
|
|
22
53
|
//# sourceMappingURL=errors.d.ts.map
|