@cat-factory/gatekeeper-worker 0.3.1

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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +276 -0
  3. package/dist/approvals.d.ts +90 -0
  4. package/dist/approvals.d.ts.map +1 -0
  5. package/dist/approvals.js +193 -0
  6. package/dist/approvals.js.map +1 -0
  7. package/dist/capability.d.ts +61 -0
  8. package/dist/capability.d.ts.map +1 -0
  9. package/dist/capability.js +125 -0
  10. package/dist/capability.js.map +1 -0
  11. package/dist/env.d.ts +45 -0
  12. package/dist/env.d.ts.map +1 -0
  13. package/dist/env.js +90 -0
  14. package/dist/env.js.map +1 -0
  15. package/dist/errors.d.ts +22 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +30 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/gatekeeper.d.ts +78 -0
  20. package/dist/gatekeeper.d.ts.map +1 -0
  21. package/dist/gatekeeper.js +162 -0
  22. package/dist/gatekeeper.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +23 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/keys.d.ts +62 -0
  28. package/dist/keys.d.ts.map +1 -0
  29. package/dist/keys.js +152 -0
  30. package/dist/keys.js.map +1 -0
  31. package/dist/masking.d.ts +9 -0
  32. package/dist/masking.d.ts.map +1 -0
  33. package/dist/masking.js +43 -0
  34. package/dist/masking.js.map +1 -0
  35. package/dist/policy/compile.d.ts +88 -0
  36. package/dist/policy/compile.d.ts.map +1 -0
  37. package/dist/policy/compile.js +170 -0
  38. package/dist/policy/compile.js.map +1 -0
  39. package/dist/policy/decisions.d.ts +93 -0
  40. package/dist/policy/decisions.d.ts.map +1 -0
  41. package/dist/policy/decisions.js +659 -0
  42. package/dist/policy/decisions.js.map +1 -0
  43. package/dist/policy/index.d.ts +6 -0
  44. package/dist/policy/index.d.ts.map +1 -0
  45. package/dist/policy/index.js +17 -0
  46. package/dist/policy/index.js.map +1 -0
  47. package/dist/state.d.ts +150 -0
  48. package/dist/state.d.ts.map +1 -0
  49. package/dist/state.js +229 -0
  50. package/dist/state.js.map +1 -0
  51. package/dist/webhook/delivery.d.ts +102 -0
  52. package/dist/webhook/delivery.d.ts.map +1 -0
  53. package/dist/webhook/delivery.js +162 -0
  54. package/dist/webhook/delivery.js.map +1 -0
  55. package/dist/webhook/signature.d.ts +17 -0
  56. package/dist/webhook/signature.d.ts.map +1 -0
  57. package/dist/webhook/signature.js +73 -0
  58. package/dist/webhook/signature.js.map +1 -0
  59. package/dist/worker.d.ts +21 -0
  60. package/dist/worker.d.ts.map +1 -0
  61. package/dist/worker.js +155 -0
  62. package/dist/worker.js.map +1 -0
  63. package/package.json +62 -0
@@ -0,0 +1,170 @@
1
+ // Compiling a Gatekeeper policy: turning what an operator wrote into the exact set of methods a
2
+ // capability will carry, plus a STATED account of everything it will not.
3
+ //
4
+ // The whole point of the generated table (`@cat-factory/gatekeeper-bindings`) is that policy is
5
+ // enforced against the operations the deployment actually serves, at the scope floors it actually
6
+ // enforces, rather than against a hand-curated list that drifts. So compilation starts from
7
+ // `bindingsWithinScope(keyScope)` and only ever SUBTRACTS. A tier cannot grant its way above the
8
+ // key backing it, and an attempt to is an operator error raised here rather than a method that
9
+ // exists, looks granted, and 403s on every call.
10
+ //
11
+ // Nothing here decides transport, retry or auth: the SDK does, through each binding's own
12
+ // `invoke` thunk.
13
+ import { bindingByName, bindingsWithinScope, resolveConsequence, } from '@cat-factory/gatekeeper-bindings';
14
+ import { PolicyError } from '../errors.js';
15
+ /**
16
+ * `admin` is deliberately unreachable as a tier's `keyScope`.
17
+ *
18
+ * `POST /api/v1/keys` mints `read`, `write` or `decide` and never `admin`, so the platform already
19
+ * holds the chain to one link: a key provisioned over the API can never itself provision. A tier
20
+ * asking for `admin` is therefore asking for the Gatekeeper's OWN provisioning secret to be handed
21
+ * to a caller, which is the one thing this whole design exists to prevent. Refusing it here names
22
+ * that, rather than letting the mint fail per-actor at runtime with a platform error an operator
23
+ * would read as a bug.
24
+ */
25
+ const MINTABLE_SCOPES = ['read', 'write', 'decide'];
26
+ /**
27
+ * A binding whose result is an SSE reader or raw bytes cannot cross a Cap'n Web call, which
28
+ * carries structured-clone values. It is withheld rather than exposed-and-failing, and it is
29
+ * NAMED as withheld for transport rather than for policy, because the fix differs: a caller that
30
+ * wants a run's events polls `tasks_get_run`, while a caller that wants a denied operation asks
31
+ * its operator to change the policy.
32
+ */
33
+ function relayable(binding) {
34
+ return binding.result === 'value';
35
+ }
36
+ function compileTier(name, tier) {
37
+ if (!MINTABLE_SCOPES.includes(tier.keyScope)) {
38
+ throw new PolicyError(`Tier '${name}' declares keyScope '${tier.keyScope}', which POST /api/v1/keys cannot mint ` +
39
+ `(it mints ${MINTABLE_SCOPES.join(', ')}). An admin-scoped tier would hand a caller the ` +
40
+ "Gatekeeper's own provisioning credential; grant the operations it needs at 'decide' instead.");
41
+ }
42
+ const withinScope = bindingsWithinScope(tier.keyScope);
43
+ const withinScopeNames = new Set(withinScope.map((binding) => binding.name));
44
+ const deny = new Set(tier.deny ?? []);
45
+ // Both lists are checked against the LIVE table first: a misspelled or retired name is an
46
+ // operator error with a fix, and silently ignoring it would make a deny read as enforced.
47
+ for (const [field, names] of [
48
+ ['allow', tier.allow === '*' ? [] : tier.allow],
49
+ ['deny', tier.deny ?? []],
50
+ ]) {
51
+ for (const bindingName of names) {
52
+ if (!bindingByName(bindingName)) {
53
+ throw new PolicyError(`Tier '${name}' names '${bindingName}' in ${field}, which this deployment's operation ` +
54
+ 'table does not carry. Check the spelling, or upgrade @cat-factory/gatekeeper-bindings ' +
55
+ 'if the operation is newer than this package.');
56
+ }
57
+ }
58
+ }
59
+ if (tier.allow !== '*') {
60
+ for (const bindingName of tier.allow) {
61
+ if (!withinScopeNames.has(bindingName) && !deny.has(bindingName)) {
62
+ const binding = bindingByName(bindingName);
63
+ throw new PolicyError(`Tier '${name}' allows '${bindingName}', whose scope floor is '${binding?.minScope}', but ` +
64
+ `the tier's keyScope is '${tier.keyScope}'. Raise the tier's keyScope or drop the ` +
65
+ 'operation: a granted method its key cannot call would refuse on every call.');
66
+ }
67
+ }
68
+ }
69
+ const allowed = tier.allow === '*' ? withinScopeNames : new Set(tier.allow);
70
+ const granted = [];
71
+ const withheld = [];
72
+ for (const binding of withinScope) {
73
+ if (deny.has(binding.name)) {
74
+ withheld.push({
75
+ name: binding.name,
76
+ reason: 'denied_by_policy',
77
+ detail: `Tier '${name}' denies it.`,
78
+ });
79
+ continue;
80
+ }
81
+ if (!allowed.has(binding.name)) {
82
+ withheld.push({
83
+ name: binding.name,
84
+ reason: 'not_in_policy',
85
+ detail: `Tier '${name}' does not allow it.`,
86
+ });
87
+ continue;
88
+ }
89
+ if (!relayable(binding)) {
90
+ withheld.push({
91
+ name: binding.name,
92
+ reason: 'not_relayable',
93
+ detail: `Its result is a ${binding.result}, which a Cap'n Web call cannot carry.`,
94
+ });
95
+ continue;
96
+ }
97
+ granted.push(binding);
98
+ }
99
+ for (const binding of bindingByScopeGap(withinScopeNames)) {
100
+ withheld.push({
101
+ name: binding.name,
102
+ reason: 'above_key_scope',
103
+ detail: `Its floor is '${binding.minScope}'; this tier's key is '${tier.keyScope}'.`,
104
+ });
105
+ }
106
+ return {
107
+ name,
108
+ description: tier.description,
109
+ keyScope: tier.keyScope,
110
+ granted,
111
+ withheld,
112
+ mask: tier.mask ?? [],
113
+ };
114
+ }
115
+ /** Every binding the deployment serves that the tier's own key could not call. */
116
+ function bindingByScopeGap(withinScope) {
117
+ return bindingsWithinScope('admin').filter((binding) => !withinScope.has(binding.name));
118
+ }
119
+ /**
120
+ * Compile a policy against the live operation table, or throw a {@link PolicyError} naming the
121
+ * first thing an operator has to fix.
122
+ */
123
+ export function compilePolicy(policy) {
124
+ const tiers = new Map();
125
+ for (const [name, tier] of Object.entries(policy.tiers)) {
126
+ tiers.set(name, compileTier(name, tier));
127
+ }
128
+ if (policy.defaultTier !== null && !tiers.has(policy.defaultTier)) {
129
+ throw new PolicyError(`defaultTier is '${policy.defaultTier}', which is not one of the declared tiers ` +
130
+ `(${[...tiers.keys()].join(', ')}).`);
131
+ }
132
+ for (const [actor, tierName] of Object.entries(policy.grants)) {
133
+ if (!tiers.has(tierName)) {
134
+ throw new PolicyError(`Actor '${actor}' is granted tier '${tierName}', which is not declared.`);
135
+ }
136
+ }
137
+ return { defaultTier: policy.defaultTier, tiers, grants: new Map(Object.entries(policy.grants)) };
138
+ }
139
+ /**
140
+ * The tier an actor holds, or a refusal.
141
+ *
142
+ * Note what is NOT here: nothing the CALLER sends picks a tier. The OS authenticates the person
143
+ * and the Gatekeeper resolves what that person may do, so a compromised or confused agent cannot
144
+ * ask for a better one.
145
+ */
146
+ export function tierForActor(policy, actorId) {
147
+ const name = policy.grants.get(actorId) ?? policy.defaultTier;
148
+ if (name === null)
149
+ return null;
150
+ return policy.tiers.get(name) ?? null;
151
+ }
152
+ /**
153
+ * What the OS needs to run its own approval governance over a call: the consequence the platform
154
+ * annotates, with the cautious default already applied.
155
+ */
156
+ export function describeBinding(binding) {
157
+ const consequence = resolveConsequence(binding);
158
+ return {
159
+ name: binding.name,
160
+ summary: binding.summary,
161
+ minScope: binding.minScope,
162
+ readOnly: binding.readOnly,
163
+ destructive: consequence.destructive,
164
+ idempotent: consequence.idempotent,
165
+ pathParams: binding.pathParams,
166
+ queryParams: binding.queryParams,
167
+ hasBody: binding.hasBody,
168
+ };
169
+ }
170
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/policy/compile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import type { PublicApiScope } from '@cat-factory/gatekeeper-bindings';
2
+ import type { PublicDecision } from '@cat-factory/sdk';
3
+ /**
4
+ * Every kind of park the surface can list, taken from the SDK's own union.
5
+ *
6
+ * Typed rather than enumerated here so the table below is EXHAUSTIVE by construction: a kind the
7
+ * platform gains fails this package's build until it has an answerer, instead of arriving in
8
+ * production as a card whose first answer attempt reports it stale. That is the whole reason the
9
+ * table is a `Record` keyed on the kind rather than an array of entries carrying one.
10
+ */
11
+ export type ParkedDecisionKind = PublicDecision['kind'];
12
+ /**
13
+ * One entry in a run's decision list, read structurally.
14
+ *
15
+ * Deliberately NOT typed against the published `PublicDecision` union: this package reads a live
16
+ * deployment's JSON, and a deployment one release ahead sends a kind (or a field) the union has
17
+ * never heard of. Narrowing here would turn that into a decode failure at the one place whose job
18
+ * is to say "the platform is holding this run on something I do not know how to answer".
19
+ */
20
+ export interface LiveDecision {
21
+ kind: string;
22
+ [field: string]: unknown;
23
+ }
24
+ /** What one verb, applied to one live decision, asks the platform to do. */
25
+ export interface DecisionCall {
26
+ binding: string;
27
+ args: Record<string, unknown>;
28
+ }
29
+ /** One way of answering a kind of park. */
30
+ export interface DecisionVerb {
31
+ /** What a caller passes as `action`. */
32
+ action: string;
33
+ /** The binding this verb forwards through. Policy is enforced there, and only there. */
34
+ binding: string;
35
+ /** One line an OS Gadget renders beside the verb. */
36
+ summary: string;
37
+ /**
38
+ * The caller-supplied fields this verb reads, with `required` naming the ones it refuses
39
+ * without. Published on `approvals_inspect` so a caller composes a valid answer from the
40
+ * capability itself rather than from a doc that can drift.
41
+ */
42
+ fields: readonly DecisionField[];
43
+ /** Build the call. Throws {@link GatekeeperError} `invalid_answer` on a field it cannot read. */
44
+ call: (decision: LiveDecision, input: AnswerFields) => DecisionCall;
45
+ }
46
+ /** One caller-supplied field a verb reads. */
47
+ export interface DecisionField {
48
+ name: string;
49
+ required: boolean;
50
+ /** The closed set of values, when there is one. */
51
+ choices?: readonly string[];
52
+ detail: string;
53
+ }
54
+ /** What a caller sends beside `action`, by the platform's own field names. */
55
+ export type AnswerFields = Readonly<Record<string, unknown>>;
56
+ /** How one kind of park is answered. Keyed by kind in {@link ANSWERERS}. */
57
+ export interface DecisionAnswerer {
58
+ /** Prose an OS Gadget shows above the verbs. */
59
+ summary: string;
60
+ /**
61
+ * Whether this entry is CURRENTLY holding the run.
62
+ *
63
+ * A decision list carries settled entries too (a resolved gate keeps its record), so answering
64
+ * the first entry of a kind would post against a park that is over. Every predicate reads the
65
+ * entry's own lifecycle field, and an entry whose field is missing or unrecognised reads as NOT
66
+ * pending: a park this package cannot confirm is one it must not answer blind.
67
+ */
68
+ pending: (decision: LiveDecision) => boolean;
69
+ verbs: readonly DecisionVerb[];
70
+ }
71
+ /** How a kind of park is answered, or `undefined` for one this package does not model. */
72
+ export declare function answererFor(kind: string): DecisionAnswerer | undefined;
73
+ /** Every kind this package can answer. */
74
+ export declare const ANSWERABLE_DECISION_KINDS: readonly ParkedDecisionKind[];
75
+ /**
76
+ * The binding names every answerer forwards through, plus the read that finds the park.
77
+ *
78
+ * Published so a policy author can grant "answer parked decisions" without transcribing forty
79
+ * operation names, and so `approvals_inspect` can say which verbs a tier actually holds. It is
80
+ * DERIVED from the table rather than restated, because a hand-kept copy is exactly the drift the
81
+ * generated binding table exists to prevent one layer down.
82
+ */
83
+ export declare const DECISION_BINDINGS: readonly string[];
84
+ /**
85
+ * The key scope every decision binding needs.
86
+ *
87
+ * Stated as a constant rather than read off the table because it is a POLICY fact a tier author
88
+ * needs before compiling: a tier granting these must mint `decide` keys. `policy.test.ts` pins it
89
+ * against the live table, so a surface that ever lowered a floor would fail there rather than
90
+ * leaving this comment quietly wrong.
91
+ */
92
+ export declare const DECISION_KEY_SCOPE: PublicApiScope;
93
+ //# sourceMappingURL=decisions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.d.ts","sourceRoot":"","sources":["../../src/policy/decisions.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGtD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;CACzB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9B;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;IAChC,iGAAiG;IACjG,IAAI,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,YAAY,CAAA;CACpE;AAED,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE5D,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;;OAOG;IACH,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,OAAO,CAAA;IAC5C,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAC/B;AAkpBD,0FAA0F;AAC1F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEtE;AAED,0CAA0C;AAC1C,eAAO,MAAM,yBAAyB,EAAE,SAAS,kBAAkB,EAE1C,CAAA;AAEzB;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAO9C,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAyB,CAAA"}