@kya-os/checkpoint-wasm-runtime 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +215 -0
- package/dist/engine-edge.d.mts +52 -16
- package/dist/engine-edge.d.ts +52 -16
- package/dist/engine-edge.js +11 -4
- package/dist/engine-edge.mjs +11 -4
- package/dist/index.d.mts +118 -1
- package/dist/index.d.ts +118 -1
- package/dist/orchestrator-edge.js +46 -13
- package/dist/orchestrator-edge.mjs +46 -13
- package/dist/orchestrator-node.js +35 -9
- package/dist/orchestrator-node.mjs +35 -9
- package/dist/orchestrator.d.mts +52 -16
- package/dist/orchestrator.d.ts +52 -16
- package/dist/orchestrator.js +46 -13
- package/dist/orchestrator.mjs +46 -13
- package/dist/policy.d.mts +148 -0
- package/dist/policy.d.ts +148 -0
- package/dist/policy.js +52 -0
- package/dist/policy.mjs +53 -0
- package/dist/reporter.d.mts +102 -0
- package/dist/reporter.d.ts +102 -0
- package/dist/reporter.js +125 -0
- package/dist/reporter.mjs +122 -0
- package/package.json +15 -5
- package/wasm/kya-os-engine/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine/package.json +4 -2
- package/wasm/kya-os-engine-bundler/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine-cedar/README.md +26 -0
- package/wasm/kya-os-engine-cedar/kya_os_engine.d.ts +77 -0
- package/wasm/kya-os-engine-cedar/kya_os_engine.js +636 -0
- package/wasm/kya-os-engine-cedar/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine-cedar/kya_os_engine_bg.wasm.d.ts +11 -0
- package/wasm/kya-os-engine-cedar/package.json +29 -0
- package/wasm/kya-os-engine-cedar-web/README.md +26 -0
- package/wasm/kya-os-engine-cedar-web/kya_os_engine.d.ts +117 -0
- package/wasm/kya-os-engine-cedar-web/kya_os_engine.js +694 -0
- package/wasm/kya-os-engine-cedar-web/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine-cedar-web/kya_os_engine_bg.wasm.d.ts +11 -0
- package/wasm/kya-os-engine-cedar-web/package.json +31 -0
- package/wasm/kya-os-engine-web/kya_os_engine_bg.wasm +0 -0
- package/wasm/kya-os-engine-web/package.json +5 -3
- package/wasm/agentshield_wasm.d.ts +0 -485
- package/wasm/agentshield_wasm.js +0 -1551
- package/wasm/agentshield_wasm_bg.wasm +0 -0
- package/wasm/agentshield_wasm_bg.wasm.d.ts +0 -97
package/dist/orchestrator.js
CHANGED
|
@@ -173,21 +173,30 @@ function parseBodyAsObject(body) {
|
|
|
173
173
|
function tryBuildMcpIFromLegacyHeader(req) {
|
|
174
174
|
const header = getHeader(req, "kya-delegation");
|
|
175
175
|
if (!header) return null;
|
|
176
|
-
let parsed;
|
|
176
|
+
let parsed = void 0;
|
|
177
177
|
try {
|
|
178
178
|
parsed = JSON.parse(header);
|
|
179
179
|
} catch {
|
|
180
|
-
return null;
|
|
181
180
|
}
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
181
|
+
if (parsed && typeof parsed === "object") {
|
|
182
|
+
const obj = parsed;
|
|
183
|
+
const protectedSeg = obj.protected;
|
|
184
|
+
const payloadSeg = obj.payload;
|
|
185
|
+
const signatureSeg = obj.signature;
|
|
186
|
+
if (typeof protectedSeg === "string" && typeof payloadSeg === "string" && typeof signatureSeg === "string") {
|
|
187
|
+
const compact = `${protectedSeg}.${payloadSeg}.${signatureSeg}`;
|
|
188
|
+
return buildMcpIRequestFromCompact(compact);
|
|
189
|
+
}
|
|
188
190
|
return null;
|
|
189
191
|
}
|
|
190
|
-
const
|
|
192
|
+
const trimmed = header.trim();
|
|
193
|
+
if (COMPACT_JWS_PATTERN.test(trimmed)) {
|
|
194
|
+
return buildMcpIRequestFromCompact(trimmed);
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
var COMPACT_JWS_PATTERN = /^[A-Za-z0-9_-]+=*\.[A-Za-z0-9_-]+=*\.[A-Za-z0-9_-]+=*$/;
|
|
199
|
+
function buildMcpIRequestFromCompact(compact) {
|
|
191
200
|
const raw = Array.from(Buffer.from(compact, "utf8"));
|
|
192
201
|
const payload = parseJwsPayloadStruct(raw);
|
|
193
202
|
if (!payload) return null;
|
|
@@ -404,20 +413,27 @@ function defaultLogger(msg) {
|
|
|
404
413
|
|
|
405
414
|
// src/engine/edge.ts
|
|
406
415
|
var initialised = null;
|
|
407
|
-
|
|
408
|
-
|
|
416
|
+
var providedModule;
|
|
417
|
+
function initEngineEdge(wasmModule) {
|
|
418
|
+
if (wasmModule !== void 0 && providedModule === void 0) {
|
|
419
|
+
providedModule = wasmModule;
|
|
420
|
+
}
|
|
421
|
+
return ensureReady().then(() => void 0);
|
|
409
422
|
}
|
|
410
|
-
function ensureReady(
|
|
423
|
+
function ensureReady() {
|
|
411
424
|
if (initialised) return initialised;
|
|
412
425
|
const pending = (async () => {
|
|
413
426
|
const mod = await import('@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-web/kya_os_engine.js');
|
|
414
|
-
await mod.default(
|
|
427
|
+
await mod.default(
|
|
428
|
+
providedModule !== void 0 ? { module_or_path: providedModule } : void 0
|
|
429
|
+
);
|
|
415
430
|
return mod.verify;
|
|
416
431
|
})();
|
|
417
432
|
initialised = pending;
|
|
418
433
|
pending.catch(() => {
|
|
419
434
|
if (initialised === pending) {
|
|
420
435
|
initialised = null;
|
|
436
|
+
providedModule = void 0;
|
|
421
437
|
}
|
|
422
438
|
});
|
|
423
439
|
return initialised;
|
|
@@ -512,6 +528,23 @@ function buildBaseHeaders(result) {
|
|
|
512
528
|
if (result.engineInfo.rulesetHash) {
|
|
513
529
|
headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
|
|
514
530
|
}
|
|
531
|
+
const meta = result.detectionDetail.metadata;
|
|
532
|
+
if (meta) {
|
|
533
|
+
const verifiedTier = meta.verified_tier;
|
|
534
|
+
if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
|
|
535
|
+
headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
|
|
536
|
+
} else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
|
|
537
|
+
headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
|
|
538
|
+
}
|
|
539
|
+
const verifiedVendor = meta.verified_vendor;
|
|
540
|
+
if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
|
|
541
|
+
headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
|
|
542
|
+
}
|
|
543
|
+
const verifiedProtocol = meta.verified_protocol;
|
|
544
|
+
if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
|
|
545
|
+
headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
515
548
|
return headers;
|
|
516
549
|
}
|
|
517
550
|
function httpStatusForBlockReason(reason) {
|
package/dist/orchestrator.mjs
CHANGED
|
@@ -171,21 +171,30 @@ function parseBodyAsObject(body) {
|
|
|
171
171
|
function tryBuildMcpIFromLegacyHeader(req) {
|
|
172
172
|
const header = getHeader(req, "kya-delegation");
|
|
173
173
|
if (!header) return null;
|
|
174
|
-
let parsed;
|
|
174
|
+
let parsed = void 0;
|
|
175
175
|
try {
|
|
176
176
|
parsed = JSON.parse(header);
|
|
177
177
|
} catch {
|
|
178
|
-
return null;
|
|
179
178
|
}
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
if (parsed && typeof parsed === "object") {
|
|
180
|
+
const obj = parsed;
|
|
181
|
+
const protectedSeg = obj.protected;
|
|
182
|
+
const payloadSeg = obj.payload;
|
|
183
|
+
const signatureSeg = obj.signature;
|
|
184
|
+
if (typeof protectedSeg === "string" && typeof payloadSeg === "string" && typeof signatureSeg === "string") {
|
|
185
|
+
const compact = `${protectedSeg}.${payloadSeg}.${signatureSeg}`;
|
|
186
|
+
return buildMcpIRequestFromCompact(compact);
|
|
187
|
+
}
|
|
186
188
|
return null;
|
|
187
189
|
}
|
|
188
|
-
const
|
|
190
|
+
const trimmed = header.trim();
|
|
191
|
+
if (COMPACT_JWS_PATTERN.test(trimmed)) {
|
|
192
|
+
return buildMcpIRequestFromCompact(trimmed);
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
var COMPACT_JWS_PATTERN = /^[A-Za-z0-9_-]+=*\.[A-Za-z0-9_-]+=*\.[A-Za-z0-9_-]+=*$/;
|
|
197
|
+
function buildMcpIRequestFromCompact(compact) {
|
|
189
198
|
const raw = Array.from(Buffer.from(compact, "utf8"));
|
|
190
199
|
const payload = parseJwsPayloadStruct(raw);
|
|
191
200
|
if (!payload) return null;
|
|
@@ -402,20 +411,27 @@ function defaultLogger(msg) {
|
|
|
402
411
|
|
|
403
412
|
// src/engine/edge.ts
|
|
404
413
|
var initialised = null;
|
|
405
|
-
|
|
406
|
-
|
|
414
|
+
var providedModule;
|
|
415
|
+
function initEngineEdge(wasmModule) {
|
|
416
|
+
if (wasmModule !== void 0 && providedModule === void 0) {
|
|
417
|
+
providedModule = wasmModule;
|
|
418
|
+
}
|
|
419
|
+
return ensureReady().then(() => void 0);
|
|
407
420
|
}
|
|
408
|
-
function ensureReady(
|
|
421
|
+
function ensureReady() {
|
|
409
422
|
if (initialised) return initialised;
|
|
410
423
|
const pending = (async () => {
|
|
411
424
|
const mod = await import('@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-web/kya_os_engine.js');
|
|
412
|
-
await mod.default(
|
|
425
|
+
await mod.default(
|
|
426
|
+
providedModule !== void 0 ? { module_or_path: providedModule } : void 0
|
|
427
|
+
);
|
|
413
428
|
return mod.verify;
|
|
414
429
|
})();
|
|
415
430
|
initialised = pending;
|
|
416
431
|
pending.catch(() => {
|
|
417
432
|
if (initialised === pending) {
|
|
418
433
|
initialised = null;
|
|
434
|
+
providedModule = void 0;
|
|
419
435
|
}
|
|
420
436
|
});
|
|
421
437
|
return initialised;
|
|
@@ -510,6 +526,23 @@ function buildBaseHeaders(result) {
|
|
|
510
526
|
if (result.engineInfo.rulesetHash) {
|
|
511
527
|
headers["X-Checkpoint-Ruleset-Hash"] = result.engineInfo.rulesetHash;
|
|
512
528
|
}
|
|
529
|
+
const meta = result.detectionDetail.metadata;
|
|
530
|
+
if (meta) {
|
|
531
|
+
const verifiedTier = meta.verified_tier;
|
|
532
|
+
if (typeof verifiedTier === "number" && Number.isFinite(verifiedTier)) {
|
|
533
|
+
headers["X-Checkpoint-Verified-Tier"] = String(verifiedTier);
|
|
534
|
+
} else if (typeof verifiedTier === "string" && verifiedTier.length > 0) {
|
|
535
|
+
headers["X-Checkpoint-Verified-Tier"] = verifiedTier;
|
|
536
|
+
}
|
|
537
|
+
const verifiedVendor = meta.verified_vendor;
|
|
538
|
+
if (typeof verifiedVendor === "string" && verifiedVendor.length > 0) {
|
|
539
|
+
headers["X-Checkpoint-Verified-Vendor"] = verifiedVendor;
|
|
540
|
+
}
|
|
541
|
+
const verifiedProtocol = meta.verified_protocol;
|
|
542
|
+
if (typeof verifiedProtocol === "string" && verifiedProtocol.length > 0) {
|
|
543
|
+
headers["X-Checkpoint-Verified-Protocol"] = verifiedProtocol;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
513
546
|
return headers;
|
|
514
547
|
}
|
|
515
548
|
function httpStatusForBlockReason(reason) {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { D as Decision } from './types-C3RniIOM.mjs';
|
|
2
|
+
export { B as BlockReason } from './types-C3RniIOM.mjs';
|
|
3
|
+
import '@kya-os/checkpoint-shared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cedar policy-evaluator bridge — Node fallback.
|
|
7
|
+
*
|
|
8
|
+
* Surfaces the `PolicyEvaluator` class exported by the cedar-enabled
|
|
9
|
+
* `kya-os-engine` WASM artifact (built by
|
|
10
|
+
* `rust/scripts/build-engine-cedar-wasm.sh`, gated on the Rust
|
|
11
|
+
* `wasm,cedar` features). A JS host (the gateway) compiles a tenant
|
|
12
|
+
* policy bundle once and authorizes many requests against it in-process,
|
|
13
|
+
* with no round-trip to a separate policy service.
|
|
14
|
+
*
|
|
15
|
+
* The cedar artifact is a SEPARATE, larger binary than the lean
|
|
16
|
+
* detection artifact — it pulls in `cedar-policy`. Detection-only
|
|
17
|
+
* consumers load `./engine` (the lean `verify()` glue); only callers that
|
|
18
|
+
* need in-process authorization reach for this bridge.
|
|
19
|
+
*
|
|
20
|
+
* ## Lazy loading
|
|
21
|
+
*
|
|
22
|
+
* The cedar `--target nodejs` glue instantiates the ~2 MB cedar WASM the
|
|
23
|
+
* moment it is `require`d (it `fs.readFileSync`s its `.wasm` sibling at
|
|
24
|
+
* module load). To keep the separate-artifact promise — detection-only
|
|
25
|
+
* consumers must NOT pay cedar's memory/startup — the glue is loaded
|
|
26
|
+
* LAZILY: nothing happens on `import`; the artifact is `require`d (and
|
|
27
|
+
* memoised) only on the first {@link createPolicyEvaluator} call. This is
|
|
28
|
+
* also why cedar lives behind the dedicated `./policy` subpath and is NOT
|
|
29
|
+
* re-exported from the `./node` barrel.
|
|
30
|
+
*
|
|
31
|
+
* `createRequire(__filename)` resolves the glue at call time in both
|
|
32
|
+
* builds — native `require` under CJS, the tsup `shims` / `createRequire`
|
|
33
|
+
* banner under ESM. The glue stays external (regex
|
|
34
|
+
* `wasm/kya-os-engine-cedar/` in tsup's `commonOpts.external`), so its
|
|
35
|
+
* `__dirname` resolves against `node_modules/.../wasm/kya-os-engine-cedar/`
|
|
36
|
+
* where the `.wasm` lives, and `"type": "commonjs"` on the artifact's
|
|
37
|
+
* package.json keeps Node from throwing `ERR_REQUIRE_ESM`.
|
|
38
|
+
*
|
|
39
|
+
* Direct .wasm path (for callers building their own loaders):
|
|
40
|
+
* `@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-cedar/kya_os_engine_bg.wasm`
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Owned, plain-data request the host hands to
|
|
45
|
+
* {@link PolicyEvaluatorRuntime.authorize}. Mirrors the Rust
|
|
46
|
+
* `AuthorizeInput` (`rust/crates/kya-os-engine/src/policy/authorize.rs`);
|
|
47
|
+
* keys are camelCase across the wasm-bindgen boundary, matching every
|
|
48
|
+
* other engine wire type.
|
|
49
|
+
*
|
|
50
|
+
* The evaluator is **synchronous over pre-resolved facts** (H-1 Kickoff
|
|
51
|
+
* § 4.5): the host pre-fetches the agent's delegation chain, reputation,
|
|
52
|
+
* etc. and ships the results here — no JS callbacks cross the boundary.
|
|
53
|
+
*
|
|
54
|
+
* `agentDid` and `reputation` are optional (omit or pass `undefined` for
|
|
55
|
+
* an anonymous or reputation-less request); `grantedScopes` defaults to an
|
|
56
|
+
* empty list when omitted.
|
|
57
|
+
*/
|
|
58
|
+
interface AuthorizeInput {
|
|
59
|
+
/** Agent DID if identity has been established, otherwise omitted. */
|
|
60
|
+
agentDid?: string;
|
|
61
|
+
/** The action being requested (e.g., `"book_flight"`). */
|
|
62
|
+
action: string;
|
|
63
|
+
/** The resource being acted on (e.g., `"travel_api"`). */
|
|
64
|
+
resource: string;
|
|
65
|
+
/** Scopes granted by the agent's delegation chain. Defaults to `[]`. */
|
|
66
|
+
grantedScopes?: string[];
|
|
67
|
+
/** Agent's reputation score in `[0.0, 1.0]`, if computed. */
|
|
68
|
+
reputation?: number;
|
|
69
|
+
/** Tenant identifier. */
|
|
70
|
+
tenantId: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Structural type of the WASM `PolicyEvaluator` class the cedar glue
|
|
74
|
+
* exports. Pinned here so this bridge type-checks against the artifact's
|
|
75
|
+
* `.d.ts` (`constructor(policy_text: string)`, `authorize(input): any`,
|
|
76
|
+
* `free()`).
|
|
77
|
+
*/
|
|
78
|
+
interface WasmPolicyEvaluator {
|
|
79
|
+
authorize(input: AuthorizeInput): unknown;
|
|
80
|
+
free(): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Host-facing wrapper around one compiled Cedar policy bundle.
|
|
84
|
+
*
|
|
85
|
+
* Construction compiles the bundle once; every {@link authorize} call
|
|
86
|
+
* evaluates a single owned request against it without re-parsing policy
|
|
87
|
+
* text (the compile-once / evaluate-many contract the engine upholds,
|
|
88
|
+
* carried across the boundary).
|
|
89
|
+
*/
|
|
90
|
+
declare class PolicyEvaluatorRuntime {
|
|
91
|
+
private readonly inner;
|
|
92
|
+
/**
|
|
93
|
+
* @param inner - The compiled WASM `PolicyEvaluator` handle. Built in
|
|
94
|
+
* {@link createPolicyEvaluator}; reused by every {@link authorize}
|
|
95
|
+
* call for the lifetime of this wrapper.
|
|
96
|
+
*/
|
|
97
|
+
constructor(inner: WasmPolicyEvaluator);
|
|
98
|
+
/**
|
|
99
|
+
* Authorize one owned request against the compiled policy bundle.
|
|
100
|
+
*
|
|
101
|
+
* The evaluator owns the fail-closed posture — a request it cannot
|
|
102
|
+
* marshal into Cedar, or one matching no `permit`, comes back as a
|
|
103
|
+
* {@link Decision} `Block` (not a thrown error). Authorization
|
|
104
|
+
* *verdicts* never throw; they surface inside the returned value.
|
|
105
|
+
*
|
|
106
|
+
* @throws {Error} only for boundary faults the WASM glue rejects: a
|
|
107
|
+
* malformed `input` that fails `AuthorizeInput` deserialisation, or a
|
|
108
|
+
* failure serialising the resulting `Decision`.
|
|
109
|
+
*/
|
|
110
|
+
authorize(input: AuthorizeInput): Decision;
|
|
111
|
+
/**
|
|
112
|
+
* Release the compiled bundle's WASM memory. Call when the evaluator is
|
|
113
|
+
* no longer needed; after this the wrapper must not be reused.
|
|
114
|
+
*/
|
|
115
|
+
free(): void;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compile a Cedar policy bundle into a reusable in-process evaluator.
|
|
119
|
+
*
|
|
120
|
+
* Mirrors `createNodeDetector`'s WASM loading: routes through the cedar
|
|
121
|
+
* `--target nodejs` glue, which self-loads its `.wasm` sibling via CJS
|
|
122
|
+
* `__dirname` + `require('fs')`. The returned {@link PolicyEvaluatorRuntime}
|
|
123
|
+
* holds one compiled bundle and authorizes many requests against it.
|
|
124
|
+
*
|
|
125
|
+
* @param policyText - The tenant's Cedar policy bundle as source text.
|
|
126
|
+
* @throws {Error} if `policyText` is not syntactically valid Cedar — a
|
|
127
|
+
* malformed bundle is an infrastructure fault surfaced here at
|
|
128
|
+
* construction, never as a per-request deny.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* import { createPolicyEvaluator } from '@kya-os/checkpoint-wasm-runtime/policy';
|
|
133
|
+
*
|
|
134
|
+
* const evaluator = createPolicyEvaluator('permit(principal, action, resource);');
|
|
135
|
+
* const decision = evaluator.authorize({
|
|
136
|
+
* action: 'book_flight',
|
|
137
|
+
* resource: 'travel_api',
|
|
138
|
+
* tenantId: 'tenant-a',
|
|
139
|
+
* grantedScopes: ['book_flight'],
|
|
140
|
+
* });
|
|
141
|
+
* if (decision.kind === 'Permit') {
|
|
142
|
+
* // proceed
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
declare function createPolicyEvaluator(policyText: string): PolicyEvaluatorRuntime;
|
|
147
|
+
|
|
148
|
+
export { type AuthorizeInput, Decision, PolicyEvaluatorRuntime, createPolicyEvaluator };
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { D as Decision } from './types-C3RniIOM.js';
|
|
2
|
+
export { B as BlockReason } from './types-C3RniIOM.js';
|
|
3
|
+
import '@kya-os/checkpoint-shared';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cedar policy-evaluator bridge — Node fallback.
|
|
7
|
+
*
|
|
8
|
+
* Surfaces the `PolicyEvaluator` class exported by the cedar-enabled
|
|
9
|
+
* `kya-os-engine` WASM artifact (built by
|
|
10
|
+
* `rust/scripts/build-engine-cedar-wasm.sh`, gated on the Rust
|
|
11
|
+
* `wasm,cedar` features). A JS host (the gateway) compiles a tenant
|
|
12
|
+
* policy bundle once and authorizes many requests against it in-process,
|
|
13
|
+
* with no round-trip to a separate policy service.
|
|
14
|
+
*
|
|
15
|
+
* The cedar artifact is a SEPARATE, larger binary than the lean
|
|
16
|
+
* detection artifact — it pulls in `cedar-policy`. Detection-only
|
|
17
|
+
* consumers load `./engine` (the lean `verify()` glue); only callers that
|
|
18
|
+
* need in-process authorization reach for this bridge.
|
|
19
|
+
*
|
|
20
|
+
* ## Lazy loading
|
|
21
|
+
*
|
|
22
|
+
* The cedar `--target nodejs` glue instantiates the ~2 MB cedar WASM the
|
|
23
|
+
* moment it is `require`d (it `fs.readFileSync`s its `.wasm` sibling at
|
|
24
|
+
* module load). To keep the separate-artifact promise — detection-only
|
|
25
|
+
* consumers must NOT pay cedar's memory/startup — the glue is loaded
|
|
26
|
+
* LAZILY: nothing happens on `import`; the artifact is `require`d (and
|
|
27
|
+
* memoised) only on the first {@link createPolicyEvaluator} call. This is
|
|
28
|
+
* also why cedar lives behind the dedicated `./policy` subpath and is NOT
|
|
29
|
+
* re-exported from the `./node` barrel.
|
|
30
|
+
*
|
|
31
|
+
* `createRequire(__filename)` resolves the glue at call time in both
|
|
32
|
+
* builds — native `require` under CJS, the tsup `shims` / `createRequire`
|
|
33
|
+
* banner under ESM. The glue stays external (regex
|
|
34
|
+
* `wasm/kya-os-engine-cedar/` in tsup's `commonOpts.external`), so its
|
|
35
|
+
* `__dirname` resolves against `node_modules/.../wasm/kya-os-engine-cedar/`
|
|
36
|
+
* where the `.wasm` lives, and `"type": "commonjs"` on the artifact's
|
|
37
|
+
* package.json keeps Node from throwing `ERR_REQUIRE_ESM`.
|
|
38
|
+
*
|
|
39
|
+
* Direct .wasm path (for callers building their own loaders):
|
|
40
|
+
* `@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-cedar/kya_os_engine_bg.wasm`
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Owned, plain-data request the host hands to
|
|
45
|
+
* {@link PolicyEvaluatorRuntime.authorize}. Mirrors the Rust
|
|
46
|
+
* `AuthorizeInput` (`rust/crates/kya-os-engine/src/policy/authorize.rs`);
|
|
47
|
+
* keys are camelCase across the wasm-bindgen boundary, matching every
|
|
48
|
+
* other engine wire type.
|
|
49
|
+
*
|
|
50
|
+
* The evaluator is **synchronous over pre-resolved facts** (H-1 Kickoff
|
|
51
|
+
* § 4.5): the host pre-fetches the agent's delegation chain, reputation,
|
|
52
|
+
* etc. and ships the results here — no JS callbacks cross the boundary.
|
|
53
|
+
*
|
|
54
|
+
* `agentDid` and `reputation` are optional (omit or pass `undefined` for
|
|
55
|
+
* an anonymous or reputation-less request); `grantedScopes` defaults to an
|
|
56
|
+
* empty list when omitted.
|
|
57
|
+
*/
|
|
58
|
+
interface AuthorizeInput {
|
|
59
|
+
/** Agent DID if identity has been established, otherwise omitted. */
|
|
60
|
+
agentDid?: string;
|
|
61
|
+
/** The action being requested (e.g., `"book_flight"`). */
|
|
62
|
+
action: string;
|
|
63
|
+
/** The resource being acted on (e.g., `"travel_api"`). */
|
|
64
|
+
resource: string;
|
|
65
|
+
/** Scopes granted by the agent's delegation chain. Defaults to `[]`. */
|
|
66
|
+
grantedScopes?: string[];
|
|
67
|
+
/** Agent's reputation score in `[0.0, 1.0]`, if computed. */
|
|
68
|
+
reputation?: number;
|
|
69
|
+
/** Tenant identifier. */
|
|
70
|
+
tenantId: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Structural type of the WASM `PolicyEvaluator` class the cedar glue
|
|
74
|
+
* exports. Pinned here so this bridge type-checks against the artifact's
|
|
75
|
+
* `.d.ts` (`constructor(policy_text: string)`, `authorize(input): any`,
|
|
76
|
+
* `free()`).
|
|
77
|
+
*/
|
|
78
|
+
interface WasmPolicyEvaluator {
|
|
79
|
+
authorize(input: AuthorizeInput): unknown;
|
|
80
|
+
free(): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Host-facing wrapper around one compiled Cedar policy bundle.
|
|
84
|
+
*
|
|
85
|
+
* Construction compiles the bundle once; every {@link authorize} call
|
|
86
|
+
* evaluates a single owned request against it without re-parsing policy
|
|
87
|
+
* text (the compile-once / evaluate-many contract the engine upholds,
|
|
88
|
+
* carried across the boundary).
|
|
89
|
+
*/
|
|
90
|
+
declare class PolicyEvaluatorRuntime {
|
|
91
|
+
private readonly inner;
|
|
92
|
+
/**
|
|
93
|
+
* @param inner - The compiled WASM `PolicyEvaluator` handle. Built in
|
|
94
|
+
* {@link createPolicyEvaluator}; reused by every {@link authorize}
|
|
95
|
+
* call for the lifetime of this wrapper.
|
|
96
|
+
*/
|
|
97
|
+
constructor(inner: WasmPolicyEvaluator);
|
|
98
|
+
/**
|
|
99
|
+
* Authorize one owned request against the compiled policy bundle.
|
|
100
|
+
*
|
|
101
|
+
* The evaluator owns the fail-closed posture — a request it cannot
|
|
102
|
+
* marshal into Cedar, or one matching no `permit`, comes back as a
|
|
103
|
+
* {@link Decision} `Block` (not a thrown error). Authorization
|
|
104
|
+
* *verdicts* never throw; they surface inside the returned value.
|
|
105
|
+
*
|
|
106
|
+
* @throws {Error} only for boundary faults the WASM glue rejects: a
|
|
107
|
+
* malformed `input` that fails `AuthorizeInput` deserialisation, or a
|
|
108
|
+
* failure serialising the resulting `Decision`.
|
|
109
|
+
*/
|
|
110
|
+
authorize(input: AuthorizeInput): Decision;
|
|
111
|
+
/**
|
|
112
|
+
* Release the compiled bundle's WASM memory. Call when the evaluator is
|
|
113
|
+
* no longer needed; after this the wrapper must not be reused.
|
|
114
|
+
*/
|
|
115
|
+
free(): void;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Compile a Cedar policy bundle into a reusable in-process evaluator.
|
|
119
|
+
*
|
|
120
|
+
* Mirrors `createNodeDetector`'s WASM loading: routes through the cedar
|
|
121
|
+
* `--target nodejs` glue, which self-loads its `.wasm` sibling via CJS
|
|
122
|
+
* `__dirname` + `require('fs')`. The returned {@link PolicyEvaluatorRuntime}
|
|
123
|
+
* holds one compiled bundle and authorizes many requests against it.
|
|
124
|
+
*
|
|
125
|
+
* @param policyText - The tenant's Cedar policy bundle as source text.
|
|
126
|
+
* @throws {Error} if `policyText` is not syntactically valid Cedar — a
|
|
127
|
+
* malformed bundle is an infrastructure fault surfaced here at
|
|
128
|
+
* construction, never as a per-request deny.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* import { createPolicyEvaluator } from '@kya-os/checkpoint-wasm-runtime/policy';
|
|
133
|
+
*
|
|
134
|
+
* const evaluator = createPolicyEvaluator('permit(principal, action, resource);');
|
|
135
|
+
* const decision = evaluator.authorize({
|
|
136
|
+
* action: 'book_flight',
|
|
137
|
+
* resource: 'travel_api',
|
|
138
|
+
* tenantId: 'tenant-a',
|
|
139
|
+
* grantedScopes: ['book_flight'],
|
|
140
|
+
* });
|
|
141
|
+
* if (decision.kind === 'Permit') {
|
|
142
|
+
* // proceed
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
declare function createPolicyEvaluator(policyText: string): PolicyEvaluatorRuntime;
|
|
147
|
+
|
|
148
|
+
export { type AuthorizeInput, Decision, PolicyEvaluatorRuntime, createPolicyEvaluator };
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var module$1 = require('module');
|
|
4
|
+
|
|
5
|
+
// src/policy-evaluator.ts
|
|
6
|
+
var cedarRequire = module$1.createRequire(__filename);
|
|
7
|
+
var cedarModule;
|
|
8
|
+
function loadCedarModule() {
|
|
9
|
+
cedarModule ??= cedarRequire(
|
|
10
|
+
"@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-cedar/kya_os_engine.js"
|
|
11
|
+
);
|
|
12
|
+
return cedarModule;
|
|
13
|
+
}
|
|
14
|
+
var PolicyEvaluatorRuntime = class {
|
|
15
|
+
/**
|
|
16
|
+
* @param inner - The compiled WASM `PolicyEvaluator` handle. Built in
|
|
17
|
+
* {@link createPolicyEvaluator}; reused by every {@link authorize}
|
|
18
|
+
* call for the lifetime of this wrapper.
|
|
19
|
+
*/
|
|
20
|
+
constructor(inner) {
|
|
21
|
+
this.inner = inner;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Authorize one owned request against the compiled policy bundle.
|
|
25
|
+
*
|
|
26
|
+
* The evaluator owns the fail-closed posture — a request it cannot
|
|
27
|
+
* marshal into Cedar, or one matching no `permit`, comes back as a
|
|
28
|
+
* {@link Decision} `Block` (not a thrown error). Authorization
|
|
29
|
+
* *verdicts* never throw; they surface inside the returned value.
|
|
30
|
+
*
|
|
31
|
+
* @throws {Error} only for boundary faults the WASM glue rejects: a
|
|
32
|
+
* malformed `input` that fails `AuthorizeInput` deserialisation, or a
|
|
33
|
+
* failure serialising the resulting `Decision`.
|
|
34
|
+
*/
|
|
35
|
+
authorize(input) {
|
|
36
|
+
return this.inner.authorize(input);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Release the compiled bundle's WASM memory. Call when the evaluator is
|
|
40
|
+
* no longer needed; after this the wrapper must not be reused.
|
|
41
|
+
*/
|
|
42
|
+
free() {
|
|
43
|
+
this.inner.free();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function createPolicyEvaluator(policyText) {
|
|
47
|
+
const { PolicyEvaluator } = loadCedarModule();
|
|
48
|
+
return new PolicyEvaluatorRuntime(new PolicyEvaluator(policyText));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
exports.PolicyEvaluatorRuntime = PolicyEvaluatorRuntime;
|
|
52
|
+
exports.createPolicyEvaluator = createPolicyEvaluator;
|
package/dist/policy.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
createRequire(import.meta.url);
|
|
6
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
7
|
+
var __filename$1 = /* @__PURE__ */ getFilename();
|
|
8
|
+
var cedarRequire = createRequire(__filename$1);
|
|
9
|
+
var cedarModule;
|
|
10
|
+
function loadCedarModule() {
|
|
11
|
+
cedarModule ??= cedarRequire(
|
|
12
|
+
"@kya-os/checkpoint-wasm-runtime/wasm/kya-os-engine-cedar/kya_os_engine.js"
|
|
13
|
+
);
|
|
14
|
+
return cedarModule;
|
|
15
|
+
}
|
|
16
|
+
var PolicyEvaluatorRuntime = class {
|
|
17
|
+
/**
|
|
18
|
+
* @param inner - The compiled WASM `PolicyEvaluator` handle. Built in
|
|
19
|
+
* {@link createPolicyEvaluator}; reused by every {@link authorize}
|
|
20
|
+
* call for the lifetime of this wrapper.
|
|
21
|
+
*/
|
|
22
|
+
constructor(inner) {
|
|
23
|
+
this.inner = inner;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Authorize one owned request against the compiled policy bundle.
|
|
27
|
+
*
|
|
28
|
+
* The evaluator owns the fail-closed posture — a request it cannot
|
|
29
|
+
* marshal into Cedar, or one matching no `permit`, comes back as a
|
|
30
|
+
* {@link Decision} `Block` (not a thrown error). Authorization
|
|
31
|
+
* *verdicts* never throw; they surface inside the returned value.
|
|
32
|
+
*
|
|
33
|
+
* @throws {Error} only for boundary faults the WASM glue rejects: a
|
|
34
|
+
* malformed `input` that fails `AuthorizeInput` deserialisation, or a
|
|
35
|
+
* failure serialising the resulting `Decision`.
|
|
36
|
+
*/
|
|
37
|
+
authorize(input) {
|
|
38
|
+
return this.inner.authorize(input);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Release the compiled bundle's WASM memory. Call when the evaluator is
|
|
42
|
+
* no longer needed; after this the wrapper must not be reused.
|
|
43
|
+
*/
|
|
44
|
+
free() {
|
|
45
|
+
this.inner.free();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function createPolicyEvaluator(policyText) {
|
|
49
|
+
const { PolicyEvaluator } = loadCedarModule();
|
|
50
|
+
return new PolicyEvaluatorRuntime(new PolicyEvaluator(policyText));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { PolicyEvaluatorRuntime, createPolicyEvaluator };
|