@mudraid/sidecar 1.1.0 → 1.1.2

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/README.md CHANGED
@@ -55,3 +55,15 @@ The image uses a build stage and runs compiled code as the unprivileged Node use
55
55
  ## Qualification still pending
56
56
 
57
57
  Exact body bytes, caller, HTTP target and action/configuration are now bound to the signed decision, and the sidecar forwards its owned authorized snapshot. Trusted business-fact profiles and policy projection remain unimplemented; the runtime must not be represented as proving independent business-state truth or a committed operation. Standalone installed-package checks also do not establish network bypass resistance, containment-channel convergence, graceful drain, chaos/soak behavior or multi-architecture publication. These require their respective runtime evidence before broader support claims.
58
+
59
+
60
+ ### Authorization expires before forwarding
61
+
62
+ A verified decision that arrives after its deadline is refused with HTTP 503,
63
+ `ENFORCE_DECIDE_UNAVAILABLE`, and reason `deadline_exceeded`. The message explains
64
+ that authorization expired and this attempt was not forwarded. This differs
65
+ from an unreachable authority; it does not mean that permission was denied.
66
+ The adapter does not automatically retry. A deliberate retry must obtain a fresh
67
+ decision. Use application-level idempotency for operations that could already
68
+ have executed in an earlier attempt; this error makes no claim about those
69
+ other attempts. Never reuse an expired decision or disable expiry validation.
@@ -2,38 +2,10 @@
2
2
  "schema": "mudraid.sidecar.build-inputs/1",
3
3
  "bundledCore": {
4
4
  "name": "@mudraid/adapter-node",
5
- "version": "1.1.0",
5
+ "version": "1.1.2",
6
6
  "license": "Apache-2.0"
7
7
  },
8
8
  "inputs": [
9
- {
10
- "path": "adapter-node/src/controlLoop.ts",
11
- "sha256": "ab76d5f9032564729bd6a3bf7cbd388a3d155cf9986b16e98c5e191beccce5be"
12
- },
13
- {
14
- "path": "adapter-node/src/decideClient.ts",
15
- "sha256": "34a649478f14e70a694e34f48df917caf2960addb917840d063ecf0c2b2b42b9"
16
- },
17
- {
18
- "path": "adapter-node/src/executionBinding.ts",
19
- "sha256": "46f30a08a1c9816d5aa1fb04851980430f30e67e6a6e3d8134ee98ec935d7df6"
20
- },
21
- {
22
- "path": "adapter-node/src/httpAuthority.ts",
23
- "sha256": "9d75be27fd2a74e3ba7700e28ff26e1041262e70a8c621e77ba9d1c55b1efc16"
24
- },
25
- {
26
- "path": "adapter-node/src/index.ts",
27
- "sha256": "f13da52bce3d35ef7fe08e2fcbb25c05b2946c987eb59293126b19987cf5b951"
28
- },
29
- {
30
- "path": "adapter-node/src/signedBundle.ts",
31
- "sha256": "8a559c9ecfc2f69a392b6334159edc4384bd1c4b7033c53c5c38f9fc41f3c87e"
32
- },
33
- {
34
- "path": "adapter-node/src/types.ts",
35
- "sha256": "c3f95641c1a014bde935963a15728473a22a3ae4c2979c5f9b3bfcdac981d444"
36
- },
37
9
  {
38
10
  "path": "src/cli.ts",
39
11
  "sha256": "848e7ecbeed33be9e9da3786e1b8a90eb29e62a2bd391a23f57d27710ff37d52"
@@ -61,6 +33,34 @@
61
33
  {
62
34
  "path": "src/upstream.ts",
63
35
  "sha256": "804536cd44cf926fd5415b4bba95d03dfe031b7f00688f275184daf3b802caf8"
36
+ },
37
+ {
38
+ "path": "adapter-node/src/controlLoop.ts",
39
+ "sha256": "e51bdeccd9369006f30a5df6fda3d31aa88d718728143bf4b0d1087ef446040a"
40
+ },
41
+ {
42
+ "path": "adapter-node/src/decideClient.ts",
43
+ "sha256": "34a649478f14e70a694e34f48df917caf2960addb917840d063ecf0c2b2b42b9"
44
+ },
45
+ {
46
+ "path": "adapter-node/src/executionBinding.ts",
47
+ "sha256": "46f30a08a1c9816d5aa1fb04851980430f30e67e6a6e3d8134ee98ec935d7df6"
48
+ },
49
+ {
50
+ "path": "adapter-node/src/httpAuthority.ts",
51
+ "sha256": "cdf8f7e18ef8d71d90a88b5dd9cab455b70a6193ded98be84392eca8aa468acb"
52
+ },
53
+ {
54
+ "path": "adapter-node/src/index.ts",
55
+ "sha256": "f13da52bce3d35ef7fe08e2fcbb25c05b2946c987eb59293126b19987cf5b951"
56
+ },
57
+ {
58
+ "path": "adapter-node/src/signedBundle.ts",
59
+ "sha256": "8a559c9ecfc2f69a392b6334159edc4384bd1c4b7033c53c5c38f9fc41f3c87e"
60
+ },
61
+ {
62
+ "path": "adapter-node/src/types.ts",
63
+ "sha256": "f2e4f0a1441bb96122c418c0559a6015eb8234a7fb03014027680eacb722fb55"
64
64
  }
65
65
  ]
66
66
  }
@@ -63,7 +63,7 @@ function buildFacts(inbound, config) {
63
63
  };
64
64
  }
65
65
 
66
- // ../mudraid-adapter-node/src/decideClient.ts
66
+ // vendor/adapter-node/src/decideClient.ts
67
67
  function staticDecideClient(result) {
68
68
  return async () => result;
69
69
  }
@@ -73,10 +73,10 @@ function throwingDecideClient(error) {
73
73
  };
74
74
  }
75
75
 
76
- // ../mudraid-adapter-node/src/httpAuthority.ts
76
+ // vendor/adapter-node/src/httpAuthority.ts
77
77
  import { randomUUID } from "node:crypto";
78
78
 
79
- // ../mudraid-adapter-node/src/signedBundle.ts
79
+ // vendor/adapter-node/src/signedBundle.ts
80
80
  import { createHash, createPublicKey, verify } from "node:crypto";
81
81
  function object(value) {
82
82
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid object");
@@ -181,7 +181,7 @@ function verifyBundle(input, keys, binding, active, now = Date.now()) {
181
181
  return freeze({ version, digest, expiresAt: expires, surface: JSON.parse(JSON.stringify(surface)), actions });
182
182
  }
183
183
 
184
- // ../mudraid-adapter-node/src/executionBinding.ts
184
+ // vendor/adapter-node/src/executionBinding.ts
185
185
  import { createHash as createHash2 } from "node:crypto";
186
186
  var sha256 = (bytes) => createHash2("sha256").update(bytes).digest("hex");
187
187
  function bindExecution(snapshot, action, context) {
@@ -223,7 +223,7 @@ function boundedArgumentBody(body) {
223
223
  return Buffer.from(body).toString("base64");
224
224
  }
225
225
 
226
- // ../mudraid-adapter-node/src/httpAuthority.ts
226
+ // vendor/adapter-node/src/httpAuthority.ts
227
227
  function keyMap(entries) {
228
228
  if (!Array.isArray(entries)) throw new Error("Invalid verification key set");
229
229
  const keys = /* @__PURE__ */ Object.create(null);
@@ -350,9 +350,14 @@ var HttpAuthority = class {
350
350
  const response = await this.request("decide", "POST", {
351
351
  schema_version: "mudraid.enforce.decide-request/1",
352
352
  decision_id: decisionId,
353
- adapter: { type: this.adapterType, version: "1.1.0" },
353
+ adapter: { type: this.adapterType, version: "1.1.2" },
354
354
  bundle: { version: snapshot.version, payload_digest: snapshot.digest },
355
- surface: snapshot.surface,
355
+ // Bundle display metadata is not part of the strict decision contract.
356
+ surface: {
357
+ platform_id: snapshot.surface["platform_id"],
358
+ environment: snapshot.surface["environment"],
359
+ canonical_resource_uri: snapshot.surface["canonical_resource_uri"]
360
+ },
356
361
  action: mapped,
357
362
  request: { transport: "mcp_streamable_http", http_method: context.httpMethod, path: context.path },
358
363
  presented_authorization: context.presentedAuthorization,
@@ -362,7 +367,7 @@ var HttpAuthority = class {
362
367
  const now = Date.now();
363
368
  const decidedAt = instant(response["decided_at"]);
364
369
  if (decidedAt > now + 3e4 || now - decidedAt > 6e4) throw new Error("Stale decision");
365
- if (instant(response["deadline_at"]) <= now) throw new Error("Expired decision");
370
+ const deadline = instant(response["deadline_at"]);
366
371
  const signature = object(response["signature"]);
367
372
  const claims = object(signature["claims"]);
368
373
  const keyId = signature["key_id"];
@@ -381,6 +386,7 @@ var HttpAuthority = class {
381
386
  if (instant(claims["not_before"]) > now + 3e4 || instant(claims["expires_at"]) <= now) throw new Error("Invalid decision window");
382
387
  if (snapshot !== this.bundle) throw new Error("Bundle changed during decision");
383
388
  if (response["decision"] !== "allow" && response["decision"] !== "deny") throw new Error("Invalid decision outcome");
389
+ if (deadline <= Date.now()) return { status: "expired" };
384
390
  if (this.observed?.version !== snapshot.version || this.observed.digest !== snapshot.digest) {
385
391
  this.observed = { version: snapshot.version, digest: snapshot.digest, at: new Date(now).toISOString() };
386
392
  }
@@ -391,11 +397,11 @@ var HttpAuthority = class {
391
397
  }
392
398
  };
393
399
 
394
- // ../mudraid-adapter-node/src/types.ts
400
+ // vendor/adapter-node/src/types.ts
395
401
  var RESERVED_HEADER_PREFIX = "x-mudraid-";
396
402
  var MAX_TOOL_NAME_LEN = 512;
397
403
 
398
- // ../mudraid-adapter-node/src/controlLoop.ts
404
+ // vendor/adapter-node/src/controlLoop.ts
399
405
  import { randomUUID as randomUUID2 } from "node:crypto";
400
406
  var CONTROL_VERBS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "DELETE"]);
401
407
  var DEFAULT_PUBLIC_METHODS = /* @__PURE__ */ new Set([
@@ -404,6 +410,7 @@ var DEFAULT_PUBLIC_METHODS = /* @__PURE__ */ new Set([
404
410
  "tools/list"
405
411
  ]);
406
412
  var DECIDE_UNAVAILABLE_REASONS = {
413
+ expired: "deadline_exceeded",
407
414
  timeout: "deadline_exceeded",
408
415
  error: "authority_source_unavailable",
409
416
  unreachable: "authority_source_unavailable",
@@ -632,6 +639,17 @@ async function evaluateV2(facts, decide) {
632
639
  stripped
633
640
  );
634
641
  }
642
+ if (result.status === "expired") {
643
+ return deny(
644
+ "deadline_exceeded",
645
+ "authorization",
646
+ 503,
647
+ "ENFORCE_DECIDE_UNAVAILABLE",
648
+ "Authorization expired before this request could be forwarded. This attempt was not forwarded. Obtain fresh authorization before retrying; do not automatically retry operations that may already have executed in another attempt.",
649
+ stripped,
650
+ "not_safely_decided"
651
+ );
652
+ }
635
653
  const reason = DECIDE_UNAVAILABLE_REASONS[result.status] ?? "authority_source_unavailable";
636
654
  return deny(
637
655
  reason,
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  main,
9
9
  staticDecideClient,
10
10
  throwingDecideClient
11
- } from "./chunk-AHPE2F3B.js";
11
+ } from "./chunk-5SXBQNJF.js";
12
12
  export {
13
13
  DEFAULT_MAX_BODY_BYTES,
14
14
  HttpAuthority,
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  main
3
- } from "./chunk-AHPE2F3B.js";
3
+ } from "./chunk-5SXBQNJF.js";
4
4
 
5
5
  // src/cli.ts
6
6
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mudraid/sidecar",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "private": false,
5
5
  "description": "Customer-hosted MudraID enforcement proxy with authenticated configuration and signed decisions.",
6
6
  "license": "Apache-2.0",
@@ -21,11 +21,11 @@
21
21
  "node": ">=20"
22
22
  },
23
23
  "devDependencies": {
24
- "@types/node": "20.17.9",
25
- "tsx": "4.19.2",
24
+ "@types/node": "20.19.43",
25
+ "esbuild": "0.28.2",
26
+ "tsx": "4.23.15",
26
27
  "typescript": "5.6.3",
27
- "vitest": "2.1.8",
28
- "esbuild": "0.23.1"
28
+ "vitest": "4.1.11"
29
29
  },
30
30
  "exports": {
31
31
  ".": "./dist/index.js"