@decionis/agent-safe-pipeline 0.3.5 → 0.3.6

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/README.md +5 -4
  2. package/dist/Index.d.ts +6 -0
  3. package/dist/Index.d.ts.map +1 -1
  4. package/dist/Index.js +6 -0
  5. package/dist/Index.js.map +1 -1
  6. package/dist/audit/AuditRecorder.d.ts +1 -1
  7. package/dist/audit/AuditRecorder.d.ts.map +1 -1
  8. package/dist/audit/AuditRecorder.js.map +1 -1
  9. package/dist/decision/DecionisGate.d.ts +11 -1
  10. package/dist/decision/DecionisGate.d.ts.map +1 -1
  11. package/dist/decision/DecionisGate.js +52 -4
  12. package/dist/decision/DecionisGate.js.map +1 -1
  13. package/dist/decision/DecisionAuthority.d.ts +15 -1
  14. package/dist/decision/DecisionAuthority.d.ts.map +1 -1
  15. package/dist/decision/DecisionAuthority.js.map +1 -1
  16. package/dist/decision/edge/BundleWindow.d.ts +43 -0
  17. package/dist/decision/edge/BundleWindow.d.ts.map +1 -0
  18. package/dist/decision/edge/BundleWindow.js +41 -0
  19. package/dist/decision/edge/BundleWindow.js.map +1 -0
  20. package/dist/decision/edge/EdgeBundleManager.d.ts +87 -0
  21. package/dist/decision/edge/EdgeBundleManager.d.ts.map +1 -0
  22. package/dist/decision/edge/EdgeBundleManager.js +175 -0
  23. package/dist/decision/edge/EdgeBundleManager.js.map +1 -0
  24. package/dist/decision/edge/EdgeBundleSource.d.ts +59 -0
  25. package/dist/decision/edge/EdgeBundleSource.d.ts.map +1 -0
  26. package/dist/decision/edge/EdgeBundleSource.js +99 -0
  27. package/dist/decision/edge/EdgeBundleSource.js.map +1 -0
  28. package/dist/decision/edge/EdgeDecisionAuthority.d.ts +104 -0
  29. package/dist/decision/edge/EdgeDecisionAuthority.d.ts.map +1 -0
  30. package/dist/decision/edge/EdgeDecisionAuthority.js +197 -0
  31. package/dist/decision/edge/EdgeDecisionAuthority.js.map +1 -0
  32. package/dist/decision/edge/EdgeModule.d.ts +63 -0
  33. package/dist/decision/edge/EdgeModule.d.ts.map +1 -0
  34. package/dist/decision/edge/EdgeModule.js +147 -0
  35. package/dist/decision/edge/EdgeModule.js.map +1 -0
  36. package/dist/execution/ActionRegistry.d.ts +23 -0
  37. package/dist/execution/ActionRegistry.d.ts.map +1 -1
  38. package/dist/execution/ActionRegistry.js +32 -2
  39. package/dist/execution/ActionRegistry.js.map +1 -1
  40. package/dist/execution/AuthorizationVerifier.d.ts.map +1 -1
  41. package/dist/execution/AuthorizationVerifier.js +7 -2
  42. package/dist/execution/AuthorizationVerifier.js.map +1 -1
  43. package/dist/execution/LocalAuthorizationVerifier.d.ts +70 -0
  44. package/dist/execution/LocalAuthorizationVerifier.d.ts.map +1 -0
  45. package/dist/execution/LocalAuthorizationVerifier.js +119 -0
  46. package/dist/execution/LocalAuthorizationVerifier.js.map +1 -0
  47. package/dist/execution/PostgresReplayStore.d.ts +57 -0
  48. package/dist/execution/PostgresReplayStore.d.ts.map +1 -0
  49. package/dist/execution/PostgresReplayStore.js +81 -0
  50. package/dist/execution/PostgresReplayStore.js.map +1 -0
  51. package/dist/execution/ReplayStore.d.ts +14 -0
  52. package/dist/execution/ReplayStore.d.ts.map +1 -1
  53. package/dist/execution/ReplayStore.js +8 -0
  54. package/dist/execution/ReplayStore.js.map +1 -1
  55. package/dist/execution/SafeExecutor.d.ts +88 -2
  56. package/dist/execution/SafeExecutor.d.ts.map +1 -1
  57. package/dist/execution/SafeExecutor.js +207 -67
  58. package/dist/execution/SafeExecutor.js.map +1 -1
  59. package/dist/testing/LocalAuthority.d.ts +17 -2
  60. package/dist/testing/LocalAuthority.d.ts.map +1 -1
  61. package/dist/testing/LocalAuthority.js +38 -1
  62. package/dist/testing/LocalAuthority.js.map +1 -1
  63. package/package.json +3 -3
@@ -0,0 +1,87 @@
1
+ import { type BundleValidity } from "./BundleWindow.js";
2
+ import type { EdgeBundleSource } from "./EdgeBundleSource.js";
3
+ import type { EdgeModule } from "./EdgeModule.js";
4
+ /** A bundle the module verified and holds under `handle`. */
5
+ export interface LoadedBundle extends BundleValidity {
6
+ readonly handle: number;
7
+ readonly bundleId: string;
8
+ readonly policyVersion: string;
9
+ readonly kid: string;
10
+ readonly expiresAt: string;
11
+ }
12
+ /**
13
+ * What the manager reports. Identifiers, codes and counts only: no URL, no
14
+ * header, no response body and no credential can reach a field here.
15
+ */
16
+ export type EdgeBundleEvent = {
17
+ readonly event: "EDGE_BUNDLE_LOADED";
18
+ readonly bundle_id: string;
19
+ readonly policy_version: string;
20
+ readonly kid: string;
21
+ readonly expires_at: string;
22
+ } | {
23
+ readonly event: "EDGE_BUNDLE_REFRESH_FAILED";
24
+ readonly code: string;
25
+ readonly failures: number;
26
+ readonly retry_ms: number;
27
+ } | {
28
+ readonly event: "EDGE_BUNDLE_EXPIRED";
29
+ readonly bundle_id: string;
30
+ readonly policy_version: string;
31
+ };
32
+ export interface EdgeBundleManagerOptions {
33
+ readonly module: EdgeModule;
34
+ readonly source: EdgeBundleSource;
35
+ /** The organisation the bundle must be issued to; the module checks it against `aud`. */
36
+ readonly orgId: string;
37
+ /** The routine refresh interval. One hour by default. */
38
+ readonly refreshSeconds?: number;
39
+ /** Epoch milliseconds; the only time the module is ever given. */
40
+ readonly clock?: () => number;
41
+ readonly onEvent?: (event: EdgeBundleEvent) => void;
42
+ readonly retryBaseMs?: number;
43
+ readonly retryMaxMs?: number;
44
+ readonly minimumMs?: number;
45
+ }
46
+ /**
47
+ * Keeps one verified bundle loaded in the module and the next one coming.
48
+ *
49
+ * The loaded bundle is replaced only by one the module has verified: the new
50
+ * handle becomes current in one assignment and the old handle is released
51
+ * after it, so a decision always runs on exactly one of the two. A failed
52
+ * fetch or a refused bundle leaves the last good bundle in place until its
53
+ * own expiry, and is retried with backoff. Past its expiry a bundle is
54
+ * released and nothing is loaded: there is no grace period, because a bundle
55
+ * past `exp` is one Decionis no longer stands behind.
56
+ */
57
+ export declare class EdgeBundleManager {
58
+ private readonly options;
59
+ private loaded;
60
+ private failures;
61
+ private timer;
62
+ private inflight;
63
+ private stopped;
64
+ private readonly clock;
65
+ private readonly policy;
66
+ constructor(options: EdgeBundleManagerOptions);
67
+ /**
68
+ * The bundle a decision may run on at `now`, or null. A bundle past its
69
+ * expiry is released here, so the moment it lapses is the moment it stops
70
+ * being used, whatever the refresh schedule is doing.
71
+ */
72
+ current(now: number): LoadedBundle | null;
73
+ /** Fetches and loads once now; the first call of `start`, and what a test drives. */
74
+ refresh(): Promise<string | null>;
75
+ /** The first refresh, awaited, then the schedule. */
76
+ start(): Promise<void>;
77
+ /** Stops the schedule and releases the loaded bundle. */
78
+ stop(): void;
79
+ private tick;
80
+ /** One fetch and load. Null when the loaded bundle is current; otherwise the failure's code. */
81
+ private attempt;
82
+ private failed;
83
+ private emit;
84
+ /** The module's refusal as a log code: its own identifier, upper-cased, and nothing else of it. */
85
+ private static refusal;
86
+ }
87
+ //# sourceMappingURL=EdgeBundleManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeBundleManager.d.ts","sourceRoot":"","sources":["../../../src/decision/edge/EdgeBundleManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAAsB,MAAM,mBAAmB,CAAC;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,4BAA4B,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,CAAC;AAEN,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAeD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;IAST,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR3C,OAAO,CAAC,MAAM,CAAmE;IACjF,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAEH,OAAO,EAAE,wBAAwB;IAWrE;;;;OAIG;IACI,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgBhD,qFAAqF;IACxE,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO9C,qDAAqD;IACxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnC,yDAAyD;IAClD,IAAI,IAAI,IAAI;YASL,IAAI;IAgBlB,gGAAgG;YAClF,OAAO;IAgDrB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,IAAI;IAQZ,mGAAmG;IACnG,OAAO,CAAC,MAAM,CAAC,OAAO;CAGvB"}
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ import { BundleWindow } from "./BundleWindow.js";
3
+ const DEFAULT_REFRESH_SECONDS = 3_600;
4
+ /** A file is looked at this often, whatever the routine interval: it changes when an operator says so. */
5
+ const FILE_POLL_MS = 10_000;
6
+ const LoadResultSchema = z.object({
7
+ handle: z.number().int().min(1).max(0xffffffff),
8
+ bundle_id: z.string().min(1).max(200),
9
+ policy_version: z.string().min(1).max(200),
10
+ kid: z.string().min(1).max(200),
11
+ not_before: z.string().datetime({ offset: true }),
12
+ expires_at: z.string().datetime({ offset: true }),
13
+ });
14
+ /**
15
+ * Keeps one verified bundle loaded in the module and the next one coming.
16
+ *
17
+ * The loaded bundle is replaced only by one the module has verified: the new
18
+ * handle becomes current in one assignment and the old handle is released
19
+ * after it, so a decision always runs on exactly one of the two. A failed
20
+ * fetch or a refused bundle leaves the last good bundle in place until its
21
+ * own expiry, and is retried with backoff. Past its expiry a bundle is
22
+ * released and nothing is loaded: there is no grace period, because a bundle
23
+ * past `exp` is one Decionis no longer stands behind.
24
+ */
25
+ export class EdgeBundleManager {
26
+ options;
27
+ loaded = null;
28
+ failures = 0;
29
+ timer = null;
30
+ inflight = null;
31
+ stopped = false;
32
+ clock;
33
+ policy;
34
+ constructor(options) {
35
+ this.options = options;
36
+ this.clock = options.clock ?? Date.now;
37
+ const refreshMs = (options.refreshSeconds ?? DEFAULT_REFRESH_SECONDS) * 1_000;
38
+ this.policy = {
39
+ refreshMs: options.source.kind === "file" ? Math.min(refreshMs, FILE_POLL_MS) : refreshMs,
40
+ retryBaseMs: options.retryBaseMs ?? 1_000,
41
+ retryMaxMs: options.retryMaxMs ?? 300_000,
42
+ minimumMs: options.minimumMs ?? 30_000,
43
+ };
44
+ }
45
+ /**
46
+ * The bundle a decision may run on at `now`, or null. A bundle past its
47
+ * expiry is released here, so the moment it lapses is the moment it stops
48
+ * being used, whatever the refresh schedule is doing.
49
+ */
50
+ current(now) {
51
+ const loaded = this.loaded;
52
+ if (loaded === null)
53
+ return null;
54
+ if (now >= loaded.expiresAtMs) {
55
+ this.loaded = null;
56
+ this.options.module.unloadBundle(loaded.handle);
57
+ this.emit({
58
+ event: "EDGE_BUNDLE_EXPIRED",
59
+ bundle_id: loaded.bundleId,
60
+ policy_version: loaded.policyVersion,
61
+ });
62
+ return null;
63
+ }
64
+ return BundleWindow.usable(loaded, now) ? loaded : null;
65
+ }
66
+ /** Fetches and loads once now; the first call of `start`, and what a test drives. */
67
+ async refresh() {
68
+ this.inflight ??= this.attempt().finally(() => {
69
+ this.inflight = null;
70
+ });
71
+ return await this.inflight;
72
+ }
73
+ /** The first refresh, awaited, then the schedule. */
74
+ async start() {
75
+ this.stopped = false;
76
+ await this.tick();
77
+ }
78
+ /** Stops the schedule and releases the loaded bundle. */
79
+ stop() {
80
+ this.stopped = true;
81
+ if (this.timer !== null)
82
+ clearTimeout(this.timer);
83
+ this.timer = null;
84
+ const loaded = this.loaded;
85
+ this.loaded = null;
86
+ if (loaded !== null)
87
+ this.options.module.unloadBundle(loaded.handle);
88
+ }
89
+ async tick() {
90
+ const failure = await this.refresh();
91
+ if (this.stopped)
92
+ return;
93
+ const delay = BundleWindow.nextDelay(this.policy, this.loaded, this.failures, this.clock());
94
+ if (failure !== null) {
95
+ this.emit({
96
+ event: "EDGE_BUNDLE_REFRESH_FAILED",
97
+ code: failure,
98
+ failures: this.failures,
99
+ retry_ms: delay,
100
+ });
101
+ }
102
+ this.timer = setTimeout(() => void this.tick(), delay);
103
+ this.timer.unref();
104
+ }
105
+ /** One fetch and load. Null when the loaded bundle is current; otherwise the failure's code. */
106
+ async attempt() {
107
+ const read = await this.options.source.read();
108
+ if (!read.ok)
109
+ return this.failed(read.code);
110
+ if (this.loaded?.signedBundle === read.signedBundle) {
111
+ this.failures = 0;
112
+ return null;
113
+ }
114
+ let envelope;
115
+ try {
116
+ envelope = this.options.module.loadBundle({
117
+ signed_bundle: read.signedBundle,
118
+ org_id: this.options.orgId,
119
+ now: new Date(this.clock()).toISOString(),
120
+ });
121
+ }
122
+ catch {
123
+ return this.failed("EDGE_MODULE_FAULTED");
124
+ }
125
+ if (!envelope.ok)
126
+ return this.failed(EdgeBundleManager.refusal(envelope.code));
127
+ const parsed = LoadResultSchema.safeParse(envelope.result);
128
+ if (!parsed.success) {
129
+ const handle = envelope.result?.handle;
130
+ if (typeof handle === "number")
131
+ this.options.module.unloadBundle(handle);
132
+ return this.failed("BUNDLE_LOAD_RESULT_INVALID");
133
+ }
134
+ const result = parsed.data;
135
+ const previous = this.loaded;
136
+ this.loaded = {
137
+ handle: result.handle,
138
+ bundleId: result.bundle_id,
139
+ policyVersion: result.policy_version,
140
+ kid: result.kid,
141
+ notBeforeMs: Date.parse(result.not_before),
142
+ expiresAtMs: Date.parse(result.expires_at),
143
+ expiresAt: new Date(Date.parse(result.expires_at)).toISOString(),
144
+ signedBundle: read.signedBundle,
145
+ };
146
+ if (previous !== null)
147
+ this.options.module.unloadBundle(previous.handle);
148
+ this.failures = 0;
149
+ this.emit({
150
+ event: "EDGE_BUNDLE_LOADED",
151
+ bundle_id: result.bundle_id,
152
+ policy_version: result.policy_version,
153
+ kid: result.kid,
154
+ expires_at: this.loaded.expiresAt,
155
+ });
156
+ return null;
157
+ }
158
+ failed(code) {
159
+ this.failures += 1;
160
+ return code;
161
+ }
162
+ emit(event) {
163
+ try {
164
+ this.options.onEvent?.(event);
165
+ }
166
+ catch {
167
+ // Reporting never changes which bundle is loaded.
168
+ }
169
+ }
170
+ /** The module's refusal as a log code: its own identifier, upper-cased, and nothing else of it. */
171
+ static refusal(code) {
172
+ return /^[a-z][a-z0-9_]{0,62}$/.test(code) ? code.toUpperCase() : "BUNDLE_REFUSED";
173
+ }
174
+ }
175
+ //# sourceMappingURL=EdgeBundleManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeBundleManager.js","sourceRoot":"","sources":["../../../src/decision/edge/EdgeBundleManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type ClientSource } from "../../http/ClientIdentification.js";
2
+ import { type Credential } from "../../http/Credential.js";
3
+ /**
4
+ * What a source produced: a signed bundle as a compact JWS, or a code saying
5
+ * why not. A code is an identifier chosen here; no status text, header,
6
+ * body or credential from the attempt is carried, so it is safe to log.
7
+ */
8
+ export type BundleRead = {
9
+ readonly ok: true;
10
+ readonly signedBundle: string;
11
+ } | {
12
+ readonly ok: false;
13
+ readonly code: string;
14
+ };
15
+ /** Where signed policy bundles come from. */
16
+ export interface EdgeBundleSource {
17
+ /** `url` fetches from Decionis; `file` reads what an operator placed on disk. */
18
+ readonly kind: "url" | "file";
19
+ read(): Promise<BundleRead>;
20
+ }
21
+ /**
22
+ * Takes the signed bundle out of what a source returned: the issuance
23
+ * response (`{"signed_bundle": …}`), or the compact JWS on its own, which is
24
+ * what an operator copying a bundle by hand will most likely place.
25
+ */
26
+ export declare function signedBundleOf(text: string): BundleRead;
27
+ export interface UrlBundleSourceOptions {
28
+ /** The Decionis API base URL, the same one the hosted gate calls. */
29
+ readonly baseUrl: string;
30
+ /** The organisation's API key (`policy:read`), read at the moment of each request. */
31
+ readonly apiKey: Credential;
32
+ readonly timeoutMs?: number;
33
+ readonly fetch?: typeof fetch;
34
+ readonly allowInsecureLoopback?: boolean;
35
+ readonly source?: ClientSource;
36
+ }
37
+ /** `GET /v1/edge/policy-bundles/current`, authenticated with the organisation's API key. */
38
+ export declare class UrlBundleSource implements EdgeBundleSource {
39
+ readonly kind = "url";
40
+ private readonly url;
41
+ private readonly apiKey;
42
+ private readonly timeoutMs;
43
+ private readonly fetchImpl;
44
+ private readonly userAgent;
45
+ constructor(options: UrlBundleSourceOptions);
46
+ read(): Promise<BundleRead>;
47
+ }
48
+ /**
49
+ * A file an operator replaces by hand, for a deployment with no route to
50
+ * Decionis. It is read on every refresh; the manager loads it only when it
51
+ * holds a bundle other than the one already loaded.
52
+ */
53
+ export declare class FileBundleSource implements EdgeBundleSource {
54
+ private readonly path;
55
+ readonly kind = "file";
56
+ constructor(path: string);
57
+ read(): Promise<BundleRead>;
58
+ }
59
+ //# sourceMappingURL=EdgeBundleSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeBundleSource.d.ts","sourceRoot":"","sources":["../../../src/decision/edge/EdgeBundleSource.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE7E;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IAC9B,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7B;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAavD;AAED,MAAM,WAAW,sBAAsB;IACrC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,4FAA4F;AAC5F,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,SAAgB,IAAI,SAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEhB,OAAO,EAAE,sBAAsB;IAYrC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;CAyBzC;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,gBAAgB;IAGpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFxC,SAAgB,IAAI,UAAU;gBAEM,IAAI,EAAE,MAAM;IAEnC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;CAWzC"}
@@ -0,0 +1,99 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { AuthorityBaseUrl } from "../../http/AuthorityBaseUrl.js";
3
+ import { BoundedResponseBody } from "../../http/BoundedResponseBody.js";
4
+ import { userAgent } from "../../http/ClientIdentification.js";
5
+ import { credentialReader } from "../../http/Credential.js";
6
+ /** Three non-empty base64url segments: the only shape `load_bundle` accepts. */
7
+ const COMPACT_JWS = /^[\w-]+\.[\w-]+\.[\w-]+$/;
8
+ /** `load_bundle` refuses inputs over 4 MiB; a bundle near that is refused here first. */
9
+ const MAX_BUNDLE_BYTES = 4 * 1024 * 1024;
10
+ /**
11
+ * Takes the signed bundle out of what a source returned: the issuance
12
+ * response (`{"signed_bundle": …}`), or the compact JWS on its own, which is
13
+ * what an operator copying a bundle by hand will most likely place.
14
+ */
15
+ export function signedBundleOf(text) {
16
+ const trimmed = text.trim();
17
+ let candidate = trimmed;
18
+ if (trimmed.startsWith("{")) {
19
+ try {
20
+ candidate = JSON.parse(trimmed).signed_bundle;
21
+ }
22
+ catch {
23
+ return { ok: false, code: "BUNDLE_RESPONSE_INVALID" };
24
+ }
25
+ }
26
+ return typeof candidate === "string" && COMPACT_JWS.test(candidate)
27
+ ? { ok: true, signedBundle: candidate }
28
+ : { ok: false, code: "BUNDLE_RESPONSE_INVALID" };
29
+ }
30
+ /** `GET /v1/edge/policy-bundles/current`, authenticated with the organisation's API key. */
31
+ export class UrlBundleSource {
32
+ kind = "url";
33
+ url;
34
+ apiKey;
35
+ timeoutMs;
36
+ fetchImpl;
37
+ userAgent;
38
+ constructor(options) {
39
+ const base = AuthorityBaseUrl.normalize(options.baseUrl, options.allowInsecureLoopback === true);
40
+ this.url = `${base}/v1/edge/policy-bundles/current`;
41
+ this.apiKey = credentialReader(options.apiKey);
42
+ this.timeoutMs = Math.min(Math.max(options.timeoutMs ?? 10_000, 1), 30_000);
43
+ this.fetchImpl = options.fetch ?? fetch;
44
+ this.userAgent = userAgent(options.source);
45
+ }
46
+ async read() {
47
+ const controller = new AbortController();
48
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
49
+ try {
50
+ const response = await this.fetchImpl(this.url, {
51
+ method: "GET",
52
+ headers: {
53
+ authorization: `Bearer ${this.apiKey()}`,
54
+ accept: "application/json",
55
+ "user-agent": this.userAgent,
56
+ },
57
+ signal: controller.signal,
58
+ });
59
+ if (!response.ok) {
60
+ await response.body?.cancel().catch(() => undefined);
61
+ return { ok: false, code: `BUNDLE_HTTP_${response.status}` };
62
+ }
63
+ const text = await BoundedResponseBody.read(response, MAX_BUNDLE_BYTES);
64
+ return text === null ? { ok: false, code: "BUNDLE_TOO_LARGE" } : signedBundleOf(text);
65
+ }
66
+ catch {
67
+ return { ok: false, code: "BUNDLE_FETCH_FAILED" };
68
+ }
69
+ finally {
70
+ clearTimeout(timeout);
71
+ }
72
+ }
73
+ }
74
+ /**
75
+ * A file an operator replaces by hand, for a deployment with no route to
76
+ * Decionis. It is read on every refresh; the manager loads it only when it
77
+ * holds a bundle other than the one already loaded.
78
+ */
79
+ export class FileBundleSource {
80
+ path;
81
+ kind = "file";
82
+ constructor(path) {
83
+ this.path = path;
84
+ }
85
+ async read() {
86
+ let text;
87
+ try {
88
+ const bytes = await readFile(this.path);
89
+ if (bytes.byteLength > MAX_BUNDLE_BYTES)
90
+ return { ok: false, code: "BUNDLE_TOO_LARGE" };
91
+ text = bytes.toString("utf8");
92
+ }
93
+ catch {
94
+ return { ok: false, code: "BUNDLE_FILE_UNREADABLE" };
95
+ }
96
+ return signedBundleOf(text);
97
+ }
98
+ }
99
+ //# sourceMappingURL=EdgeBundleSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeBundleSource.js","sourceRoot":"","sources":["../../../src/decision/edge/EdgeBundleSource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import type { LocalGrants } from "../../execution/LocalAuthorizationVerifier.js";
2
+ import type { ReplayStore } from "../../execution/ReplayStore.js";
3
+ import type { CapturedIntent } from "../../intent/ExecutionIntent.js";
4
+ import { type DecisionAuthority, type DecisionEvaluationMode, type DecisionEvaluationOptions, type DecisionEvidence, type DecisionVerdict, type GateDecision } from "../DecisionAuthority.js";
5
+ import type { EdgeBundleManager } from "./EdgeBundleManager.js";
6
+ import type { EdgeModule } from "./EdgeModule.js";
7
+ /** What happens when no local decision can be made: ask Decionis, or refuse. */
8
+ export type EdgeUnavailablePolicy = "hosted" | "block";
9
+ /**
10
+ * One line of local evidence. A decision line names the bundle it was made
11
+ * on and the digest of the evaluation; it carries the identifiers and the
12
+ * verdict the hosted record carries, and no parameter, amount, or value from
13
+ * the intent. These lines are what a deployment's edge usage is counted from.
14
+ */
15
+ export type EdgeDecisionRecord = {
16
+ readonly event: "EDGE_DECISION";
17
+ readonly mode: DecisionEvaluationMode;
18
+ readonly intent_id: string;
19
+ readonly intent_hash: string;
20
+ readonly decision_id: string;
21
+ readonly verdict: DecisionVerdict;
22
+ readonly reason_codes: readonly string[];
23
+ readonly policy_version: string;
24
+ readonly bundle_id: string;
25
+ readonly kid: string;
26
+ readonly jti: string;
27
+ readonly evaluation_digest: string;
28
+ /** True when the verdict sends the intent on to Decionis (an `ESCALATE` in enforcement). */
29
+ readonly delegated: boolean;
30
+ } | {
31
+ readonly event: "EDGE_UNAVAILABLE";
32
+ readonly mode: DecisionEvaluationMode;
33
+ readonly intent_id: string;
34
+ readonly intent_hash: string;
35
+ readonly reason: string;
36
+ /** The module's own refusal code, when it gave one. */
37
+ readonly module_code: string | null;
38
+ readonly fallback: "HOSTED" | "BLOCK";
39
+ };
40
+ export interface EdgeDecisionAuthorityOptions {
41
+ readonly module: EdgeModule;
42
+ readonly bundles: EdgeBundleManager;
43
+ /**
44
+ * The hosted authority: Decionis through `enforce-and-bind`. An `ESCALATE`
45
+ * goes to it, so Presence and attestation flows run as they always have,
46
+ * and so does an intent the edge cannot decide when `onUnavailable` is
47
+ * `hosted`.
48
+ */
49
+ readonly hosted: DecisionAuthority;
50
+ /** Where an enforcement `ALLOW` is held for the executor to consume once. */
51
+ readonly grants: LocalGrants;
52
+ /**
53
+ * The single-use store the local verifier claims intents in. When given,
54
+ * an enforcement decision about an intent that has already run (on this
55
+ * replica or, with a shared store, on any) is refused before it is made.
56
+ */
57
+ readonly replay?: ReplayStore;
58
+ readonly mode?: DecisionEvaluationMode;
59
+ /** `hosted` (the default) or `block`. Neither ever allows without a valid bundle. */
60
+ readonly onUnavailable?: EdgeUnavailablePolicy;
61
+ readonly clock?: () => number;
62
+ /** The executor's evidence stream. A decision that cannot be recorded is not made. */
63
+ readonly record?: (record: EdgeDecisionRecord) => void;
64
+ }
65
+ /**
66
+ * A `DecisionAuthority` that decides inside the deployment, with the Decionis
67
+ * edge evaluator: the same verdict hosted `enforce-and-bind` gives for the
68
+ * same binding and bundle, with no network call on the way.
69
+ *
70
+ * - `ALLOW` in enforcement becomes a local authorization, held in
71
+ * `LocalGrants` and consumed once by `LocalAuthorizationVerifier`; there is
72
+ * no Decionis grant on this path and nothing to claim.
73
+ * - `BLOCK` is a refusal, as it is hosted.
74
+ * - `ESCALATE` in enforcement hands the same intent, with the same escalation
75
+ * options, to the hosted authority: the human-approval paths are hosted.
76
+ * - Approval evidence is only ever evaluated hosted.
77
+ * - No usable bundle, a module fault, or a result this host cannot read
78
+ * follows `onUnavailable`: the hosted authority, or a fail-closed `BLOCK`.
79
+ *
80
+ * This class is the open host. The evaluator is a separately licensed
81
+ * module this package does not contain; without it the executor runs on the
82
+ * hosted authority alone, exactly as it does without this class.
83
+ */
84
+ export declare class EdgeDecisionAuthority implements DecisionAuthority {
85
+ private readonly options;
86
+ readonly evaluationMode: DecisionEvaluationMode;
87
+ private readonly onUnavailable;
88
+ private readonly clock;
89
+ constructor(options: EdgeDecisionAuthorityOptions);
90
+ evaluate(captured: CapturedIntent, evidence?: DecisionEvidence, options?: DecisionEvaluationOptions): Promise<GateDecision>;
91
+ private local;
92
+ private unavailable;
93
+ /**
94
+ * Why an enforcement decision must not be made: the intent has already
95
+ * been claimed, or the store that would say so cannot be asked. Neither is
96
+ * a decision, so neither is recorded or counted.
97
+ */
98
+ private replayed;
99
+ /** The module must have decided on the bundle this host gave it. */
100
+ private static sameBundle;
101
+ /** Unique per intent and evaluation, and derivable from the record that names it. */
102
+ private static decisionId;
103
+ }
104
+ //# sourceMappingURL=EdgeDecisionAuthority.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeDecisionAuthority.d.ts","sourceRoot":"","sources":["../../../src/decision/edge/EdgeDecisionAuthority.ts"],"names":[],"mappings":"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"}