@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,150 @@
1
+ /** An image the workspace renders beside a vendor, an account or a resource. */
2
+ export interface AvatarImage {
3
+ url: string;
4
+ }
5
+ /**
6
+ * The stable tag an OS admin rule is keyed on, plus the label a human reads.
7
+ *
8
+ * The tag has to survive a rename of the label, because a pre-approval rule an operator wrote
9
+ * months ago is matched by it. Ours is the binding name, which is generated from the operation's
10
+ * own group and method, so it is exactly as stable as the public API is.
11
+ */
12
+ export interface ActionKind {
13
+ tag: string;
14
+ label: string;
15
+ }
16
+ /** What the OS shows about a vendor before any account exists. */
17
+ export interface VendorDescription {
18
+ displayName: string;
19
+ url: string;
20
+ logo?: AvatarImage;
21
+ color?: string;
22
+ tagline?: string;
23
+ description?: string;
24
+ /** True only if the connect flow yields a provider-verified email. Ours does not. */
25
+ providesAuth?: boolean;
26
+ /** True when the vendor can mint an account with no OAuth flow (see `createAccount`). */
27
+ autoProvisionsAccount?: boolean;
28
+ }
29
+ /** A class of thing this vendor can serve, matched against a URL by `urlPattern`. */
30
+ export interface SupportedResource {
31
+ /** A URLPattern string, e.g. `https://cat-factory.example.com/*`. */
32
+ urlPattern: string;
33
+ title: string;
34
+ description: string;
35
+ icon?: AvatarImage;
36
+ /** True when the account must be granted access to this type separately. */
37
+ grantable?: boolean;
38
+ }
39
+ /** What the OS shows about one connected account. */
40
+ export interface AccountDescription {
41
+ displayName?: string;
42
+ /** The canonical, unique name for the account. */
43
+ uniqueName?: string;
44
+ avatar: AvatarImage;
45
+ grantedResourceUrlPatterns?: string[];
46
+ }
47
+ /** What the OS shows about one bound resource, and how it types the session behind it. */
48
+ export interface ResourceDescription {
49
+ /** The resource's canonical URL: visiting it in a browser opens the resource's own UI. */
50
+ url: string;
51
+ title: string;
52
+ snippet: string;
53
+ /** The name the binding is first created with. The user may rename it. */
54
+ suggestedBindingName: string;
55
+ /** A type name exported by this gatekeeper's `getTypeScriptTypes()`. */
56
+ tsType: string;
57
+ }
58
+ /**
59
+ * A read the gadget wants to make, described for the authorizer.
60
+ *
61
+ * `prohibitAllSharing` is the strongest statement available and the one worth getting right: it
62
+ * says the data this read returns may not be shared onward with any observer, whatever their own
63
+ * access is.
64
+ */
65
+ export interface ObservationDescription {
66
+ title: string;
67
+ description: string;
68
+ prohibitAllSharing?: boolean;
69
+ excludeObservers?: string[];
70
+ }
71
+ /** A side effect the gadget wants to make, described for the approver. */
72
+ export interface ActionDescription {
73
+ /** One line, like an email subject, for a list. */
74
+ title: string;
75
+ /** Markdown. Everything an approver needs in order to decide, with nothing held back. */
76
+ description: string;
77
+ /** Whether this gatekeeper implements `revertAction` for this action. */
78
+ implementsRevert: boolean;
79
+ /**
80
+ * Tells the agent not to keep working until this settles. Set by a gatekeeper that does NOT
81
+ * simulate the action's effects, which is ours: there is no provisional run id to hand back.
82
+ */
83
+ awaitDecision?: boolean;
84
+ /** The author's verdict that this action is safe to auto-apply, if the user opted in. */
85
+ autoApprovable?: boolean;
86
+ actionKind?: ActionKind;
87
+ }
88
+ /** The half of the queue that governs reads. */
89
+ export interface ObservationAuthorizer {
90
+ /** Resolves when the read is permitted, and THROWS when it is not. */
91
+ authorizeObservation(description: ObservationDescription): Promise<void>;
92
+ }
93
+ /**
94
+ * What the OS hands to `startSession`: the chokepoint every operation passes through.
95
+ *
96
+ * `submitAction` takes an id this gatekeeper assigns and returns as soon as the action is QUEUED,
97
+ * not when it is decided. The decision arrives later as `applyAction(id)` or `rejectAction(id)` on
98
+ * the resource object.
99
+ */
100
+ export interface ApprovalQueue extends ObservationAuthorizer {
101
+ submitAction(action: number, description: ActionDescription): Promise<void>;
102
+ }
103
+ /** What a `GATEKEEPER_*` service binding reaches. */
104
+ export interface VendorEntrypoint {
105
+ describe(): Promise<VendorDescription>;
106
+ getSupportedResources(options?: {
107
+ userId?: string;
108
+ }): Promise<SupportedResource[]>;
109
+ getTypeScriptTypes(): Promise<string>;
110
+ /** Returns the account stub the workspace persists. Opaque here: it is the OS's to hold. */
111
+ createAccount(): Promise<unknown>;
112
+ }
113
+ /** What the workspace holds for one connected account. */
114
+ export interface AccountEntrypoint {
115
+ describe(): Promise<AccountDescription>;
116
+ getSupportedResources(): Promise<SupportedResource[]>;
117
+ getGatekeeperClassFor(url: string): Promise<{
118
+ class: unknown;
119
+ resource: SupportedResource;
120
+ }>;
121
+ ensureResources(resourceUrlPatterns: string[]): Promise<Record<string, never>>;
122
+ revoke(): Promise<void>;
123
+ getVerifier(): Promise<unknown>;
124
+ getAuthenticatedEmail(): Promise<string | null>;
125
+ reconnect(): Promise<{
126
+ url: string;
127
+ }>;
128
+ }
129
+ /** What one bound resource serves: the session, its types, and the action lifecycle. */
130
+ export interface ResourceObject {
131
+ describe(): Promise<ResourceDescription>;
132
+ getTypeScriptTypes(): Promise<string>;
133
+ startSession(approvalQueue: ApprovalQueue): Promise<unknown>;
134
+ getAutoApprovableActions(): Promise<ActionKind[]>;
135
+ applyAction(action: number): Promise<void>;
136
+ rejectAction(action: number): Promise<void>;
137
+ revertAction(action: number): Promise<{
138
+ message: string;
139
+ canRetry: false;
140
+ }>;
141
+ addObserver(id: string, user: unknown): Promise<void>;
142
+ removeObserver(id: string): Promise<void>;
143
+ }
144
+ /** The identity stub another vendor's gatekeeper is handed. Carries no authority by design. */
145
+ export interface VerifierEntrypoint {
146
+ describe(): Promise<{
147
+ accountId: string;
148
+ }>;
149
+ }
150
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/os/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ // The Cloudflare OS gatekeeper contract, transcribed.
2
+ //
3
+ // The published source (`cloudflare/cloudflare-os`, `packages/workshop-shared/src/gatekeeper.ts`)
4
+ // is the authority for every shape below. It is TRANSCRIBED rather than imported because this
5
+ // package depends on the cat-factory public surface and nothing else: taking a dependency on the
6
+ // partner workspace would make their release cadence this package's build, which is the same
7
+ // coupling the initiative's own CI rule refuses. The cost is that these declarations can fall
8
+ // behind, and the answer to that is the nightly leg (`GATEKEEPER_OS_REF`), which boots a pinned
9
+ // partner commit against this Worker and is allowed to go red on its own.
10
+ //
11
+ // The transcription is deliberately PARTIAL: it carries the members this Worker implements or is
12
+ // handed, and omits the ones it does not (`getAgentCatalog`, `getSlashCommandProvider`,
13
+ // `startAppUi`, the UI frames). An omitted optional member is a capability the OS correctly
14
+ // reports as absent; a transcribed one this Worker did not implement would be a method that
15
+ // exists and lies.
16
+ //
17
+ // Structural typing is what makes this safe. Nothing here is `extends`ed by a class the OS
18
+ // constructs: the OS reaches a bound Worker's entrypoint over native RPC and calls methods by
19
+ // name, so what has to match is the SHAPE. These interfaces exist to make our shape a compile-time
20
+ // claim rather than a hope.
21
+ export {};
22
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/os/protocol.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,kGAAkG;AAClG,8FAA8F;AAC9F,iGAAiG;AACjG,6FAA6F;AAC7F,8FAA8F;AAC9F,gGAAgG;AAChG,0EAA0E;AAC1E,EAAE;AACF,iGAAiG;AACjG,wFAAwF;AACxF,4FAA4F;AAC5F,4FAA4F;AAC5F,mBAAmB;AACnB,EAAE;AACF,2FAA2F;AAC3F,8FAA8F;AAC9F,mGAAmG;AACnG,4BAA4B"}
@@ -0,0 +1,90 @@
1
+ import type { SessionGovernance } from '../capability.js';
2
+ import { type CallSubject } from './descriptions.js';
3
+ import type { ApprovalQueue } from './protocol.js';
4
+ /**
5
+ * One session's view of the ledger: what it may register, and the end of everything it registered.
6
+ *
7
+ * A handle rather than a second store, because the IDS are the object's (see {@link ActionLedger})
8
+ * while the LIFETIME is the session's, and the two facts have to live in one map for a decision
9
+ * naming an id to find the entry it settles.
10
+ */
11
+ export interface LedgerSession {
12
+ /** Register an effect, returning the id to submit it under and the promise the caller awaits. */
13
+ register(label: string, perform: () => Promise<unknown>): {
14
+ id: number;
15
+ settled: Promise<unknown>;
16
+ };
17
+ /**
18
+ * Drop a registration whose submission never reached the queue.
19
+ *
20
+ * Without it a queue that throws would leave an entry the workspace can never decide, and the
21
+ * caller would be told about the submission failure while the ledger silently grew.
22
+ */
23
+ abandon(id: number): void;
24
+ /** The session is over: refuse every action it left undecided and drop them. */
25
+ end(): void;
26
+ }
27
+ /**
28
+ * The submitted-but-undecided actions of one resource object, and the decisions that settle them.
29
+ *
30
+ * Ids are sequential per object rather than per session, because the OS hands them back to the
31
+ * OBJECT (`applyAction` is a method on the resource, not on the session): two sessions minting
32
+ * overlapping ids would settle each other's actions.
33
+ */
34
+ export declare class ActionLedger {
35
+ #private;
36
+ /** Open one session's handle. Ending it is what bounds the entries it registered. */
37
+ openSession(): LedgerSession;
38
+ /**
39
+ * Perform an approved action and hand its result to the waiting caller.
40
+ *
41
+ * The entry is removed BEFORE the effect runs, so a redelivered `applyAction` cannot perform it
42
+ * twice: at-least-once delivery is the normal state of a decision arriving over a network, and
43
+ * the second copy must be a refusal rather than a second write.
44
+ *
45
+ * A failing effect rejects the caller AND throws on to the workspace, which the contract asks
46
+ * for: the person who approved it is the one who gets to retry or discard.
47
+ */
48
+ apply(id: number): Promise<void>;
49
+ /** Reject an action: the awaiting call throws, and nothing is performed. */
50
+ reject(id: number): void;
51
+ /**
52
+ * How many actions are still waiting, across every live session on this object.
53
+ *
54
+ * Here so the ledger's one growth path is observable: an entry is added by a session and removed
55
+ * by a decision, an abandoned submission, or that session ending, and a count that does not
56
+ * return to zero once the sessions are gone is the leak this exists to fail a test on.
57
+ */
58
+ get pendingCount(): number;
59
+ }
60
+ /**
61
+ * Take a reference to the workspace's queue that outlives the call it arrived on.
62
+ *
63
+ * Workers RPC disposes every stub a call received as a PARAMETER the moment that call returns, and
64
+ * `startSession` returns immediately with a session that goes on using the queue for the rest of
65
+ * its life. Without this the first governed read or write would authorize against a stub the
66
+ * runtime had already torn down, and the failure would reach the caller as a broken connection
67
+ * rather than as anything naming the queue. `dup()` is what the RPC contract offers for exactly
68
+ * this case.
69
+ *
70
+ * The check is not defensiveness about a malformed argument: `ResourceCore` is also driven
71
+ * IN-PROCESS, by this package's own suite and by anything embedding it, and there the queue is an
72
+ * ordinary object that was never a stub and has nothing to duplicate.
73
+ *
74
+ * Paired with {@link releaseQueue} and kept beside it, because a duplicate taken here and released
75
+ * anywhere else is a leak that nothing about either half would show on its own.
76
+ */
77
+ export declare function holdQueue(queue: ApprovalQueue): ApprovalQueue;
78
+ /**
79
+ * The governance one session applies, over the queue the workspace handed it at `startSession`.
80
+ *
81
+ * Every method here is a thin translation: what to describe comes from the operation table
82
+ * (`descriptions.ts`), what to do about the answer is the contract's, and the effect itself stays
83
+ * the machinery's. That is what keeps this a facade rather than a second implementation.
84
+ */
85
+ export declare function queueGovernance(deps: {
86
+ queue: ApprovalQueue;
87
+ ledger: LedgerSession;
88
+ subject: CallSubject;
89
+ }): SessionGovernance;
90
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/os/queue.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEzD,OAAO,EAAuC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAclD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,iGAAiG;IACjG,QAAQ,CACN,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAC9B;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAA;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,gFAAgF;IAChF,GAAG,IAAI,IAAI,CAAA;CACZ;AAED;;;;;;GAMG;AACH,qBAAa,YAAY;;IAKvB,qFAAqF;IACrF,WAAW,IAAI,aAAa,CAO3B;IAED;;;;;;;;;OASG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQrC;IAED,4EAA4E;IAC5E,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAQvB;IAED;;;;;;OAMG;IACH,IAAI,YAAY,IAAI,MAAM,CAEzB;CAiDF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAG7D;AAaD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,aAAa,CAAA;IACrB,OAAO,EAAE,WAAW,CAAA;CACrB,GAAG,iBAAiB,CA4CpB"}
@@ -0,0 +1,197 @@
1
+ // The approval queue, threaded onto the session's one `invoke` seam.
2
+ //
3
+ // This is the half of the published contract that runs in the opposite direction from our approval
4
+ // INBOX, and the two must not be confused. The inbox carries the PLATFORM's parked runs outward to
5
+ // the OS, so a person can answer them. The queue carries the OS's governance INWARD over every call
6
+ // an agent makes, so nothing is read or written without the workspace's kernel having authorized
7
+ // it. A Gatekeeper needs both, and having one is no reason to skip the other.
8
+ //
9
+ // The action lifecycle is genuinely asynchronous, and the shape follows from that:
10
+ //
11
+ // 1. A mutating call assigns the next action id and records the effect in memory, UNPERFORMED.
12
+ // 2. It submits the description and awaits a decision that does not come back from `submitAction`.
13
+ // 3. `applyAction(id)` performs the effect and resolves the awaiting call with its result;
14
+ // `rejectAction(id)` rejects it.
15
+ //
16
+ // The pending set is in MEMORY, on purpose. The awaiting caller is a suspended RPC into this object,
17
+ // so the object cannot be evicted while an action is pending, and if the RPC dies there is nobody
18
+ // left to resolve: persisting the effect would only let a later `applyAction` perform a write whose
19
+ // result nothing reads and whose caller was told nothing. An action that outlives its session is a
20
+ // SIMULATING gatekeeper's problem, and simulation is deliberately not what this one does
21
+ // (`awaitDecision: true` says so on every action it submits).
22
+ //
23
+ // So an entry's LIFETIME IS ITS SESSION'S, and nothing else bounds it. A TTL would be the wrong
24
+ // bound twice over: the thing being waited on is a person deciding, which has no deadline worth
25
+ // guessing, and expiring an entry would settle an action the workspace can still legitimately
26
+ // apply. What DOES end is the session: when the workspace drops it, every action it submitted is
27
+ // refused and dropped together, so the ledger of a long-lived resource object holds pending work
28
+ // for live sessions only. Without that, an approval card nobody ever answered pinned its entry for
29
+ // the object's whole lifetime, and `#take`'s "the session that submitted it has ended" was a
30
+ // sentence nothing made true.
31
+ import { GatekeeperError } from '../errors.js';
32
+ import { describeAction, describeObservation } from './descriptions.js';
33
+ /**
34
+ * The submitted-but-undecided actions of one resource object, and the decisions that settle them.
35
+ *
36
+ * Ids are sequential per object rather than per session, because the OS hands them back to the
37
+ * OBJECT (`applyAction` is a method on the resource, not on the session): two sessions minting
38
+ * overlapping ids would settle each other's actions.
39
+ */
40
+ export class ActionLedger {
41
+ #nextId = 1;
42
+ #nextSession = 1;
43
+ #pending = new Map();
44
+ /** Open one session's handle. Ending it is what bounds the entries it registered. */
45
+ openSession() {
46
+ const session = this.#nextSession++;
47
+ return {
48
+ register: (label, perform) => this.#register(session, label, perform),
49
+ abandon: (id) => this.#pending.delete(id),
50
+ end: () => this.#endSession(session),
51
+ };
52
+ }
53
+ /**
54
+ * Perform an approved action and hand its result to the waiting caller.
55
+ *
56
+ * The entry is removed BEFORE the effect runs, so a redelivered `applyAction` cannot perform it
57
+ * twice: at-least-once delivery is the normal state of a decision arriving over a network, and
58
+ * the second copy must be a refusal rather than a second write.
59
+ *
60
+ * A failing effect rejects the caller AND throws on to the workspace, which the contract asks
61
+ * for: the person who approved it is the one who gets to retry or discard.
62
+ */
63
+ async apply(id) {
64
+ const action = this.#take(id, 'apply');
65
+ try {
66
+ action.resolve(await action.perform());
67
+ }
68
+ catch (error) {
69
+ action.reject(error);
70
+ throw error;
71
+ }
72
+ }
73
+ /** Reject an action: the awaiting call throws, and nothing is performed. */
74
+ reject(id) {
75
+ const action = this.#take(id, 'reject');
76
+ action.reject(new GatekeeperError('action_rejected', `The workspace rejected this action (${action.label}), so it was not performed.`));
77
+ }
78
+ /**
79
+ * How many actions are still waiting, across every live session on this object.
80
+ *
81
+ * Here so the ledger's one growth path is observable: an entry is added by a session and removed
82
+ * by a decision, an abandoned submission, or that session ending, and a count that does not
83
+ * return to zero once the sessions are gone is the leak this exists to fail a test on.
84
+ */
85
+ get pendingCount() {
86
+ return this.#pending.size;
87
+ }
88
+ #register(session, label, perform) {
89
+ const id = this.#nextId++;
90
+ const settled = new Promise((resolve, reject) => {
91
+ this.#pending.set(id, { perform, resolve, reject, label, session });
92
+ });
93
+ return { id, settled };
94
+ }
95
+ /**
96
+ * End one session: every action it submitted and nobody decided is refused, and nothing is
97
+ * performed.
98
+ *
99
+ * The awaiting caller is told, rather than left on a promise that can no longer settle: the
100
+ * session it called through is gone, so no `applyAction` can ever reach its effect again.
101
+ */
102
+ #endSession(session) {
103
+ // Deleting the entry the iterator is standing on is defined behaviour for a Map, so this
104
+ // walks the live one rather than a snapshot of it.
105
+ for (const [id, action] of this.#pending) {
106
+ if (action.session !== session)
107
+ continue;
108
+ this.#pending.delete(id);
109
+ action.reject(new GatekeeperError('session_ended', `The session that submitted this action (${action.label}) ended before the workspace ` +
110
+ 'decided it, so it was not performed.'));
111
+ }
112
+ }
113
+ #take(id, verb) {
114
+ const action = this.#pending.get(id);
115
+ if (action === undefined) {
116
+ throw new GatekeeperError('unknown_action', `No action ${id} is pending on this resource, so there is nothing to ${verb}. It was ` +
117
+ 'already decided, or the session that submitted it has ended.');
118
+ }
119
+ this.#pending.delete(id);
120
+ return action;
121
+ }
122
+ }
123
+ /**
124
+ * Take a reference to the workspace's queue that outlives the call it arrived on.
125
+ *
126
+ * Workers RPC disposes every stub a call received as a PARAMETER the moment that call returns, and
127
+ * `startSession` returns immediately with a session that goes on using the queue for the rest of
128
+ * its life. Without this the first governed read or write would authorize against a stub the
129
+ * runtime had already torn down, and the failure would reach the caller as a broken connection
130
+ * rather than as anything naming the queue. `dup()` is what the RPC contract offers for exactly
131
+ * this case.
132
+ *
133
+ * The check is not defensiveness about a malformed argument: `ResourceCore` is also driven
134
+ * IN-PROCESS, by this package's own suite and by anything embedding it, and there the queue is an
135
+ * ordinary object that was never a stub and has nothing to duplicate.
136
+ *
137
+ * Paired with {@link releaseQueue} and kept beside it, because a duplicate taken here and released
138
+ * anywhere else is a leak that nothing about either half would show on its own.
139
+ */
140
+ export function holdQueue(queue) {
141
+ const stub = queue;
142
+ return typeof stub.dup === 'function' ? stub.dup() : queue;
143
+ }
144
+ /**
145
+ * Release this session's hold on the workspace's queue.
146
+ *
147
+ * The other half of {@link holdQueue}: a duplicate nobody disposes holds the workspace's end of
148
+ * the connection open for the resource object's whole lifetime. The optional call is the
149
+ * in-process case again, where nothing was duplicated and there is nothing to give back.
150
+ */
151
+ function releaseQueue(queue) {
152
+ ;
153
+ queue[Symbol.dispose]?.();
154
+ }
155
+ /**
156
+ * The governance one session applies, over the queue the workspace handed it at `startSession`.
157
+ *
158
+ * Every method here is a thin translation: what to describe comes from the operation table
159
+ * (`descriptions.ts`), what to do about the answer is the contract's, and the effect itself stays
160
+ * the machinery's. That is what keeps this a facade rather than a second implementation.
161
+ */
162
+ export function queueGovernance(deps) {
163
+ return {
164
+ async observe(binding, args) {
165
+ await deps.queue.authorizeObservation(describeObservation(binding, args, deps.subject));
166
+ },
167
+ async observeLocal(title, detail) {
168
+ await deps.queue.authorizeObservation({
169
+ title: `${title} (${deps.subject.deployment})`,
170
+ description: `${detail}\n\nServed from this Gatekeeper's own record of what the paired deployment ` +
171
+ `at ${deps.subject.deployment} delivered, for account \`${deps.subject.accountId}\` at ` +
172
+ `policy tier \`${deps.subject.tier}\`.`,
173
+ });
174
+ },
175
+ async act(binding, args, perform) {
176
+ const { id, settled } = deps.ledger.register(binding.summary, perform);
177
+ try {
178
+ await deps.queue.submitAction(id, describeAction(binding, args, deps.subject));
179
+ }
180
+ catch (error) {
181
+ // The submission failed, so no decision will ever arrive for this id. Dropping the
182
+ // registration is what stops the caller waiting on a promise nothing can settle.
183
+ deps.ledger.abandon(id);
184
+ throw error;
185
+ }
186
+ return (await settled);
187
+ },
188
+ close() {
189
+ // Order matters: the actions are refused while the queue is still ours to hold, so an
190
+ // awaiting caller is told the session ended rather than left on a promise whose only route
191
+ // to a decision has already been released.
192
+ deps.ledger.end();
193
+ releaseQueue(deps.queue);
194
+ },
195
+ };
196
+ }
197
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/os/queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import type { GatekeeperEnv } from '../env.js';
2
+ import type { GatekeeperPolicy } from '../policy/compile.js';
3
+ import type { ActionKind, ApprovalQueue, ResourceDescription, ResourceObject } from './protocol.js';
4
+ /**
5
+ * What the account imbues a resource object with when it hands the class to the workspace.
6
+ *
7
+ * The account and NOTHING else. The URL the workspace matched is deliberately not here: under this
8
+ * Gatekeeper's own model a resource IS the paired workspace, so every URL that matches the pattern
9
+ * binds the same one and the matched URL decides nothing. Carrying it would be state whose only
10
+ * effect is that the next reader has to work out it decides nothing, and it had already been
11
+ * assigned the pattern rather than the match without anything noticing. A consumer that genuinely
12
+ * needs the bound URL adds it back WITH the reader that wants it, which is also the change that
13
+ * would say which of the two it meant.
14
+ */
15
+ export interface ResourceProps {
16
+ /** The account this resource is bound for. Every key minted through it is stamped with this. */
17
+ accountId: string;
18
+ }
19
+ /** One bound resource: the session it opens, its types, and the action lifecycle behind it. */
20
+ export declare class ResourceCore implements ResourceObject {
21
+ #private;
22
+ constructor(env: GatekeeperEnv, policy: GatekeeperPolicy, props: ResourceProps);
23
+ describe(): Promise<ResourceDescription>;
24
+ /**
25
+ * The session's own types, rendered for the tier this account holds.
26
+ *
27
+ * Tier-specific rather than a copy of the vendor's, because this is the one the contract points a
28
+ * caller at: `ResourceDescription.tsType` must name an export of THIS method's output, and a
29
+ * session carries exactly its granted operations.
30
+ */
31
+ getTypeScriptTypes(): Promise<string>;
32
+ /**
33
+ * Open a governed session.
34
+ *
35
+ * The queue is not optional and not a decoration: every operation the returned object carries
36
+ * funnels through the one `invoke` closure, which submits actions to this queue and authorizes
37
+ * observations against it. The tier policy underneath is the FLOOR, so an operation the policy
38
+ * never granted is absent from the object rather than something the queue has to refuse.
39
+ *
40
+ * The queue passed here is OWNED by the session that comes back: it is released, along with
41
+ * every action that session left undecided, when the session is disposed. A caller reaching this
42
+ * over RPC therefore hands in a reference of its own rather than the parameter it received (see
43
+ * `resource.ts`), because the parameter's lifetime ends when this call returns and the session's
44
+ * does not.
45
+ */
46
+ startSession(approvalQueue: ApprovalQueue): Promise<unknown>;
47
+ /**
48
+ * How many submitted actions this object is still holding, across every live session.
49
+ *
50
+ * Exposed for the same reason the ledger counts them: the ONE unbounded thing about a long-lived
51
+ * resource object is this set, and a count that does not fall back to zero when the sessions are
52
+ * gone is the leak rather than a slow day.
53
+ */
54
+ get pendingActionCount(): number;
55
+ /**
56
+ * The action kinds this Gatekeeper may auto-apply, if the user opted into the kind.
57
+ *
58
+ * Derived from the same table and the same consequence reading `describeAction` stamps onto each
59
+ * submission, so a pre-approval UI listing kinds before any action exists and the `autoApprovable`
60
+ * flag on an action that has been submitted cannot disagree.
61
+ *
62
+ * TODAY THIS IS EMPTY, and that is the honest answer rather than a gap. The public surface
63
+ * annotates a consequence only where the stakes are real money or a merged pull request, so every
64
+ * other mutation is unannotated, and the table's documented reading of an unannotated mutation is
65
+ * that it is destructive. Offering those for unattended auto-approval would mean inverting that
66
+ * default here, which is precisely the misreading `resolveConsequence` exists to stop. If the
67
+ * surface ever states that a write is safe, it appears here with no further decision.
68
+ */
69
+ getAutoApprovableActions(): Promise<ActionKind[]>;
70
+ /** The workspace approved an action: perform it, and hand the result to the waiting call. */
71
+ applyAction(action: number): Promise<void>;
72
+ /** The workspace rejected an action: the waiting call throws and nothing is performed. */
73
+ rejectAction(action: number): Promise<void>;
74
+ /**
75
+ * Reverting is not something this Gatekeeper can do, which every action it submits already states
76
+ * (`implementsRevert: false`).
77
+ *
78
+ * It answers rather than throwing because the caller is the workspace UI on behalf of a person who
79
+ * wants their change undone: a message naming what they have to do themselves is the useful
80
+ * answer, where an exception would surface as a failed revert they might retry.
81
+ */
82
+ revertAction(_action: number): Promise<{
83
+ message: string;
84
+ canRetry: false;
85
+ }>;
86
+ /**
87
+ * Sharing this resource's observations onward is REFUSED, which blocks the share.
88
+ *
89
+ * The contract asks the gatekeeper to verify that the new viewer could directly read everything
90
+ * historically observed through it. This Gatekeeper cannot answer that: it keeps no observation
91
+ * log, and the plausible rule (the observer's own tier reaches every operation that produced the
92
+ * observed data) needs a tier for a viewer this deployment's policy has never named. A share
93
+ * blocked loudly beats an observation leaked quietly, so the refusal stands until there is a rule
94
+ * worth writing down.
95
+ */
96
+ addObserver(id: string, _user: unknown): Promise<void>;
97
+ /** Idempotent by contract: nothing was ever added, so there is nothing to forget. */
98
+ removeObserver(_id: string): Promise<void>;
99
+ }
100
+ //# sourceMappingURL=resource-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-core.d.ts","sourceRoot":"","sources":["../../src/os/resource-core.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAG9C,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE1E,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInG;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,gGAAgG;IAChG,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,+FAA+F;AAC/F,qBAAa,YAAa,YAAW,cAAc;;IAMjD,YAAY,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAI7E;IAEK,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAW7C;IAED;;;;;;OAMG;IACG,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAE1C;IAED;;;;;;;;;;;;;OAaG;IACG,YAAY,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAejE;IAED;;;;;;OAMG;IACH,IAAI,kBAAkB,IAAI,MAAM,CAE/B;IAED;;;;;;;;;;;;;OAaG;IACG,wBAAwB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAKtD;IAED,6FAA6F;IACvF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;IAED,0FAA0F;IACpF,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhD;IAED;;;;;;;OAOG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAC,CAOjF;IAED;;;;;;;;;OASG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAO3D;IAED,qFAAqF;IAC/E,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAG;CASpD"}