@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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Whether one account may be shown what another has already read through this resource.
|
|
2
|
+
//
|
|
3
|
+
// The contract's requirement is exact and it is not "is this person trusted": `addObserver` must
|
|
4
|
+
// verify that the new viewer could DIRECTLY READ everything historically observed through the
|
|
5
|
+
// resource, and throw to block the share otherwise. Slice 8 shipped the blanket refusal, which was
|
|
6
|
+
// the right default while there was no rule worth writing down. This is the rule.
|
|
7
|
+
//
|
|
8
|
+
// It rests on one fact about this Gatekeeper that makes the question answerable at all: a resource
|
|
9
|
+
// object is bound FOR ONE ACCOUNT (`ResourceProps.accountId`), so every observation ever made
|
|
10
|
+
// through it was made at that account's tier. There is no observation log and there does not need
|
|
11
|
+
// to be one, because the tier is an upper bound on what any of those reads could have returned.
|
|
12
|
+
// So the test is a comparison of two compiled tiers:
|
|
13
|
+
//
|
|
14
|
+
// 1. The observer's tier GRANTS every operation the owner's tier grants. An operation the
|
|
15
|
+
// observer does not hold is one they could not have made themselves, whatever they are shown.
|
|
16
|
+
// 2. The observer's MASK hides nothing the owner's does not. Masking is redaction on the way
|
|
17
|
+
// out, so a field the observer's tier blanks is a field they cannot read directly, even
|
|
18
|
+
// through an operation they hold.
|
|
19
|
+
// 3. The owner's tier reaches NO TELEMETRY SINK. Those reads serve captured agent text and are
|
|
20
|
+
// described with `prohibitAllSharing`, which says the data may not be shared onward with any
|
|
21
|
+
// observer whatever their own access is. Since nothing here records which reads were actually
|
|
22
|
+
// made, a tier that COULD have made one is a tier whose observations may include it.
|
|
23
|
+
//
|
|
24
|
+
// Every failure is a refusal that names which of the three it is, because they need different
|
|
25
|
+
// answers: a grant an operator can widen, a mask they can align, and one they must not "fix" at
|
|
26
|
+
// all. And an observer this Gatekeeper cannot identify is refused separately again, because
|
|
27
|
+
// "unknown" and "not permitted" are the same outcome and opposite facts.
|
|
28
|
+
//
|
|
29
|
+
// WHICH IS WHY IDENTIFYING ONE IS TWO QUESTIONS, not one. Reading an account id off the verifier
|
|
30
|
+
// answers what the observer CLAIMS; it does not answer whether this deployment has ever heard of
|
|
31
|
+
// them, and the tier lookup behind the comparison cannot tell the difference on its own. It
|
|
32
|
+
// resolves an unknown id to the auto-provisioned tier, which is the tier nearly every account
|
|
33
|
+
// here holds, so a viewer from an entirely different vendor (whose verifier honestly names an
|
|
34
|
+
// account of THEIRS) resolved to the same tier as the owner and passed all three tests without
|
|
35
|
+
// holding a single one of the operations they were being shown. The comparison was answering
|
|
36
|
+
// "would an account like this be able to read it", which is not a question anyone asked.
|
|
37
|
+
//
|
|
38
|
+
// So an observer must be an account this Gatekeeper MINTED. That is a fact only the deployment's
|
|
39
|
+
// own state knows, so the caller supplies the recogniser and this module supplies the refusal:
|
|
40
|
+
// the message it already carried ("Observers must hold an account on this same Gatekeeper") is
|
|
41
|
+
// now enforced rather than asserted.
|
|
42
|
+
//
|
|
43
|
+
// The rule is deliberately conservative in the one direction that costs nothing but a share:
|
|
44
|
+
// every accepted observer can read everything the owner's tier can, so no observation ever needs
|
|
45
|
+
// excluding afterwards and `ObservationDescription.excludeObservers` stays unused rather than
|
|
46
|
+
// being a second, weaker gate on the same question.
|
|
47
|
+
import { describeError, GatekeeperError } from '../errors.js';
|
|
48
|
+
/** How many withheld operations a refusal names before it stops listing them. */
|
|
49
|
+
const NAMED_OPERATIONS = 5;
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the account behind an observer's verifier, or refuse.
|
|
52
|
+
*
|
|
53
|
+
* The verifier is the contract's own identity token: another vendor's gatekeeper is handed one,
|
|
54
|
+
* and the only thing it carries is who its holder is. Ours answers `{ accountId }`, so a stub that
|
|
55
|
+
* answers anything else did not come from this vendor and cannot be resolved against this
|
|
56
|
+
* deployment's policy. Answering the right SHAPE is not the same as being one of ours, which is
|
|
57
|
+
* why the id is then checked against the accounts this deployment actually minted.
|
|
58
|
+
*
|
|
59
|
+
* What that does NOT establish is worth stating, because the rule above is only as strong as this:
|
|
60
|
+
* a verifier that names an account of ours is taken at its word. The stub is reached over RPC from
|
|
61
|
+
* whoever the workspace handed us, so proving the holder IS that account would take a challenge
|
|
62
|
+
* the published contract does not carry. What is closed here is the case that needs no
|
|
63
|
+
* impersonation at all, which was every honest viewer from every other vendor.
|
|
64
|
+
*/
|
|
65
|
+
export async function identifyObserver(user, deps) {
|
|
66
|
+
const verifier = user;
|
|
67
|
+
if (typeof verifier?.describe !== 'function') {
|
|
68
|
+
throw new GatekeeperError('sharing_unverifiable', 'The observer offered no verifier this Gatekeeper can question, so there is no account to ' +
|
|
69
|
+
'resolve a tier for. A share is refused rather than granted to an identity nobody stated.');
|
|
70
|
+
}
|
|
71
|
+
let identity;
|
|
72
|
+
try {
|
|
73
|
+
identity = await verifier.describe();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new GatekeeperError('sharing_unverifiable', `The observer's verifier could not be questioned (${describeError(error)}), so the share ` +
|
|
77
|
+
'is refused: an identity this Gatekeeper failed to read is not an identity it may assume.');
|
|
78
|
+
}
|
|
79
|
+
if (typeof identity?.accountId !== 'string' || identity.accountId.length === 0) {
|
|
80
|
+
throw new GatekeeperError('sharing_unverifiable', "The observer's verifier named no account, so this deployment's policy has nothing to " +
|
|
81
|
+
'resolve a tier against. Observers must hold an account on this same Gatekeeper.');
|
|
82
|
+
}
|
|
83
|
+
if (!(await deps.recognize(identity.accountId))) {
|
|
84
|
+
throw new GatekeeperError('sharing_unverifiable', `This Gatekeeper did not mint account '${identity.accountId}', so it holds no tier for it ` +
|
|
85
|
+
'and cannot judge what its holder could read for themselves. A viewer connected to some ' +
|
|
86
|
+
'other vendor is refused here rather than measured against the tier an account of ours ' +
|
|
87
|
+
'would have had: that comparison would pass for a viewer holding none of these ' +
|
|
88
|
+
'operations. Connect this deployment as a vendor from that workspace and share with the ' +
|
|
89
|
+
'account it mints.');
|
|
90
|
+
}
|
|
91
|
+
return identity.accountId;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Refuse the share unless the observer could have read all of it themselves.
|
|
95
|
+
*
|
|
96
|
+
* Takes the two COMPILED tiers rather than resolving them, so the rule is testable without a
|
|
97
|
+
* policy, a Worker or an account: what it decides is a comparison, and everything else is lookup.
|
|
98
|
+
*/
|
|
99
|
+
export function assertObserverMaySee(deps) {
|
|
100
|
+
const { owner, observer } = deps;
|
|
101
|
+
const captured = owner.granted.filter((binding) => binding.telemetrySink !== undefined);
|
|
102
|
+
if (captured.length > 0) {
|
|
103
|
+
throw new GatekeeperError('sharing_refused', `This resource is bound at tier '${owner.name}', which can read captured agent text ` +
|
|
104
|
+
`(${names(captured.map((binding) => binding.name))}). Those reads are marked as not ` +
|
|
105
|
+
'shareable onward whatever the viewer holds, and nothing here records which of them were ' +
|
|
106
|
+
`actually made, so the share to '${deps.observerId}' is refused. A tier without the ` +
|
|
107
|
+
'telemetry operations can be shared.');
|
|
108
|
+
}
|
|
109
|
+
const held = new Set(observer.granted.map((binding) => binding.name));
|
|
110
|
+
const unreachable = owner.granted.filter((binding) => !held.has(binding.name));
|
|
111
|
+
if (unreachable.length > 0) {
|
|
112
|
+
throw new GatekeeperError('sharing_refused', `Account '${deps.observerAccount}' holds tier '${observer.name}', which does not grant ` +
|
|
113
|
+
`${names(unreachable.map((binding) => binding.name))}. This resource is bound at tier ` +
|
|
114
|
+
`'${owner.name}', so it may already have read those, and the share would show them what ` +
|
|
115
|
+
'they cannot read for themselves.');
|
|
116
|
+
}
|
|
117
|
+
const ownerMask = new Set(owner.mask);
|
|
118
|
+
const extraMask = observer.mask.filter((path) => !ownerMask.has(path));
|
|
119
|
+
if (extraMask.length > 0) {
|
|
120
|
+
throw new GatekeeperError('sharing_refused', `Tier '${observer.name}' masks ${names(extraMask)}, which tier '${owner.name}' does not. A ` +
|
|
121
|
+
'masked field is one this observer cannot read directly even through an operation they ' +
|
|
122
|
+
'hold, so the share is refused rather than served with the redaction undone.');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** A bounded list: enough to act on, never the whole surface pasted into a refusal. */
|
|
126
|
+
function names(all) {
|
|
127
|
+
const shown = all
|
|
128
|
+
.slice(0, NAMED_OPERATIONS)
|
|
129
|
+
.map((name) => `'${name}'`)
|
|
130
|
+
.join(', ');
|
|
131
|
+
return all.length > NAMED_OPERATIONS
|
|
132
|
+
? `${shown} and ${all.length - NAMED_OPERATIONS} more`
|
|
133
|
+
: shown;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=sharing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharing.js","sourceRoot":"","sources":["../../src/os/sharing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type GatekeeperEnv } from '../env.js';
|
|
2
|
+
import type { GatekeeperPolicy } from '../policy/compile.js';
|
|
3
|
+
import type { VendorEntrypoint } from './protocol.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build the vendor entrypoint class over a deployment's policy.
|
|
6
|
+
*
|
|
7
|
+
* The deployment exports the result under the name the OS pins:
|
|
8
|
+
*
|
|
9
|
+
* export const GatekeeperVendor = createGatekeeperVendor({ policy: POLICY })
|
|
10
|
+
*/
|
|
11
|
+
export declare function createGatekeeperVendor(options: {
|
|
12
|
+
policy: GatekeeperPolicy;
|
|
13
|
+
}): new (ctx: ExecutionContext, env: GatekeeperEnv) => VendorEntrypoint;
|
|
14
|
+
//# sourceMappingURL=vendor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vendor.d.ts","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG5D,OAAO,KAAK,EAAwC,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAe3F;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAA;CACzB,GAAG,KAAK,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE,aAAa,KAAK,gBAAgB,CAkEtE"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// The entrypoint a Cloudflare OS deployment discovers this Worker through.
|
|
2
|
+
//
|
|
3
|
+
// The workspace scans its own environment for `GATEKEEPER_*` SERVICE BINDINGS and treats each as a
|
|
4
|
+
// vendor, reaching the bound Worker's `GatekeeperVendor` entrypoint over native Workers RPC. That
|
|
5
|
+
// is a different door from `/rpc`, and the difference that matters is the authorization:
|
|
6
|
+
//
|
|
7
|
+
// - On this path, HOLDING THE BINDING IS THE AUTHORIZATION. A service binding is configuration
|
|
8
|
+
// only the OS deployment's operator can write, and the call never traverses the internet.
|
|
9
|
+
// There is no shared token in the published model and none is asked for here; adding one would
|
|
10
|
+
// be a second secret to rotate that protects nothing the binding does not already.
|
|
11
|
+
// - `/rpc` stays bearer-gated by `OS_SHARED_TOKEN`, because a Worker with a route attached is
|
|
12
|
+
// reachable by anyone who finds it. Nothing about that door changes: it is what a non-OS
|
|
13
|
+
// consumer speaks, and the promise that this Worker is not Cloudflare-OS-only rests on it.
|
|
14
|
+
//
|
|
15
|
+
// Everything the vendor answers is a PROJECTION of the operation table or of the policy compiled
|
|
16
|
+
// against it. Nothing here is transcribed, so a surface change cannot leave the description of it
|
|
17
|
+
// behind.
|
|
18
|
+
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
19
|
+
import { stateFor } from '../env.js';
|
|
20
|
+
import { Gatekeeper } from '../gatekeeper.js';
|
|
21
|
+
import { loopbackExport } from './exports.js';
|
|
22
|
+
import { supportedResourceFor } from './resources.js';
|
|
23
|
+
import { renderTierSessionTypes } from './session-types.js';
|
|
24
|
+
/**
|
|
25
|
+
* A fresh account id.
|
|
26
|
+
*
|
|
27
|
+
* Random rather than derived: the vendor is handed nothing to derive one FROM (`createAccount()`
|
|
28
|
+
* takes no arguments by design, so that a public method cannot be used to look an existing account
|
|
29
|
+
* up), and a guessable id would be one an agent could name in place of its own.
|
|
30
|
+
*/
|
|
31
|
+
function mintAccountId() {
|
|
32
|
+
return `acct_${crypto.randomUUID().replaceAll('-', '')}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build the vendor entrypoint class over a deployment's policy.
|
|
36
|
+
*
|
|
37
|
+
* The deployment exports the result under the name the OS pins:
|
|
38
|
+
*
|
|
39
|
+
* export const GatekeeperVendor = createGatekeeperVendor({ policy: POLICY })
|
|
40
|
+
*/
|
|
41
|
+
export function createGatekeeperVendor(options) {
|
|
42
|
+
const { policy } = options;
|
|
43
|
+
return class GatekeeperVendor extends WorkerEntrypoint {
|
|
44
|
+
#gatekeeper() {
|
|
45
|
+
return Gatekeeper.create(this.env, policy);
|
|
46
|
+
}
|
|
47
|
+
async describe() {
|
|
48
|
+
const deployment = this.#gatekeeper().deployment;
|
|
49
|
+
return {
|
|
50
|
+
displayName: 'cat-factory',
|
|
51
|
+
url: deployment,
|
|
52
|
+
tagline: 'Ship software with managed agents.',
|
|
53
|
+
description: 'File work onto a cat-factory board, start a pipeline run, watch it, and answer what it ' +
|
|
54
|
+
'parks on. Calls are made with a per-account API key this Gatekeeper mints and holds; ' +
|
|
55
|
+
'no agent ever sees a credential.',
|
|
56
|
+
// This vendor cannot authenticate anyone for sign-in: there is no per-user OAuth on the
|
|
57
|
+
// cat-factory side, so no provider-verified email exists to be one.
|
|
58
|
+
providesAuth: false,
|
|
59
|
+
// It CAN mint an account with no flow, which is the contract's own escape hatch for
|
|
60
|
+
// exactly this case. See `account.ts` for what that does and does not buy.
|
|
61
|
+
autoProvisionsAccount: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async getSupportedResources(_options) {
|
|
65
|
+
return [supportedResourceFor(this.#gatekeeper().deployment)];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The session types, rendered for the tier an auto-provisioned account will get.
|
|
69
|
+
*
|
|
70
|
+
* That is `autoProvisionedTier`, never `defaultTier`: the two are different knobs answering
|
|
71
|
+
* different questions (`policy/compile.ts` holds why), and this door only ever mints accounts.
|
|
72
|
+
* The tier is resolved WITHOUT an account, because there is none yet and inventing one to
|
|
73
|
+
* resolve through would make the refusal name an `acct_…` id no operator could find. The
|
|
74
|
+
* authoritative copy for a bound session is the resource's own `getTypeScriptTypes()`, which
|
|
75
|
+
* knows the tier the account actually resolved to and is the one `ResourceDescription.tsType`
|
|
76
|
+
* points at.
|
|
77
|
+
*/
|
|
78
|
+
async getTypeScriptTypes() {
|
|
79
|
+
return renderTierSessionTypes(this.#gatekeeper().tierForNewAccount());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Mint a new connected account.
|
|
83
|
+
*
|
|
84
|
+
* Deliberately creating-only and identity-free, which is what makes it safe on a public
|
|
85
|
+
* interface: it cannot look an existing account up, so holding the binding buys the ability to
|
|
86
|
+
* make an account and never the ability to reach somebody else's.
|
|
87
|
+
*/
|
|
88
|
+
async createAccount() {
|
|
89
|
+
const props = { accountId: mintAccountId() };
|
|
90
|
+
// Resolved before the account is handed back, so a deployment missing the export is refused
|
|
91
|
+
// at the call that would have created the account rather than at the first use of it.
|
|
92
|
+
const account = loopbackExport(this.ctx.exports, 'account', props);
|
|
93
|
+
// The one thing recorded about an account, and it is recorded HERE because this is the only
|
|
94
|
+
// moment the fact exists: minting is what makes an id one of ours, and `addObserver` has no
|
|
95
|
+
// other way to tell a viewer holding one from a viewer naming one. It lands before the stub
|
|
96
|
+
// does, so an account the workspace can use is never one this Gatekeeper would disown.
|
|
97
|
+
await stateFor(this.env).recordAccount(props.accountId, Date.now());
|
|
98
|
+
return account;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=vendor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../src/os/vendor.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,yFAAyF;AACzF,EAAE;AACF,iGAAiG;AACjG,8FAA8F;AAC9F,mGAAmG;AACnG,uFAAuF;AACvF,gGAAgG;AAChG,6FAA6F;AAC7F,+FAA+F;AAC/F,EAAE;AACF,iGAAiG;AACjG,kGAAkG;AAClG,UAAU;AAEV,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAsB,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAE3D;;;;;;GAMG;AACH,SAAS,aAAa;IACpB,OAAO,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAA;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAEtC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,gBAAiB,SAAQ,gBAA+B;QACnE,WAAW;YACT,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;QAED,KAAK,CAAC,QAAQ;YACZ,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAA;YAChD,OAAO;gBACL,WAAW,EAAE,aAAa;gBAC1B,GAAG,EAAE,UAAU;gBACf,OAAO,EAAE,oCAAoC;gBAC7C,WAAW,EACT,yFAAyF;oBACzF,uFAAuF;oBACvF,kCAAkC;gBACpC,wFAAwF;gBACxF,oEAAoE;gBACpE,YAAY,EAAE,KAAK;gBACnB,oFAAoF;gBACpF,2EAA2E;gBAC3E,qBAAqB,EAAE,IAAI;aAC5B,CAAA;QACH,CAAC;QAED,KAAK,CAAC,qBAAqB,CAAC,QAA8B;YACxD,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;QAC9D,CAAC;QAED;;;;;;;;;;WAUG;QACH,KAAK,CAAC,kBAAkB;YACtB,OAAO,sBAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAA;QACvE,CAAC;QAED;;;;;;WAMG;QACH,KAAK,CAAC,aAAa;YACjB,MAAM,KAAK,GAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,CAAA;YAC1D,4FAA4F;YAC5F,sFAAsF;YACtF,MAAM,OAAO,GAAG,cAAc,CAAU,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;YAC3E,4FAA4F;YAC5F,4FAA4F;YAC5F,4FAA4F;YAC5F,uFAAuF;YACvF,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;YACnE,OAAO,OAAO,CAAA;QAChB,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/policy/compile.d.ts
CHANGED
|
@@ -25,6 +25,25 @@ export interface TierPolicy {
|
|
|
25
25
|
export interface GatekeeperPolicy {
|
|
26
26
|
/** The tier an actor with no explicit grant is given. Name `null` to refuse unknown actors. */
|
|
27
27
|
defaultTier: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* The tier an AUTO-PROVISIONED account gets: the Cloudflare OS entrypoint's own default.
|
|
30
|
+
*
|
|
31
|
+
* A second knob rather than a reuse of `defaultTier`, because the two doors establish identity
|
|
32
|
+
* differently and an operator's answer about one is not their answer about the other.
|
|
33
|
+
* `defaultTier` is about a person the OS names on a `connect({ actorId })` call, so a deployment
|
|
34
|
+
* can keep a roster and refuse strangers. An account minted through `createAccount()` has no name
|
|
35
|
+
* to put on a roster: the contract's auto-provisioning flow carries no identity by design, so
|
|
36
|
+
* `grants` can never match one and every such account falls to this.
|
|
37
|
+
*
|
|
38
|
+
* Absent or `null` REFUSES, and it does not inherit from `defaultTier`. Inheriting would widen
|
|
39
|
+
* silently in whichever direction the deployment did not mean: a roster deployment would hand a
|
|
40
|
+
* tier to every account the workspace mints, or turning on OS discovery would quietly give every
|
|
41
|
+
* unrostered `/rpc` caller one. Naming a tier here is what turns Cloudflare OS discovery on.
|
|
42
|
+
*
|
|
43
|
+
* An account still resolves through `grants` first, so an operator who wants ONE account raised
|
|
44
|
+
* reads its id off the account's own `describe()` (`uniqueName`) and grants that id directly.
|
|
45
|
+
*/
|
|
46
|
+
autoProvisionedTier?: string | null;
|
|
28
47
|
tiers: Record<string, TierPolicy>;
|
|
29
48
|
/** OS user identity (whatever the OS authenticates) to tier name. */
|
|
30
49
|
grants: Record<string, string>;
|
|
@@ -54,6 +73,7 @@ export interface CompiledTier {
|
|
|
54
73
|
/** The policy, resolved once and read per request. */
|
|
55
74
|
export interface CompiledPolicy {
|
|
56
75
|
defaultTier: string | null;
|
|
76
|
+
autoProvisionedTier: string | null;
|
|
57
77
|
tiers: ReadonlyMap<string, CompiledTier>;
|
|
58
78
|
grants: ReadonlyMap<string, string>;
|
|
59
79
|
}
|
|
@@ -70,6 +90,24 @@ export declare function compilePolicy(policy: GatekeeperPolicy): CompiledPolicy;
|
|
|
70
90
|
* ask for a better one.
|
|
71
91
|
*/
|
|
72
92
|
export declare function tierForActor(policy: CompiledPolicy, actorId: string): CompiledTier | null;
|
|
93
|
+
/**
|
|
94
|
+
* The tier EVERY auto-provisioned account falls to, resolved without an account to ask about.
|
|
95
|
+
*
|
|
96
|
+
* Asked on its own because the vendor is questioned before any account exists: the session types
|
|
97
|
+
* it publishes are the ones a not-yet-created account will get. Answering that by minting a
|
|
98
|
+
* throwaway id and letting it miss `grants` produces the same tier and a refusal naming an id that
|
|
99
|
+
* exists nowhere in the operator's config, which is the wrong sentence for the one reader who has
|
|
100
|
+
* to act on it.
|
|
101
|
+
*/
|
|
102
|
+
export declare function autoProvisionedTier(policy: CompiledPolicy): CompiledTier | null;
|
|
103
|
+
/**
|
|
104
|
+
* The tier one auto-provisioned Cloudflare OS account holds, or a refusal.
|
|
105
|
+
*
|
|
106
|
+
* `grants` is consulted FIRST and by the account's own minted id, which is the whole affordance an
|
|
107
|
+
* operator has for raising one account above the rest: nothing else about such an account is
|
|
108
|
+
* nameable, because nothing about it was named when it was created.
|
|
109
|
+
*/
|
|
110
|
+
export declare function tierForAccount(policy: CompiledPolicy, accountId: string): CompiledTier | null;
|
|
73
111
|
/**
|
|
74
112
|
* What the OS needs to run its own approval governance over a call: the consequence the platform
|
|
75
113
|
* annotates, with the cautious default already applied.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/policy/compile.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAA;AAGzC;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,QAAQ,EAAE,cAAc,CAAA;IACxB,4EAA4E;IAC5E,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAA;IAC9B,mFAAmF;IACnF,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACzB;AAED,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACjC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACtB,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,eAAe,CAAA;AAEnB,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,cAAc,CAAA;IACxB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACrC;;;;OAIG;IACH,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;IACpC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACxB;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACxC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AA2HD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/policy/compile.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,kCAAkC,CAAA;AAGzC;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,QAAQ,EAAE,cAAc,CAAA;IACxB,4EAA4E;IAC5E,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAA;IAC9B,mFAAmF;IACnF,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACzB;AAED,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B;;;;;;;;;;;;;;;;;OAiBG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACjC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACtB,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,eAAe,CAAA;AAEnB,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,cAAc,CAAA;IACxB,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACrC;;;;OAIG;IACH,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;IACpC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACxB;AAED,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACxC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AA2HD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,cAAc,CAgCtE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAIzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,YAAY,GAAG,IAAI,CAG/E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAI7F;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,cAAc,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAA;IAC5C,OAAO,EAAE,OAAO,CAAA;CACjB,CAaA"}
|
package/dist/policy/compile.js
CHANGED
|
@@ -125,16 +125,27 @@ export function compilePolicy(policy) {
|
|
|
125
125
|
for (const [name, tier] of Object.entries(policy.tiers)) {
|
|
126
126
|
tiers.set(name, compileTier(name, tier));
|
|
127
127
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
const autoProvisionedTier = policy.autoProvisionedTier ?? null;
|
|
129
|
+
for (const [field, named] of [
|
|
130
|
+
['defaultTier', policy.defaultTier],
|
|
131
|
+
['autoProvisionedTier', autoProvisionedTier],
|
|
132
|
+
]) {
|
|
133
|
+
if (named !== null && !tiers.has(named)) {
|
|
134
|
+
throw new PolicyError(`${field} is '${named}', which is not one of the declared tiers ` +
|
|
135
|
+
`(${[...tiers.keys()].join(', ')}).`);
|
|
136
|
+
}
|
|
131
137
|
}
|
|
132
138
|
for (const [actor, tierName] of Object.entries(policy.grants)) {
|
|
133
139
|
if (!tiers.has(tierName)) {
|
|
134
140
|
throw new PolicyError(`Actor '${actor}' is granted tier '${tierName}', which is not declared.`);
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
|
-
return {
|
|
143
|
+
return {
|
|
144
|
+
defaultTier: policy.defaultTier,
|
|
145
|
+
autoProvisionedTier,
|
|
146
|
+
tiers,
|
|
147
|
+
grants: new Map(Object.entries(policy.grants)),
|
|
148
|
+
};
|
|
138
149
|
}
|
|
139
150
|
/**
|
|
140
151
|
* The tier an actor holds, or a refusal.
|
|
@@ -149,6 +160,33 @@ export function tierForActor(policy, actorId) {
|
|
|
149
160
|
return null;
|
|
150
161
|
return policy.tiers.get(name) ?? null;
|
|
151
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* The tier EVERY auto-provisioned account falls to, resolved without an account to ask about.
|
|
165
|
+
*
|
|
166
|
+
* Asked on its own because the vendor is questioned before any account exists: the session types
|
|
167
|
+
* it publishes are the ones a not-yet-created account will get. Answering that by minting a
|
|
168
|
+
* throwaway id and letting it miss `grants` produces the same tier and a refusal naming an id that
|
|
169
|
+
* exists nowhere in the operator's config, which is the wrong sentence for the one reader who has
|
|
170
|
+
* to act on it.
|
|
171
|
+
*/
|
|
172
|
+
export function autoProvisionedTier(policy) {
|
|
173
|
+
if (policy.autoProvisionedTier === null)
|
|
174
|
+
return null;
|
|
175
|
+
return policy.tiers.get(policy.autoProvisionedTier) ?? null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The tier one auto-provisioned Cloudflare OS account holds, or a refusal.
|
|
179
|
+
*
|
|
180
|
+
* `grants` is consulted FIRST and by the account's own minted id, which is the whole affordance an
|
|
181
|
+
* operator has for raising one account above the rest: nothing else about such an account is
|
|
182
|
+
* nameable, because nothing about it was named when it was created.
|
|
183
|
+
*/
|
|
184
|
+
export function tierForAccount(policy, accountId) {
|
|
185
|
+
const granted = policy.grants.get(accountId);
|
|
186
|
+
if (granted !== undefined)
|
|
187
|
+
return policy.tiers.get(granted) ?? null;
|
|
188
|
+
return autoProvisionedTier(policy);
|
|
189
|
+
}
|
|
152
190
|
/**
|
|
153
191
|
* What the OS needs to run its own approval governance over a call: the consequence the platform
|
|
154
192
|
* annotates, with the cautious default already applied.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/policy/compile.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,0EAA0E;AAC1E,EAAE;AACF,gGAAgG;AAChG,kGAAkG;AAClG,4FAA4F;AAC5F,iGAAiG;AACjG,+FAA+F;AAC/F,iDAAiD;AACjD,EAAE;AACF,0FAA0F;AAC1F,kBAAkB;AAElB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,kBAAkB,GAInB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/policy/compile.ts"],"names":[],"mappings":"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"}
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { compilePolicy, describeBinding, tierForActor, type CompiledPolicy, type CompiledTier, type GatekeeperPolicy, type TierPolicy, type WithheldBinding, type WithheldReason, } from './compile.js';
|
|
1
|
+
export { autoProvisionedTier, compilePolicy, describeBinding, tierForAccount, tierForActor, type CompiledPolicy, type CompiledTier, type GatekeeperPolicy, type TierPolicy, type WithheldBinding, type WithheldReason, } from './compile.js';
|
|
2
2
|
export { ANSWERABLE_DECISION_KINDS, answererFor, DECISION_BINDINGS, DECISION_KEY_SCOPE, type DecisionAnswerer, type DecisionCall, type DecisionField, type DecisionVerb, type LiveDecision, type ParkedDecisionKind, } from './decisions.js';
|
|
3
3
|
export { GatekeeperError, PolicyError, type GatekeeperReason } from '../errors.js';
|
|
4
4
|
export { applyMask, MASKED } from '../masking.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAA;AAIvB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAKjD,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,aAAa,GACd,MAAM,kCAAkC,CAAA;AAMzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,YAAY,EACZ,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAA;AAIvB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAKjD,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,aAAa,GACd,MAAM,kCAAkC,CAAA;AAMzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA"}
|
package/dist/policy/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Everything here is re-exported from the main entry too, so a deployment that does not care can
|
|
10
10
|
// import from one place.
|
|
11
|
-
export { compilePolicy, describeBinding, tierForActor, } from './compile.js';
|
|
11
|
+
export { autoProvisionedTier, compilePolicy, describeBinding, tierForAccount, tierForActor, } from './compile.js';
|
|
12
12
|
export { ANSWERABLE_DECISION_KINDS, answererFor, DECISION_BINDINGS, DECISION_KEY_SCOPE, } from './decisions.js';
|
|
13
13
|
// A policy is refused by a `PolicyError` and MASKED paths are the other half of a tier, so both
|
|
14
14
|
// belong to the vocabulary a policy file and its tests speak.
|
package/dist/policy/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,4BAA4B;AAC5B,EAAE;AACF,6FAA6F;AAC7F,kGAAkG;AAClG,iGAAiG;AACjG,qFAAqF;AACrF,EAAE;AACF,iGAAiG;AACjG,yBAAyB;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,GAOb,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GAOnB,MAAM,gBAAgB,CAAA;AAEvB,gGAAgG;AAChG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAyB,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAYjD,+FAA+F;AAC/F,gGAAgG;AAChG,iGAAiG;AACjG,yDAAyD;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,4BAA4B;AAC5B,EAAE;AACF,6FAA6F;AAC7F,kGAAkG;AAClG,iGAAiG;AACjG,qFAAqF;AACrF,EAAE;AACF,iGAAiG;AACjG,yBAAyB;AAEzB,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,YAAY,GAOb,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EACjB,kBAAkB,GAOnB,MAAM,gBAAgB,CAAA;AAEvB,gGAAgG;AAChG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAyB,MAAM,cAAc,CAAA;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAYjD,+FAA+F;AAC/F,gGAAgG;AAChG,iGAAiG;AACjG,yDAAyD;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA"}
|
package/dist/state.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DurableObject } from 'cloudflare:workers';
|
|
2
|
+
import { type HookDispatchReport, type HookPushTarget, type HookRecord, type HookRegistration } from './os/hooks.js';
|
|
3
|
+
import type { HookInitiator } from './os/protocol.js';
|
|
2
4
|
/** A parked cat-factory decision, as the OS shows it. */
|
|
3
5
|
export interface ApprovalCard {
|
|
4
6
|
/** The delivery that raised it. Also the id the OS answers on. */
|
|
@@ -83,6 +85,18 @@ export type DeliveryApplication = {
|
|
|
83
85
|
} | {
|
|
84
86
|
applied: true;
|
|
85
87
|
effect: 'opened' | 'superseded' | 'run-event' | 'none';
|
|
88
|
+
/**
|
|
89
|
+
* What this delivery CHANGED, for the hooks registered on each topic to be pushed about.
|
|
90
|
+
*
|
|
91
|
+
* Named here rather than re-derived by the dispatcher, for two reasons that are really one.
|
|
92
|
+
* A terminal run event both records the transition and settles the run's open cards, so it
|
|
93
|
+
* changes two topics' worth of state, and a dispatcher handed only the effect could see just
|
|
94
|
+
* the first: a card-subscribed gadget went on rendering settled cards as answerable, which
|
|
95
|
+
* is exactly what `HOOK_TOPICS.approval_card` tells an approver it will not do. And what the
|
|
96
|
+
* write actually changed is knowable only here, so a supersede that met a card already
|
|
97
|
+
* settled pushes nothing rather than re-announcing a transition that did not happen.
|
|
98
|
+
*/
|
|
99
|
+
pushes: HookPushTarget[];
|
|
86
100
|
};
|
|
87
101
|
export declare class GatekeeperState extends DurableObject {
|
|
88
102
|
#private;
|
|
@@ -95,13 +109,78 @@ export declare class GatekeeperState extends DurableObject {
|
|
|
95
109
|
* re-deriving that on every replay would spend the platform's retry budget on it.
|
|
96
110
|
*/
|
|
97
111
|
applyDelivery(deliveryId: string, effect: DeliveryEffect, now: number): Promise<DeliveryApplication>;
|
|
98
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Settle a card this Gatekeeper answered. A card already settled keeps its first resolution.
|
|
114
|
+
*
|
|
115
|
+
* The settlement is pushed before this returns, which is the opposite disposition from the
|
|
116
|
+
* webhook receiver's and for the reason that separates the two callers. There the fan-out is
|
|
117
|
+
* deferred because the platform's retry is waiting on the acknowledgement and would re-drive a
|
|
118
|
+
* delivery this Gatekeeper has already committed; here the caller is the session that just
|
|
119
|
+
* answered the card, nothing is retrying it, and a push bounded by its own per-hook deadline
|
|
120
|
+
* costs that one call rather than the record.
|
|
121
|
+
*/
|
|
99
122
|
resolveCard(cardId: string, resolution: string, now: number): Promise<ApprovalCard | null>;
|
|
100
123
|
getCard(cardId: string): Promise<ApprovalCard | null>;
|
|
101
124
|
/** Every card, newest first. Resolved ones are included: the OS decides what it renders. */
|
|
102
125
|
listCards(): Promise<ApprovalCard[]>;
|
|
103
126
|
/** The latest lifecycle state of every run a delivery has mentioned, newest first. */
|
|
104
127
|
listRunStates(): Promise<RunState[]>;
|
|
128
|
+
/**
|
|
129
|
+
* The workspace enabled a hook: store the registration, and hold the live half beside it.
|
|
130
|
+
*
|
|
131
|
+
* The counters of an existing registration SURVIVE a re-enable. A workspace re-arms a hook
|
|
132
|
+
* exactly when it noticed one had gone quiet, and the number that told it so is `missed`;
|
|
133
|
+
* zeroing it on the way back up would delete the evidence at the one moment it is being acted
|
|
134
|
+
* on. What is replaced is the initiator, which the contract asks for by name.
|
|
135
|
+
*/
|
|
136
|
+
enableHook(record: HookRecord, initiator: HookInitiator): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* The workspace withdrew a hook: both halves go, which is the permanent clean-up asked for.
|
|
139
|
+
*
|
|
140
|
+
* Found BY HOOK ID rather than addressed by it, because the row is keyed on the target while the
|
|
141
|
+
* controller a workspace persisted names the id it was bound under. A controller whose row was
|
|
142
|
+
* replaced by a later bind from the same gadget finds nothing and settles for releasing its own
|
|
143
|
+
* half, which is the right answer: it is being asked to withdraw a registration that a re-arm
|
|
144
|
+
* has already superseded, and deleting the successor's row would silence a live hook.
|
|
145
|
+
*/
|
|
146
|
+
disableHook(hookId: string): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* The hooks enabled for one account, newest first, each with whether its live half survives.
|
|
149
|
+
*
|
|
150
|
+
* Scoped to the account rather than answering the object's whole set: a session reads this, and
|
|
151
|
+
* one account's gadget bindings are not another's to enumerate.
|
|
152
|
+
*/
|
|
153
|
+
listHooks(accountId: string): Promise<HookRegistration[]>;
|
|
154
|
+
/**
|
|
155
|
+
* Push what a change produced to every hook registered for each payload's topic.
|
|
156
|
+
*
|
|
157
|
+
* Called AFTER the write it reports has committed, never as part of it: the card is the durable
|
|
158
|
+
* truth and the push is the accelerator over it, so an outbound call that hangs or refuses costs
|
|
159
|
+
* a notification and never the record of what the deployment reported. Each registration is
|
|
160
|
+
* pushed independently and concurrently, and every outcome lands on that registration's own
|
|
161
|
+
* counters, because one workspace's broken gadget must not decide whether another's hook fires
|
|
162
|
+
* or how long it waits to.
|
|
163
|
+
*/
|
|
164
|
+
dispatchHooks(pushes: readonly HookPushTarget[], now: number): Promise<HookDispatchReport>;
|
|
165
|
+
/**
|
|
166
|
+
* Remember that this deployment minted an account.
|
|
167
|
+
*
|
|
168
|
+
* The one thing an account id has to be checkable for, and the reason it is the only account
|
|
169
|
+
* state here: `addObserver` is handed an id by a verifier and has to decide whether this
|
|
170
|
+
* Gatekeeper has ever heard of it. Nothing else consults this, because nothing else needs to. A
|
|
171
|
+
* session's account id rides `ctx.props` on a stub the workspace holds, so it was minted here by
|
|
172
|
+
* construction, and re-deriving that on every call would be a lookup with no question behind it.
|
|
173
|
+
*/
|
|
174
|
+
recordAccount(accountId: string, now: number): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* Whether this deployment minted the named account.
|
|
177
|
+
*
|
|
178
|
+
* Never pruned, and that is deliberate rather than an omission: the workspace holds the account
|
|
179
|
+
* stub for as long as it likes, and an id whose row expired would read as an account this
|
|
180
|
+
* Gatekeeper never made. Revoking an account's keys does not remove it either, because what was
|
|
181
|
+
* revoked is a credential and what is asked here is a fact about who exists.
|
|
182
|
+
*/
|
|
183
|
+
hasAccount(accountId: string): Promise<boolean>;
|
|
105
184
|
/**
|
|
106
185
|
* Either the actor's existing key, or an exclusive claim on minting one.
|
|
107
186
|
*
|
package/dist/state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|