@cat-factory/gatekeeper-worker 0.4.2 → 0.5.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.
Files changed (76) hide show
  1. package/README.md +58 -12
  2. package/dist/capability.d.ts +45 -0
  3. package/dist/capability.d.ts.map +1 -1
  4. package/dist/capability.js +60 -4
  5. package/dist/capability.js.map +1 -1
  6. package/dist/errors.d.ts +13 -1
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js.map +1 -1
  9. package/dist/gatekeeper.d.ts +47 -2
  10. package/dist/gatekeeper.d.ts.map +1 -1
  11. package/dist/gatekeeper.js +89 -5
  12. package/dist/gatekeeper.js.map +1 -1
  13. package/dist/index.d.ts +13 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +15 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/markdown.d.ts +12 -0
  18. package/dist/markdown.d.ts.map +1 -0
  19. package/dist/markdown.js +28 -0
  20. package/dist/markdown.js.map +1 -0
  21. package/dist/os/account.d.ts +27 -0
  22. package/dist/os/account.d.ts.map +1 -0
  23. package/dist/os/account.js +130 -0
  24. package/dist/os/account.js.map +1 -0
  25. package/dist/os/descriptions.d.ts +43 -0
  26. package/dist/os/descriptions.d.ts.map +1 -0
  27. package/dist/os/descriptions.js +100 -0
  28. package/dist/os/descriptions.js.map +1 -0
  29. package/dist/os/discoverability.d.ts +30 -0
  30. package/dist/os/discoverability.d.ts.map +1 -0
  31. package/dist/os/discoverability.js +46 -0
  32. package/dist/os/discoverability.js.map +1 -0
  33. package/dist/os/exports.d.ts +28 -0
  34. package/dist/os/exports.d.ts.map +1 -0
  35. package/dist/os/exports.js +68 -0
  36. package/dist/os/exports.js.map +1 -0
  37. package/dist/os/protocol.d.ts +150 -0
  38. package/dist/os/protocol.d.ts.map +1 -0
  39. package/dist/os/protocol.js +22 -0
  40. package/dist/os/protocol.js.map +1 -0
  41. package/dist/os/queue.d.ts +90 -0
  42. package/dist/os/queue.d.ts.map +1 -0
  43. package/dist/os/queue.js +197 -0
  44. package/dist/os/queue.js.map +1 -0
  45. package/dist/os/resource-core.d.ts +100 -0
  46. package/dist/os/resource-core.d.ts.map +1 -0
  47. package/dist/os/resource-core.js +155 -0
  48. package/dist/os/resource-core.js.map +1 -0
  49. package/dist/os/resource.d.ts +16 -0
  50. package/dist/os/resource.d.ts.map +1 -0
  51. package/dist/os/resource.js +64 -0
  52. package/dist/os/resource.js.map +1 -0
  53. package/dist/os/resources.d.ts +11 -0
  54. package/dist/os/resources.d.ts.map +1 -0
  55. package/dist/os/resources.js +26 -0
  56. package/dist/os/resources.js.map +1 -0
  57. package/dist/os/session-types.d.ts +6 -0
  58. package/dist/os/session-types.d.ts.map +1 -0
  59. package/dist/os/session-types.js +82 -0
  60. package/dist/os/session-types.js.map +1 -0
  61. package/dist/os/vendor.d.ts +14 -0
  62. package/dist/os/vendor.d.ts.map +1 -0
  63. package/dist/os/vendor.js +95 -0
  64. package/dist/os/vendor.js.map +1 -0
  65. package/dist/policy/compile.d.ts +38 -0
  66. package/dist/policy/compile.d.ts.map +1 -1
  67. package/dist/policy/compile.js +42 -4
  68. package/dist/policy/compile.js.map +1 -1
  69. package/dist/policy/index.d.ts +1 -1
  70. package/dist/policy/index.d.ts.map +1 -1
  71. package/dist/policy/index.js +1 -1
  72. package/dist/policy/index.js.map +1 -1
  73. package/dist/worker.d.ts.map +1 -1
  74. package/dist/worker.js +24 -5
  75. package/dist/worker.js.map +1 -1
  76. package/package.json +5 -5
@@ -0,0 +1,155 @@
1
+ // What a bound resource DOES, separated from the Durable Object it lives in.
2
+ //
3
+ // The split is not ceremony. A Durable Object can only be constructed by workerd from a real
4
+ // `DurableObjectState`, and the props-imbued class this Gatekeeper hands the workspace is opaque by
5
+ // design (the workspace instantiates it, through machinery that is its own). So a suite that could
6
+ // only reach this behaviour through the object would be reduced to asserting that a class was
7
+ // returned. Everything worth getting wrong lives here instead, where it is ordinary code with
8
+ // ordinary dependencies, and `resource.ts` is the shell that supplies `ctx.props` and holds one of
9
+ // these for the object's lifetime.
10
+ //
11
+ // Holding ONE per object is what the action ledger needs: the workspace settles an action by
12
+ // calling `applyAction` on the resource, so a core rebuilt per call would lose the pending action
13
+ // the decision is about.
14
+ import { GatekeeperError } from '../errors.js';
15
+ import { Gatekeeper } from '../gatekeeper.js';
16
+ import { actionKindOf } from './descriptions.js';
17
+ import { ActionLedger, queueGovernance } from './queue.js';
18
+ import { renderTierSessionTypes, SESSION_INTERFACE_NAME } from './session-types.js';
19
+ /** One bound resource: the session it opens, its types, and the action lifecycle behind it. */
20
+ export class ResourceCore {
21
+ #env;
22
+ #policy;
23
+ #props;
24
+ #ledger = new ActionLedger();
25
+ constructor(env, policy, props) {
26
+ this.#env = env;
27
+ this.#policy = policy;
28
+ this.#props = props;
29
+ }
30
+ async describe() {
31
+ const tier = this.#tier();
32
+ return {
33
+ url: this.#gatekeeper().deployment,
34
+ title: 'cat-factory workspace',
35
+ snippet: `File work, start runs and answer what they park on, at policy tier '${tier.name}': ` +
36
+ `${tier.description}`,
37
+ suggestedBindingName: 'catFactory',
38
+ tsType: SESSION_INTERFACE_NAME,
39
+ };
40
+ }
41
+ /**
42
+ * The session's own types, rendered for the tier this account holds.
43
+ *
44
+ * Tier-specific rather than a copy of the vendor's, because this is the one the contract points a
45
+ * caller at: `ResourceDescription.tsType` must name an export of THIS method's output, and a
46
+ * session carries exactly its granted operations.
47
+ */
48
+ async getTypeScriptTypes() {
49
+ return renderTierSessionTypes(this.#tier());
50
+ }
51
+ /**
52
+ * Open a governed session.
53
+ *
54
+ * The queue is not optional and not a decoration: every operation the returned object carries
55
+ * funnels through the one `invoke` closure, which submits actions to this queue and authorizes
56
+ * observations against it. The tier policy underneath is the FLOOR, so an operation the policy
57
+ * never granted is absent from the object rather than something the queue has to refuse.
58
+ *
59
+ * The queue passed here is OWNED by the session that comes back: it is released, along with
60
+ * every action that session left undecided, when the session is disposed. A caller reaching this
61
+ * over RPC therefore hands in a reference of its own rather than the parameter it received (see
62
+ * `resource.ts`), because the parameter's lifetime ends when this call returns and the session's
63
+ * does not.
64
+ */
65
+ async startSession(approvalQueue) {
66
+ const gatekeeper = this.#gatekeeper();
67
+ const accountId = this.#props.accountId;
68
+ return gatekeeper.capabilityForAccount(accountId, queueGovernance({
69
+ queue: approvalQueue,
70
+ ledger: this.#ledger.openSession(),
71
+ subject: {
72
+ accountId,
73
+ tier: gatekeeper.tierForAccount(accountId).name,
74
+ deployment: gatekeeper.deployment,
75
+ },
76
+ }));
77
+ }
78
+ /**
79
+ * How many submitted actions this object is still holding, across every live session.
80
+ *
81
+ * Exposed for the same reason the ledger counts them: the ONE unbounded thing about a long-lived
82
+ * resource object is this set, and a count that does not fall back to zero when the sessions are
83
+ * gone is the leak rather than a slow day.
84
+ */
85
+ get pendingActionCount() {
86
+ return this.#ledger.pendingCount;
87
+ }
88
+ /**
89
+ * The action kinds this Gatekeeper may auto-apply, if the user opted into the kind.
90
+ *
91
+ * Derived from the same table and the same consequence reading `describeAction` stamps onto each
92
+ * submission, so a pre-approval UI listing kinds before any action exists and the `autoApprovable`
93
+ * flag on an action that has been submitted cannot disagree.
94
+ *
95
+ * TODAY THIS IS EMPTY, and that is the honest answer rather than a gap. The public surface
96
+ * annotates a consequence only where the stakes are real money or a merged pull request, so every
97
+ * other mutation is unannotated, and the table's documented reading of an unannotated mutation is
98
+ * that it is destructive. Offering those for unattended auto-approval would mean inverting that
99
+ * default here, which is precisely the misreading `resolveConsequence` exists to stop. If the
100
+ * surface ever states that a write is safe, it appears here with no further decision.
101
+ */
102
+ async getAutoApprovableActions() {
103
+ return this.#tier()
104
+ .granted.filter((binding) => !binding.readOnly)
105
+ .filter((binding) => (binding.consequence?.destructive ?? true) === false)
106
+ .map(actionKindOf);
107
+ }
108
+ /** The workspace approved an action: perform it, and hand the result to the waiting call. */
109
+ async applyAction(action) {
110
+ await this.#ledger.apply(action);
111
+ }
112
+ /** The workspace rejected an action: the waiting call throws and nothing is performed. */
113
+ async rejectAction(action) {
114
+ this.#ledger.reject(action);
115
+ }
116
+ /**
117
+ * Reverting is not something this Gatekeeper can do, which every action it submits already states
118
+ * (`implementsRevert: false`).
119
+ *
120
+ * It answers rather than throwing because the caller is the workspace UI on behalf of a person who
121
+ * wants their change undone: a message naming what they have to do themselves is the useful
122
+ * answer, where an exception would surface as a failed revert they might retry.
123
+ */
124
+ async revertAction(_action) {
125
+ return {
126
+ message: 'This Gatekeeper cannot revert a cat-factory operation. A started run is stopped from the ' +
127
+ 'board or with `tasks_stop`; a merged pull request is reverted in the repository, not here.',
128
+ canRetry: false,
129
+ };
130
+ }
131
+ /**
132
+ * Sharing this resource's observations onward is REFUSED, which blocks the share.
133
+ *
134
+ * The contract asks the gatekeeper to verify that the new viewer could directly read everything
135
+ * historically observed through it. This Gatekeeper cannot answer that: it keeps no observation
136
+ * log, and the plausible rule (the observer's own tier reaches every operation that produced the
137
+ * observed data) needs a tier for a viewer this deployment's policy has never named. A share
138
+ * blocked loudly beats an observation leaked quietly, so the refusal stands until there is a rule
139
+ * worth writing down.
140
+ */
141
+ async addObserver(id, _user) {
142
+ throw new GatekeeperError('sharing_refused', `This Gatekeeper cannot verify that '${id}' may see everything already read through this ` +
143
+ 'resource, so it refuses the share. Give them their own connected account instead: their ' +
144
+ "tier is then resolved from this deployment's own policy.");
145
+ }
146
+ /** Idempotent by contract: nothing was ever added, so there is nothing to forget. */
147
+ async removeObserver(_id) { }
148
+ #gatekeeper() {
149
+ return Gatekeeper.create(this.#env, this.#policy);
150
+ }
151
+ #tier() {
152
+ return this.#gatekeeper().tierForAccount(this.#props.accountId);
153
+ }
154
+ }
155
+ //# sourceMappingURL=resource-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-core.js","sourceRoot":"","sources":["../../src/os/resource-core.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,6FAA6F;AAC7F,oGAAoG;AACpG,mGAAmG;AACnG,8FAA8F;AAC9F,8FAA8F;AAC9F,mGAAmG;AACnG,mCAAmC;AACnC,EAAE;AACF,6FAA6F;AAC7F,kGAAkG;AAClG,yBAAyB;AAGzB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC1D,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAkBnF,+FAA+F;AAC/F,MAAM,OAAO,YAAY;IACd,IAAI,CAAe;IACnB,OAAO,CAAkB;IACzB,MAAM,CAAe;IACrB,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;IAErC,YAAY,GAAkB,EAAE,MAAwB,EAAE,KAAoB;QAC5E,IAAI,CAAC,IAAI,GAAG,GAAG,CAAA;QACf,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;QACrB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,CAAA;QACzB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU;YAClC,KAAK,EAAE,uBAAuB;YAC9B,OAAO,EACL,uEAAuE,IAAI,CAAC,IAAI,KAAK;gBACrF,GAAG,IAAI,CAAC,WAAW,EAAE;YACvB,oBAAoB,EAAE,YAAY;YAClC,MAAM,EAAE,sBAAsB;SAC/B,CAAA;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,kBAAkB;QACtB,OAAO,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;IAC7C,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,YAAY,CAAC,aAA4B;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAA;QACvC,OAAO,UAAU,CAAC,oBAAoB,CACpC,SAAS,EACT,eAAe,CAAC;YACd,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;YAClC,OAAO,EAAE;gBACP,SAAS;gBACT,IAAI,EAAE,UAAU,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,IAAI;gBAC/C,UAAU,EAAE,UAAU,CAAC,UAAU;aAClC;SACF,CAAC,CACH,CAAA;IACH,CAAC;IAED;;;;;;OAMG;IACH,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAA;IAClC,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,wBAAwB;QAC5B,OAAO,IAAI,CAAC,KAAK,EAAE;aAChB,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;aAC9C,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;aACzE,GAAG,CAAC,YAAY,CAAC,CAAA;IACtB,CAAC;IAED,6FAA6F;IAC7F,KAAK,CAAC,WAAW,CAAC,MAAc;QAC9B,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IAED,0FAA0F;IAC1F,KAAK,CAAC,YAAY,CAAC,MAAc;QAC/B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAAC,OAAe;QAChC,OAAO;YACL,OAAO,EACL,2FAA2F;gBAC3F,4FAA4F;YAC9F,QAAQ,EAAE,KAAK;SAChB,CAAA;IACH,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,KAAc;QAC1C,MAAM,IAAI,eAAe,CACvB,iBAAiB,EACjB,uCAAuC,EAAE,iDAAiD;YACxF,0FAA0F;YAC1F,0DAA0D,CAC7D,CAAA;IACH,CAAC;IAED,qFAAqF;IACrF,KAAK,CAAC,cAAc,CAAC,GAAW,IAAkB,CAAC;IAEnD,WAAW;QACT,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IACnD,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IACjE,CAAC;CACF"}
@@ -0,0 +1,16 @@
1
+ import type { GatekeeperEnv } from '../env.js';
2
+ import type { GatekeeperPolicy } from '../policy/compile.js';
3
+ import type { ResourceObject } from './protocol.js';
4
+ import { type ResourceProps } from './resource-core.js';
5
+ export type { ResourceProps } from './resource-core.js';
6
+ /**
7
+ * Build the resource Durable Object class over a deployment's policy.
8
+ *
9
+ * A factory rather than a class, for the reason `createGatekeeperWorker` is one: the policy is the
10
+ * deployment's and arrives as an argument, so a class that imported one would own the file the
11
+ * operator is supposed to write.
12
+ */
13
+ export declare function createGatekeeperResource(options: {
14
+ policy: GatekeeperPolicy;
15
+ }): new (ctx: DurableObjectState<ResourceProps>, env: GatekeeperEnv) => ResourceObject;
16
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/os/resource.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAkD,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnG,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAErE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAChD,MAAM,EAAE,gBAAgB,CAAA;CACzB,GAAG,KAAK,GAAG,EAAE,kBAAkB,CAAC,aAAa,CAAC,EAAE,GAAG,EAAE,aAAa,KAAK,cAAc,CA+CrF"}
@@ -0,0 +1,64 @@
1
+ // The per-resource object the workspace binds: a Durable Object serving one paired cat-factory
2
+ // workspace to one account.
3
+ //
4
+ // WHAT A RESOURCE IS, decided here and recorded in the initiative tracker: the PAIRED cat-factory
5
+ // WORKSPACE, named by a URLPattern over the deployment origin. It follows from the credential
6
+ // rather than being a modelling preference. This Worker holds one provisioning key, a cat-factory
7
+ // key is scoped to exactly one workspace, and `STATE` is already keyed on the deployment origin for
8
+ // the same reason. So one Gatekeeper Worker serves one resource, and an OS deployment that wants
9
+ // two workspaces runs two Gatekeepers with two bindings, which is also the only arrangement in
10
+ // which the two workspaces' credentials sit in different secret stores.
11
+ //
12
+ // Everything below is a FACADE. The policy compilation, the key broker, the durable state and the
13
+ // approval answerers are the same single implementation the `/rpc` door reaches; what differs is
14
+ // the door, and the governance that door brings with it. This file is the SHELL: it supplies
15
+ // `ctx.props` and holds one `ResourceCore`, which is where the behaviour lives.
16
+ import { DurableObject } from 'cloudflare:workers';
17
+ import { holdQueue } from './queue.js';
18
+ import { ResourceCore } from './resource-core.js';
19
+ /**
20
+ * Build the resource Durable Object class over a deployment's policy.
21
+ *
22
+ * A factory rather than a class, for the reason `createGatekeeperWorker` is one: the policy is the
23
+ * deployment's and arrives as an argument, so a class that imported one would own the file the
24
+ * operator is supposed to write.
25
+ */
26
+ export function createGatekeeperResource(options) {
27
+ const { policy } = options;
28
+ return class CatFactoryResource extends DurableObject {
29
+ // ONE core for the object's lifetime, because the action ledger inside it is what a later
30
+ // `applyAction` settles: a core rebuilt per call would have forgotten the action the decision
31
+ // is about. Field initializers run after the base constructor, so `ctx` and `env` are set.
32
+ #core = new ResourceCore(this.env, policy, this.ctx.props);
33
+ async describe() {
34
+ return this.#core.describe();
35
+ }
36
+ async getTypeScriptTypes() {
37
+ return this.#core.getTypeScriptTypes();
38
+ }
39
+ // The one method where being the SHELL is not delegation: this is where the RPC boundary is,
40
+ // so this is where the queue's lifetime is taken over from the call that carried it in.
41
+ async startSession(approvalQueue) {
42
+ return this.#core.startSession(holdQueue(approvalQueue));
43
+ }
44
+ async getAutoApprovableActions() {
45
+ return this.#core.getAutoApprovableActions();
46
+ }
47
+ async applyAction(action) {
48
+ return this.#core.applyAction(action);
49
+ }
50
+ async rejectAction(action) {
51
+ return this.#core.rejectAction(action);
52
+ }
53
+ async revertAction(action) {
54
+ return this.#core.revertAction(action);
55
+ }
56
+ async addObserver(id, user) {
57
+ return this.#core.addObserver(id, user);
58
+ }
59
+ async removeObserver(id) {
60
+ return this.#core.removeObserver(id);
61
+ }
62
+ };
63
+ }
64
+ //# sourceMappingURL=resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/os/resource.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,4BAA4B;AAC5B,EAAE;AACF,kGAAkG;AAClG,8FAA8F;AAC9F,kGAAkG;AAClG,oGAAoG;AACpG,iGAAiG;AACjG,+FAA+F;AAC/F,wEAAwE;AACxE,EAAE;AACF,kGAAkG;AAClG,iGAAiG;AACjG,6FAA6F;AAC7F,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIlD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAIrE;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAExC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE1B,OAAO,MAAM,kBAAmB,SAAQ,aAA2C;QACjF,0FAA0F;QAC1F,8FAA8F;QAC9F,2FAA2F;QAClF,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAEnE,KAAK,CAAC,QAAQ;YACZ,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC9B,CAAC;QAED,KAAK,CAAC,kBAAkB;YACtB,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAA;QACxC,CAAC;QAED,6FAA6F;QAC7F,wFAAwF;QACxF,KAAK,CAAC,YAAY,CAAC,aAA4B;YAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAA;QAC1D,CAAC;QAED,KAAK,CAAC,wBAAwB;YAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAA;QAC9C,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAc;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACvC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAc;YAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,IAAa;YACzC,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;QACzC,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,EAAU;YAC7B,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { SupportedResource } from './protocol.js';
2
+ /**
3
+ * The single resource: the paired cat-factory workspace, named by a pattern over its origin.
4
+ *
5
+ * The pattern is anchored on the deployment's ORIGIN with a wildcard path, which is what makes a
6
+ * board URL a person pastes (`https://cat-factory.example.com/w/…`) bind to this Gatekeeper. It is
7
+ * not `grantable`: there is nothing to grant separately, because the account's access is the
8
+ * provisioning key this Worker already holds.
9
+ */
10
+ export declare function supportedResourceFor(deployment: string): SupportedResource;
11
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/os/resources.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAW1E"}
@@ -0,0 +1,26 @@
1
+ // What this Gatekeeper serves, as the workspace's own resource vocabulary.
2
+ //
3
+ // One function, in a file of its own, because three callers need the SAME answer and a second
4
+ // spelling of the URL pattern would be a resource an account advertises and a bind cannot match:
5
+ // the vendor lists it, the account lists it and matches a URL against it, and the resource object
6
+ // describes itself with it.
7
+ /**
8
+ * The single resource: the paired cat-factory workspace, named by a pattern over its origin.
9
+ *
10
+ * The pattern is anchored on the deployment's ORIGIN with a wildcard path, which is what makes a
11
+ * board URL a person pastes (`https://cat-factory.example.com/w/…`) bind to this Gatekeeper. It is
12
+ * not `grantable`: there is nothing to grant separately, because the account's access is the
13
+ * provisioning key this Worker already holds.
14
+ */
15
+ export function supportedResourceFor(deployment) {
16
+ const origin = new URL(deployment).origin;
17
+ return {
18
+ urlPattern: `${origin}/*`,
19
+ title: 'cat-factory workspace',
20
+ description: 'The cat-factory workspace this Gatekeeper is paired with: file tasks, start runs, watch ' +
21
+ 'them, and answer the decisions they park on. One Gatekeeper serves one workspace, because ' +
22
+ 'the provisioning key it holds is scoped to one.',
23
+ grantable: false,
24
+ };
25
+ }
26
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/os/resources.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,8FAA8F;AAC9F,iGAAiG;AACjG,kGAAkG;AAClG,4BAA4B;AAI5B;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,CAAA;IACzC,OAAO;QACL,UAAU,EAAE,GAAG,MAAM,IAAI;QACzB,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,0FAA0F;YAC1F,4FAA4F;YAC5F,iDAAiD;QACnD,SAAS,EAAE,KAAK;KACjB,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { CompiledTier } from '../policy/compile.js';
2
+ /** The type name a resource declares as its `tsType`, and the interface the `.d.ts` exports. */
3
+ export declare const SESSION_INTERFACE_NAME = "CatFactoryWorkspace";
4
+ /** Render the session `.d.ts` for one compiled tier. */
5
+ export declare function renderTierSessionTypes(tier: CompiledTier): string;
6
+ //# sourceMappingURL=session-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-types.d.ts","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,gGAAgG;AAChG,eAAO,MAAM,sBAAsB,wBAAwB,CAAA;AA2D3D,wDAAwD;AACxD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAUjE"}
@@ -0,0 +1,82 @@
1
+ // The `.d.ts` a session serves through `getTypeScriptTypes()`, composed for one tier.
2
+ //
3
+ // The per-operation signatures are GENERATED (`@cat-factory/gatekeeper-bindings`'s
4
+ // `SESSION_METHOD_SIGNATURES`, rendered by `pnpm gen:sdk` from the same spec the bindings table
5
+ // comes from), so the types an agent codes against cannot describe a surface the deployment does
6
+ // not serve. What is composed here is the tier's own subset plus the reserved methods, because a
7
+ // session carries exactly what its policy granted: a file naming the whole surface would promise
8
+ // methods the object does not have.
9
+ //
10
+ // The reserved methods are written out here rather than generated because they are this package's,
11
+ // not the deployment's: they exist at exactly the same version as this file.
12
+ import { renderSessionTypes } from '@cat-factory/gatekeeper-bindings';
13
+ /** The type name a resource declares as its `tsType`, and the interface the `.d.ts` exports. */
14
+ export const SESSION_INTERFACE_NAME = 'CatFactoryWorkspace';
15
+ /**
16
+ * The methods every session carries whatever its policy: who the caller is, what they hold, what
17
+ * they do not, and the approval inbox.
18
+ *
19
+ * `withheld()` is the one worth reading twice. An agent that cannot tell "your policy hides this"
20
+ * from "no policy could grant this" from "ask for it another way" reports the wrong one to whoever
21
+ * has to fix it, which is why it is on the object rather than in a document.
22
+ */
23
+ const RESERVED_MEMBERS = [
24
+ ` /** Who this session acts as, and the policy tier that account resolved to. */
25
+ tier(): Promise<{ actorId: string; tier: string; description: string; keyScope: string }>
26
+ `,
27
+ ` /**
28
+ * Every operation this session carries, with the scope floor and consequence of each.
29
+ */
30
+ bindings(): Promise<
31
+ {
32
+ name: string
33
+ summary: string
34
+ minScope: string
35
+ readOnly: boolean
36
+ destructive: boolean
37
+ idempotent: boolean
38
+ pathParams: readonly string[]
39
+ queryParams: readonly { name: string; required: boolean }[]
40
+ hasBody: boolean
41
+ }[]
42
+ >
43
+ `,
44
+ ` /**
45
+ * Every operation this session does NOT carry, and why: \`not_in_policy\` and
46
+ * \`denied_by_policy\` are your operator's decision, \`above_key_scope\` needs a higher tier,
47
+ * and \`not_relayable\` cannot cross a session call at all (ask for it another way).
48
+ */
49
+ withheld(): Promise<{ name: string; reason: string; detail: string }[]>
50
+ `,
51
+ ` /** The approval cards the paired deployment has raised and this Gatekeeper still holds open. */
52
+ approvals_list(): Promise<unknown[]>
53
+ `,
54
+ ` /**
55
+ * What a card's run is ACTUALLY parked on now, with the verbs each park takes and whether this
56
+ * session holds the operation behind it. The card is a pointer; the run is the truth.
57
+ */
58
+ approvals_inspect(cardId: string): Promise<unknown>
59
+ `,
60
+ ` /**
61
+ * Answer a park. Three outcomes are distinguishable and all three matter: \`answered\` (the run
62
+ * is unparked), \`recorded\` (your vote counted, the quorum is unmet, the run is still parked)
63
+ * and \`stale\` (the run moved on).
64
+ */
65
+ approvals_answer(cardId: string, input: unknown): Promise<unknown>
66
+ `,
67
+ ` /** Every run this Gatekeeper has been pushed lifecycle events for. */
68
+ runs_watched(): Promise<unknown[]>
69
+ `,
70
+ ];
71
+ /** Render the session `.d.ts` for one compiled tier. */
72
+ export function renderTierSessionTypes(tier) {
73
+ return renderSessionTypes({
74
+ interfaceName: SESSION_INTERFACE_NAME,
75
+ bindings: tier.granted.map((binding) => binding.name),
76
+ extraMembers: RESERVED_MEMBERS,
77
+ preamble: `// The session for policy tier \`${tier.name}\`: ${tier.description}\n` +
78
+ `// Calls are made with a per-account API key at scope \`${tier.keyScope}\`, and every one of\n` +
79
+ "// them passes through this workspace's approval queue.",
80
+ });
81
+ }
82
+ //# sourceMappingURL=session-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-types.js","sourceRoot":"","sources":["../../src/os/session-types.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,EAAE;AACF,mFAAmF;AACnF,gGAAgG;AAChG,iGAAiG;AACjG,iGAAiG;AACjG,iGAAiG;AACjG,oCAAoC;AACpC,EAAE;AACF,mGAAmG;AACnG,6EAA6E;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AAGrE,gGAAgG;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAA;AAE3D;;;;;;;GAOG;AACH,MAAM,gBAAgB,GAAsB;IAC1C;;CAED;IACC;;;;;;;;;;;;;;;;CAgBD;IACC;;;;;;CAMD;IACC;;CAED;IACC;;;;;CAKD;IACC;;;;;;CAMD;IACC;;CAED;CACA,CAAA;AAED,wDAAwD;AACxD,MAAM,UAAU,sBAAsB,CAAC,IAAkB;IACvD,OAAO,kBAAkB,CAAC;QACxB,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACrD,YAAY,EAAE,gBAAgB;QAC9B,QAAQ,EACN,oCAAoC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,WAAW,IAAI;YACxE,2DAA2D,IAAI,CAAC,QAAQ,wBAAwB;YAChG,yDAAyD;KAC5D,CAAC,CAAA;AACJ,CAAC"}
@@ -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,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAE9C,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,CA4DtE"}
@@ -0,0 +1,95 @@
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 { Gatekeeper } from '../gatekeeper.js';
20
+ import { loopbackExport } from './exports.js';
21
+ import { supportedResourceFor } from './resources.js';
22
+ import { renderTierSessionTypes } from './session-types.js';
23
+ /**
24
+ * A fresh account id.
25
+ *
26
+ * Random rather than derived: the vendor is handed nothing to derive one FROM (`createAccount()`
27
+ * takes no arguments by design, so that a public method cannot be used to look an existing account
28
+ * up), and a guessable id would be one an agent could name in place of its own.
29
+ */
30
+ function mintAccountId() {
31
+ return `acct_${crypto.randomUUID().replaceAll('-', '')}`;
32
+ }
33
+ /**
34
+ * Build the vendor entrypoint class over a deployment's policy.
35
+ *
36
+ * The deployment exports the result under the name the OS pins:
37
+ *
38
+ * export const GatekeeperVendor = createGatekeeperVendor({ policy: POLICY })
39
+ */
40
+ export function createGatekeeperVendor(options) {
41
+ const { policy } = options;
42
+ return class GatekeeperVendor extends WorkerEntrypoint {
43
+ #gatekeeper() {
44
+ return Gatekeeper.create(this.env, policy);
45
+ }
46
+ async describe() {
47
+ const deployment = this.#gatekeeper().deployment;
48
+ return {
49
+ displayName: 'cat-factory',
50
+ url: deployment,
51
+ tagline: 'Ship software with managed agents.',
52
+ description: 'File work onto a cat-factory board, start a pipeline run, watch it, and answer what it ' +
53
+ 'parks on. Calls are made with a per-account API key this Gatekeeper mints and holds; ' +
54
+ 'no agent ever sees a credential.',
55
+ // This vendor cannot authenticate anyone for sign-in: there is no per-user OAuth on the
56
+ // cat-factory side, so no provider-verified email exists to be one.
57
+ providesAuth: false,
58
+ // It CAN mint an account with no flow, which is the contract's own escape hatch for
59
+ // exactly this case. See `account.ts` for what that does and does not buy.
60
+ autoProvisionsAccount: true,
61
+ };
62
+ }
63
+ async getSupportedResources(_options) {
64
+ return [supportedResourceFor(this.#gatekeeper().deployment)];
65
+ }
66
+ /**
67
+ * The session types, rendered for the tier an auto-provisioned account will get.
68
+ *
69
+ * That is `autoProvisionedTier`, never `defaultTier`: the two are different knobs answering
70
+ * different questions (`policy/compile.ts` holds why), and this door only ever mints accounts.
71
+ * The tier is resolved WITHOUT an account, because there is none yet and inventing one to
72
+ * resolve through would make the refusal name an `acct_…` id no operator could find. The
73
+ * authoritative copy for a bound session is the resource's own `getTypeScriptTypes()`, which
74
+ * knows the tier the account actually resolved to and is the one `ResourceDescription.tsType`
75
+ * points at.
76
+ */
77
+ async getTypeScriptTypes() {
78
+ return renderTierSessionTypes(this.#gatekeeper().tierForNewAccount());
79
+ }
80
+ /**
81
+ * Mint a new connected account.
82
+ *
83
+ * Deliberately creating-only and identity-free, which is what makes it safe on a public
84
+ * interface: it cannot look an existing account up, so holding the binding buys the ability to
85
+ * make an account and never the ability to reach somebody else's.
86
+ */
87
+ async createAccount() {
88
+ const props = { accountId: mintAccountId() };
89
+ // Resolved before the account is handed back, so a deployment missing the export is refused
90
+ // at the call that would have created the account rather than at the first use of it.
91
+ return loopbackExport(this.ctx.exports, 'account', props);
92
+ }
93
+ };
94
+ }
95
+ //# 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;AAErD,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,OAAO,cAAc,CAAU,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;QACpE,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -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,CAqBtE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAIzF;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"}
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"}