@nekzus/liop 1.3.0-alpha.1 → 2.0.0-alpha.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.
Files changed (122) hide show
  1. package/README.md +41 -17
  2. package/dist/bin/agent.d.ts +0 -1
  3. package/dist/bin/agent.js +5 -306
  4. package/dist/bin/agent.js.map +1 -0
  5. package/dist/{bridge/stream.d.ts → bridge.d.ts} +44 -3
  6. package/dist/bridge.js +2 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/chunk-4ABAFG44.js +33 -0
  9. package/dist/chunk-4ABAFG44.js.map +1 -0
  10. package/dist/chunk-ANFXJGMP.js +2 -0
  11. package/dist/chunk-ANFXJGMP.js.map +1 -0
  12. package/dist/chunk-DBXGYHKY.js +2 -0
  13. package/dist/chunk-DBXGYHKY.js.map +1 -0
  14. package/dist/chunk-HM77MWB6.js +2 -0
  15. package/dist/chunk-HM77MWB6.js.map +1 -0
  16. package/dist/chunk-HNDVAKEK.js +24 -0
  17. package/dist/chunk-HNDVAKEK.js.map +1 -0
  18. package/dist/chunk-HQZHZM6U.js +2 -0
  19. package/dist/chunk-HQZHZM6U.js.map +1 -0
  20. package/dist/chunk-P52IE4L6.js +2 -0
  21. package/dist/chunk-P52IE4L6.js.map +1 -0
  22. package/dist/chunk-PIBCW4BD.js +13 -0
  23. package/dist/chunk-PIBCW4BD.js.map +1 -0
  24. package/dist/chunk-PPCOS2NU.js +2 -0
  25. package/dist/chunk-PPCOS2NU.js.map +1 -0
  26. package/dist/chunk-RWRRBYG4.js +2 -0
  27. package/dist/chunk-RWRRBYG4.js.map +1 -0
  28. package/dist/chunk-S6RJHZV2.js +2 -0
  29. package/dist/chunk-S6RJHZV2.js.map +1 -0
  30. package/dist/chunk-UVTEJYHN.js +2 -0
  31. package/dist/chunk-UVTEJYHN.js.map +1 -0
  32. package/dist/chunk-X6FJATUE.js +29 -0
  33. package/dist/chunk-X6FJATUE.js.map +1 -0
  34. package/dist/chunk-XLVRRGOX.js +3 -0
  35. package/dist/chunk-XLVRRGOX.js.map +1 -0
  36. package/dist/client.d.ts +5 -0
  37. package/dist/client.js +2 -0
  38. package/dist/client.js.map +1 -0
  39. package/dist/{gateway/router.d.ts → gateway.d.ts} +37 -5
  40. package/dist/gateway.js +2 -0
  41. package/dist/gateway.js.map +1 -0
  42. package/dist/{client/index.d.ts → index-CyxNLlz7.d.ts} +24 -5
  43. package/dist/index.d.ts +313 -12
  44. package/dist/index.js +31 -12
  45. package/dist/index.js.map +1 -0
  46. package/dist/kyber-2WDOTUQX.js +2 -0
  47. package/dist/kyber-2WDOTUQX.js.map +1 -0
  48. package/dist/{mesh/node.d.ts → mesh.d.ts} +5 -3
  49. package/dist/mesh.js +2 -0
  50. package/dist/mesh.js.map +1 -0
  51. package/dist/{server/index.d.ts → server.d.ts} +145 -10
  52. package/dist/server.js +2 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/types.d.ts +17 -14
  55. package/dist/types.js +2 -26
  56. package/dist/types.js.map +1 -0
  57. package/dist/{crypto/verifier.d.ts → verifier-DTCD9imJ.d.ts} +3 -1
  58. package/dist/verifier-RQRYXA4C.js +2 -0
  59. package/dist/verifier-RQRYXA4C.js.map +1 -0
  60. package/dist/workers/logic-execution.d.ts +4 -2
  61. package/dist/workers/logic-execution.js +2 -123
  62. package/dist/workers/logic-execution.js.map +1 -0
  63. package/dist/workers/zk-verifier.d.ts +4 -2
  64. package/dist/workers/zk-verifier.js +2 -98
  65. package/dist/workers/zk-verifier.js.map +1 -0
  66. package/package.json +31 -17
  67. package/dist/bridge/index.d.ts +0 -37
  68. package/dist/bridge/index.js +0 -249
  69. package/dist/bridge/stream.js +0 -210
  70. package/dist/client/index.js +0 -275
  71. package/dist/crypto/logic-image-id.d.ts +0 -3
  72. package/dist/crypto/logic-image-id.js +0 -27
  73. package/dist/crypto/verifier.js +0 -97
  74. package/dist/economy/estimator.d.ts +0 -53
  75. package/dist/economy/estimator.js +0 -69
  76. package/dist/economy/index.d.ts +0 -5
  77. package/dist/economy/index.js +0 -3
  78. package/dist/economy/otel.d.ts +0 -38
  79. package/dist/economy/otel.js +0 -100
  80. package/dist/economy/telemetry.d.ts +0 -77
  81. package/dist/economy/telemetry.js +0 -224
  82. package/dist/errors.d.ts +0 -14
  83. package/dist/errors.js +0 -19
  84. package/dist/gateway/hybrid.d.ts +0 -23
  85. package/dist/gateway/hybrid.js +0 -199
  86. package/dist/gateway/router.js +0 -1036
  87. package/dist/mesh/index.d.ts +0 -1
  88. package/dist/mesh/index.js +0 -1
  89. package/dist/mesh/node.js +0 -853
  90. package/dist/prompts/adapters.d.ts +0 -16
  91. package/dist/prompts/adapters.js +0 -55
  92. package/dist/rpc/client.d.ts +0 -22
  93. package/dist/rpc/client.js +0 -40
  94. package/dist/rpc/codec/lpm.d.ts +0 -20
  95. package/dist/rpc/codec/lpm.js +0 -36
  96. package/dist/rpc/crypto/aes.d.ts +0 -22
  97. package/dist/rpc/crypto/aes.js +0 -47
  98. package/dist/rpc/crypto/kyber.d.ts +0 -27
  99. package/dist/rpc/crypto/kyber.js +0 -70
  100. package/dist/rpc/proto.d.ts +0 -2
  101. package/dist/rpc/proto.js +0 -33
  102. package/dist/rpc/server.d.ts +0 -13
  103. package/dist/rpc/server.js +0 -50
  104. package/dist/rpc/tls.d.ts +0 -26
  105. package/dist/rpc/tls.js +0 -54
  106. package/dist/rpc/types.d.ts +0 -28
  107. package/dist/rpc/types.js +0 -5
  108. package/dist/sandbox/guardian.d.ts +0 -18
  109. package/dist/sandbox/guardian.js +0 -58
  110. package/dist/sandbox/wasi.d.ts +0 -36
  111. package/dist/sandbox/wasi.js +0 -209
  112. package/dist/security/guardian.d.ts +0 -22
  113. package/dist/security/guardian.js +0 -52
  114. package/dist/security/zk.d.ts +0 -37
  115. package/dist/security/zk.js +0 -76
  116. package/dist/server/index.js +0 -937
  117. package/dist/server/pii.d.ts +0 -40
  118. package/dist/server/pii.js +0 -266
  119. package/dist/utils/logger.d.ts +0 -21
  120. package/dist/utils/logger.js +0 -70
  121. package/dist/utils/mcpCompact.d.ts +0 -11
  122. package/dist/utils/mcpCompact.js +0 -29
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/workers/zk-verifier.ts"],"names":["deriveImageId","logicPayload","deriveLogicImageDigest","verifyZkReceipt","payload","remoteImageIdHex","zkReceipt","sessionSecret","localImageIdHex","receiptBuf","version","journalLen","journal","seal","journalData","expectedSeal","crypto","workerHandler","task","error"],"mappings":"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","file":"zk-verifier.js","sourcesContent":["import crypto from \"node:crypto\";\nimport { parentPort } from \"node:worker_threads\";\nimport { deriveLogicImageDigest } from \"../crypto/logic-image-id.js\";\n\n// Ensure this worker is used via Piscina pool\nif (!parentPort) {\n\t// Not fatal in Piscina, but handled appropriately\n}\n\n/**\n * ZK Verification Payload Structure.\n * Modeled after RISC Zero & SP1 Receipt formats.\n */\nexport interface ZkVerificationPayload {\n\taction: \"verify_receipt\";\n\t/** Original logic payload (JS/WASM) sent by client */\n\tlogicPayload: Uint8Array;\n\t/** Expected ImageID (SHA-256) of the execution state */\n\tremoteImageIdHex: string;\n\t/** Cbor-encoded or raw buffer containing the execution Receipt (Journal + Seal) */\n\tzkReceipt: Uint8Array;\n\t/** Kyber-derived session secret to verify HMAC signature */\n\tsessionSecret?: Uint8Array;\n}\n\nfunction deriveImageId(logicPayload: Uint8Array): Buffer {\n\treturn deriveLogicImageDigest(logicPayload);\n}\n\n/**\n * Simulates heavy ZK-Proof cryptographic verification.\n * In a real environment, this delegates to @risc0/verifier or SP1 FFI bindings.\n */\nasync function verifyZkReceipt(\n\tpayload: ZkVerificationPayload,\n): Promise<{ verified: boolean; message: string }> {\n\tconst { logicPayload, remoteImageIdHex, zkReceipt, sessionSecret } = payload;\n\n\t// 1. Calculate local ImageID (Integrity Check)\n\tconst localImageId = deriveImageId(logicPayload);\n\tconst localImageIdHex = localImageId.toString(\"hex\");\n\n\tif (localImageIdHex !== remoteImageIdHex) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\tmessage: `Integrity Violation: Local (${localImageIdHex.slice(0, 8)}) != Remote (${remoteImageIdHex.slice(0, 8)})`,\n\t\t};\n\t}\n\n\t// 2. Structural Verification: Deserialize Binary Receipt\n\tconst receiptBuf = Buffer.from(zkReceipt);\n\tif (receiptBuf.length < 35) {\n\t\t// 1 version + 2 len + 32 seal minimum\n\t\treturn {\n\t\t\tverified: false,\n\t\t\tmessage: \"Receipt too short for binary format.\",\n\t\t};\n\t}\n\n\tconst version = receiptBuf[0];\n\tif (version !== 0x01) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\tmessage: `Unknown receipt version: ${version}`,\n\t\t};\n\t}\n\n\tconst journalLen = receiptBuf.readUInt16BE(1);\n\tconst journal = receiptBuf.subarray(3, 3 + journalLen);\n\tconst seal = receiptBuf.subarray(3 + journalLen);\n\n\tif (seal.length !== 32) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\tmessage: \"Invalid seal length (expected 32 bytes HMAC-SHA256).\",\n\t\t};\n\t}\n\n\t// 3. Parse journal and verify imageId\n\ttry {\n\t\tconst journalData = JSON.parse(journal.toString());\n\t\tif (journalData.image_id !== localImageIdHex) {\n\t\t\treturn {\n\t\t\t\tverified: false,\n\t\t\t\tmessage: `Journal ImageID mismatch: ${journalData.image_id.slice(0, 8)} != ${localImageIdHex.slice(0, 8)}`,\n\t\t\t};\n\t\t}\n\t} catch (_e) {\n\t\treturn { verified: false, message: \"Failed to parse journal data.\" };\n\t}\n\n\t// 4. Mathematical Verification (HMAC-SHA256)\n\tif (sessionSecret && sessionSecret.length > 0) {\n\t\tconst expectedSeal = crypto\n\t\t\t.createHmac(\"sha256\", sessionSecret)\n\t\t\t.update(journal)\n\t\t\t.digest();\n\t\tif (!crypto.timingSafeEqual(seal, expectedSeal)) {\n\t\t\treturn {\n\t\t\t\tverified: false,\n\t\t\t\tmessage: \"Invalid seal: HMAC verification failed.\",\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tverified: true,\n\t\tmessage: \"HMAC Commitment Verified: Integrity intact.\",\n\t};\n}\n\n/**\n * Main worker entry point for Piscina.\n */\nexport default async function workerHandler(\n\ttask: ZkVerificationPayload,\n): Promise<{ verified: boolean; message: string }> {\n\ttry {\n\t\tif (task.action === \"verify_receipt\") {\n\t\t\treturn await verifyZkReceipt(task);\n\t\t}\n\t\tthrow new Error(\"Unknown action in ZkVerifier Worker.\");\n\t} catch (error) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\tmessage: `Verification Error: ${(error as Error).message}`,\n\t\t};\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@nekzus/liop",
3
- "version": "1.3.0-alpha.1",
3
+ "version": "2.0.0-alpha.2",
4
4
  "description": "Official SDK for Logic-Injection-on-Origin Protocol (LIOP). Deploy Logic-on-Origin with WebAssembly at gRPC speed and bidirectional MCP compatibility.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
8
8
  "liop-agent": "./dist/bin/agent.js"
9
9
  },
10
+ "sideEffects": false,
10
11
  "files": [
11
12
  "dist",
12
13
  "README.md",
@@ -15,35 +16,35 @@
15
16
  "exports": {
16
17
  ".": {
17
18
  "types": "./dist/index.d.ts",
18
- "default": "./dist/index.js"
19
+ "import": "./dist/index.js"
19
20
  },
20
21
  "./client": {
21
- "types": "./dist/client/index.d.ts",
22
- "default": "./dist/client/index.js"
22
+ "types": "./dist/client.d.ts",
23
+ "import": "./dist/client.js"
23
24
  },
24
25
  "./server": {
25
- "types": "./dist/server/index.d.ts",
26
- "default": "./dist/server/index.js"
26
+ "types": "./dist/server.d.ts",
27
+ "import": "./dist/server.js"
27
28
  },
28
29
  "./types": {
29
30
  "types": "./dist/types.d.ts",
30
- "default": "./dist/types.js"
31
+ "import": "./dist/types.js"
31
32
  },
32
33
  "./bridge": {
33
- "types": "./dist/bridge/index.d.ts",
34
- "default": "./dist/bridge/index.js"
34
+ "types": "./dist/bridge.d.ts",
35
+ "import": "./dist/bridge.js"
35
36
  },
36
37
  "./gateway": {
37
- "types": "./dist/gateway/hybrid.d.ts",
38
- "default": "./dist/gateway/hybrid.js"
38
+ "types": "./dist/gateway.d.ts",
39
+ "import": "./dist/gateway.js"
39
40
  },
40
41
  "./mesh": {
41
- "types": "./dist/mesh/index.d.ts",
42
- "default": "./dist/mesh/index.js"
42
+ "types": "./dist/mesh.d.ts",
43
+ "import": "./dist/mesh.js"
43
44
  }
44
45
  },
45
46
  "scripts": {
46
- "build": "tsc -p tsconfig.build.json && npx tsx scripts/copy-protos.ts",
47
+ "build": "tsup && npx tsx scripts/copy-protos.ts",
47
48
  "test": "vitest run --fileParallelism=false",
48
49
  "test:all": "vitest run --fileParallelism=false",
49
50
  "test:integration": "vitest run tests/integration --fileParallelism=false",
@@ -107,6 +108,7 @@
107
108
  "@opentelemetry/sdk-metrics": "^2.7.0",
108
109
  "@types/node": "^25.3.1",
109
110
  "@vitest/coverage-v8": "^4.0.18",
111
+ "tsup": "^8.5.1",
110
112
  "tsx": "^4.21.0",
111
113
  "typescript": "^5.9.3",
112
114
  "vitest": "^4.0.18"
@@ -122,16 +124,15 @@
122
124
  "@libp2p/identify": "^4.0.14",
123
125
  "@libp2p/kad-dht": "^16.1.7",
124
126
  "@libp2p/mplex": "^12.0.11",
125
- "@libp2p/noise": "^1.0.1",
126
127
  "@libp2p/peer-id": "^4.0.10",
127
128
  "@libp2p/peer-id-factory": "^4.0.10",
128
129
  "@libp2p/ping": "^3.0.12",
129
130
  "@libp2p/tcp": "^11.0.14",
130
131
  "@libp2p/websockets": "^10.1.7",
131
- "@modelcontextprotocol/sdk": "^1.28.0",
132
132
  "@multiformats/multiaddr": "^13.0.1",
133
133
  "@opentelemetry/api": "^1.9.1",
134
- "gpt-tokenizer": "^3.4.0",
134
+ "acorn": "^8.16.0",
135
+ "acorn-walk": "^8.3.5",
135
136
  "hono": "^4.12.5",
136
137
  "it-pipe": "^3.0.1",
137
138
  "libp2p": "^3.1.3",
@@ -143,5 +144,18 @@
143
144
  "uint8arrays": "^3.1.1",
144
145
  "zod": "^3.23.11",
145
146
  "zod-to-json-schema": "^3.24.1"
147
+ },
148
+ "optionalDependencies": {
149
+ "@modelcontextprotocol/sdk": "^1.28.0",
150
+ "compromise": "14.15.0",
151
+ "gpt-tokenizer": "^3.4.0"
152
+ },
153
+ "peerDependencies": {
154
+ "@modelcontextprotocol/sdk": "^1.28.0"
155
+ },
156
+ "peerDependenciesMeta": {
157
+ "@modelcontextprotocol/sdk": {
158
+ "optional": true
159
+ }
146
160
  }
147
161
  }
@@ -1,37 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { LiopServerOptions } from "../server/index.js";
3
- import { LiopServer } from "../server/index.js";
4
- export interface LiopBridgeOptions {
5
- publishToMesh?: boolean;
6
- meshIdentity?: string;
7
- serverInfo?: {
8
- name: string;
9
- version: string;
10
- };
11
- security?: LiopServerOptions["security"];
12
- }
13
- /**
14
- * LIOP MCP Bridge
15
- * A bi-directional bridge that allows legacy MCP servers to join the LIOP mesh,
16
- * or exposes a LIOP server as an MCP-compatible stdio process for tools like Claude Desktop.
17
- */
18
- export declare class LiopMcpBridge {
19
- private options;
20
- private liopServer;
21
- private legacyMcpServer;
22
- constructor(source: LiopServer | McpServer, options?: LiopBridgeOptions);
23
- /**
24
- * Handles an incoming standard MCP JSON-RPC 2.0 payload.
25
- * Pipes it to the underlying server (LIOP or Legacy MCP).
26
- */
27
- handleJsonRpcRequest(payload: Record<string, unknown>): Promise<unknown>;
28
- private handleLiopToMcp;
29
- private successResponse;
30
- private errorResponse;
31
- private verifyZkReceipt;
32
- /**
33
- * Connects the bridge via stdio or Mesh depending on mode.
34
- */
35
- connect(): Promise<void>;
36
- }
37
- export * from "./stream.js";
@@ -1,249 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { LiopServer } from "../server/index.js";
3
- import { log } from "../utils/logger.js";
4
- /**
5
- * LIOP MCP Bridge
6
- * A bi-directional bridge that allows legacy MCP servers to join the LIOP mesh,
7
- * or exposes a LIOP server as an MCP-compatible stdio process for tools like Claude Desktop.
8
- */
9
- export class LiopMcpBridge {
10
- options;
11
- liopServer = null;
12
- legacyMcpServer = null;
13
- constructor(source, options = {}) {
14
- this.options = options;
15
- // Determine mode: Exposing LIOP to MCP (Claude) or Wrapping MCP to LIOP (Mesh)
16
- if (source instanceof LiopServer) {
17
- this.liopServer = source;
18
- log.info("[LIOP-Bridge] Mode: EXPOSE (LIOP -> MCP Stdio)");
19
- }
20
- else if (source instanceof McpServer) {
21
- this.legacyMcpServer = source;
22
- log.info("[LIOP-Bridge] Mode: WRAP (Legacy MCP -> LIOP Mesh)");
23
- }
24
- }
25
- /**
26
- * Handles an incoming standard MCP JSON-RPC 2.0 payload.
27
- * Pipes it to the underlying server (LIOP or Legacy MCP).
28
- */
29
- async handleJsonRpcRequest(payload) {
30
- const id = payload.id;
31
- const method = payload.method;
32
- const params = payload.params;
33
- if (payload.jsonrpc !== "2.0") {
34
- return this.errorResponse(id, -32600, "Invalid Request");
35
- }
36
- // Mode: EXPOSE (Standard behavior used by Claude Desktop)
37
- if (this.liopServer) {
38
- return this.handleLiopToMcp(id, method, params);
39
- }
40
- // Mode: WRAP (Redirecting via internal LiopServer after connect())
41
- if (this.legacyMcpServer && this.liopServer) {
42
- return this.handleLiopToMcp(id, method, params);
43
- }
44
- return this.errorResponse(id, -32601, "Bridge source not configured");
45
- }
46
- async handleLiopToMcp(id, method, params) {
47
- if (!this.liopServer)
48
- return null;
49
- if (method === "initialize") {
50
- return this.successResponse(id, {
51
- protocolVersion: "2025-11-25",
52
- capabilities: {
53
- prompts: {},
54
- resources: {},
55
- tools: {},
56
- },
57
- serverInfo: this.liopServer.getServerInfo(),
58
- });
59
- }
60
- if (method === "notifications/initialized")
61
- return undefined;
62
- if (method === "ping")
63
- return this.successResponse(id, {});
64
- if (method === "tools/list") {
65
- const tools = this.liopServer.listTools();
66
- return this.successResponse(id, { tools });
67
- }
68
- if (method === "resources/list") {
69
- const resources = this.liopServer.listResources();
70
- return this.successResponse(id, { resources });
71
- }
72
- if (method === "prompts/list") {
73
- const prompts = this.liopServer.listPrompts();
74
- return this.successResponse(id, { prompts });
75
- }
76
- if (method === "prompts/get") {
77
- if (!params?.name) {
78
- return this.errorResponse(id, -32602, "Missing prompt name");
79
- }
80
- try {
81
- const result = await this.liopServer.getPrompt({
82
- name: params.name,
83
- arguments: params.arguments,
84
- });
85
- return this.successResponse(id, result);
86
- }
87
- catch (err) {
88
- return this.errorResponse(id, -32000, err.message);
89
- }
90
- }
91
- if (method === "resources/read") {
92
- if (!params?.uri) {
93
- return this.errorResponse(id, -32602, "Missing resource URI");
94
- }
95
- try {
96
- const result = await this.liopServer.readResource(params.uri);
97
- return this.successResponse(id, result);
98
- }
99
- catch (err) {
100
- return this.errorResponse(id, -32000, err.message);
101
- }
102
- }
103
- if (method === "tools/call") {
104
- if (!params?.name) {
105
- return this.errorResponse(id, -32602, "Missing tool name");
106
- }
107
- const request = {
108
- name: params.name,
109
- arguments: params.arguments || {},
110
- };
111
- try {
112
- const result = await this.liopServer.callTool(request);
113
- const isVerified = await this.verifyZkReceipt(request, result);
114
- if (!isVerified) {
115
- return this.successResponse(id, {
116
- content: [
117
- {
118
- type: "text",
119
- text: "ALERT [LIOP ZERO-TRUST SHIELD] ZK Verification Failed. The mathematical ImageID does not match the original payload.",
120
- },
121
- ],
122
- isError: true,
123
- });
124
- }
125
- return this.successResponse(id, result);
126
- }
127
- catch (err) {
128
- return this.errorResponse(id, -32000, err.message);
129
- }
130
- }
131
- return this.errorResponse(id, -32601, "Method not found");
132
- }
133
- successResponse(id, result) {
134
- return { jsonrpc: "2.0", id, result };
135
- }
136
- errorResponse(id, code, message) {
137
- return { jsonrpc: "2.0", id, error: { code, message } };
138
- }
139
- async verifyZkReceipt(request, result) {
140
- if (!request.arguments?.payload ||
141
- typeof request.arguments.payload !== "string") {
142
- return true;
143
- }
144
- try {
145
- const payload = request.arguments.payload;
146
- const contentText = result.content[0]?.text;
147
- if (contentText && typeof contentText === "string") {
148
- try {
149
- const data = JSON.parse(contentText);
150
- if (data.image_id || data.zk_receipt) {
151
- // 1. Instantiate the Industrial Verifier ( backed by Piscina Worker Pool )
152
- const { LiopVerifier } = await import("../crypto/verifier.js");
153
- const verifier = new LiopVerifier();
154
- // 2. Delegate the heavy mathematical check (ZK Journal + Seal)
155
- const isAuthentic = await verifier.verifyZkReceipt(Buffer.from(payload, "utf-8"), data.image_id, Buffer.from(data.zk_receipt || "", "base64"));
156
- if (!isAuthentic) {
157
- return false;
158
- }
159
- data.audit_status =
160
- "VERIFIED: ZK-Receipt & ImageID Mathematically Verified by LiopMcpBridge";
161
- result.content[0].text = JSON.stringify(data);
162
- }
163
- }
164
- catch {
165
- // Output not JSON
166
- }
167
- }
168
- return true;
169
- }
170
- catch (e) {
171
- log.info("[LIOP-Bridge] ZK-Verifier Failure:", e);
172
- return false;
173
- }
174
- }
175
- /**
176
- * Connects the bridge via stdio or Mesh depending on mode.
177
- */
178
- async connect() {
179
- // In WRAP mode, we actually need to create a LiopServer and join the mesh
180
- if (this.legacyMcpServer) {
181
- const { LiopServer } = await import("../server/index.js");
182
- this.liopServer = new LiopServer(this.options.serverInfo || {
183
- name: "liop-bridge",
184
- version: "1.0.0",
185
- }, { security: this.options.security });
186
- if (this.options.publishToMesh) {
187
- await this.liopServer.connect();
188
- // Automatically Bridge Legacy Capabilities to LIOP Mesh
189
- // biome-ignore lint/suspicious/noExplicitAny: Internal legacy MCP properties are completely opaque and unexported
190
- const legacy = this.legacyMcpServer;
191
- // 1. Sync Tools
192
- if (legacy._registeredTools) {
193
- for (const [name, tool] of Object.entries(legacy._registeredTools)) {
194
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy structure
195
- const t = tool;
196
- this.liopServer.tool(name, t.description || "", t.inputSchema || {},
197
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy callback args
198
- async (args) => {
199
- return await t.handler(args);
200
- });
201
- }
202
- }
203
- // 2. Sync Resources
204
- if (legacy._registeredResources) {
205
- for (const [uri, resource] of Object.entries(legacy._registeredResources)) {
206
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy structure
207
- const r = resource;
208
- this.liopServer.resource(r.name, uri, r.metadata?.description || "", r.metadata?.mimeType || "application/octet-stream", async () => {
209
- const res = await r.readCallback(new URL(uri));
210
- return res.contents[0].text;
211
- });
212
- }
213
- }
214
- }
215
- return;
216
- }
217
- // In EXPOSE mode, listen to stdio (Claude Desktop)
218
- const readline = await import("node:readline");
219
- const rl = readline.createInterface({
220
- input: process.stdin,
221
- output: process.stdout,
222
- terminal: false,
223
- });
224
- const shutdown = async () => {
225
- log.info("[LIOP-Bridge] Disconnecting session...");
226
- if (this.liopServer)
227
- await this.liopServer.close();
228
- process.exit(0);
229
- };
230
- rl.on("close", shutdown);
231
- process.on("SIGINT", shutdown);
232
- process.on("SIGTERM", shutdown);
233
- rl.on("line", async (line) => {
234
- if (!line.trim())
235
- return;
236
- try {
237
- const payload = JSON.parse(line);
238
- const response = await this.handleJsonRpcRequest(payload);
239
- if (response) {
240
- process.stdout.write(`${JSON.stringify(response)}\n`);
241
- }
242
- }
243
- catch (e) {
244
- log.error(`[LIOP-Bridge] Error: ${e.message}`);
245
- }
246
- });
247
- }
248
- }
249
- export * from "./stream.js";
@@ -1,210 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { serve } from "@hono/node-server";
3
- import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
4
- import { Hono } from "hono";
5
- import { cors } from "hono/cors";
6
- import { log } from "../utils/logger.js";
7
- import { LiopMcpBridge } from "./index.js";
8
- const DEFAULT_MAX_SESSIONS_PER_IP = 10;
9
- const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
10
- const EVICTION_INTERVAL_MS = 60 * 1000; // Check every minute
11
- /**
12
- * LiopStreamBridge
13
- *
14
- * Exposes a LiopServer over a remote HTTP network using the industry-standard
15
- * MCP Streamable HTTP Transport + Hono JS.
16
- *
17
- * Supports concurrent multi-client connections via per-session transport instances (Map pattern).
18
- * External agents connect using only a URL + Bearer Token (Zero-Trust).
19
- *
20
- * Security hardening:
21
- * - Zero-Trust Bearer Token enforcement
22
- * - Per-IP rate limiting on session creation
23
- * - Automatic eviction of idle sessions (TTL)
24
- */
25
- export class LiopStreamBridge {
26
- options;
27
- app;
28
- httpServer = null;
29
- bridgeLogic;
30
- activeSessions;
31
- evictionTimer = null;
32
- maxSessionsPerIp;
33
- sessionTimeoutMs;
34
- constructor(internalServer, options = {}) {
35
- this.options = options;
36
- this.app = new Hono();
37
- this.bridgeLogic = new LiopMcpBridge(internalServer);
38
- this.activeSessions = new Map();
39
- this.maxSessionsPerIp =
40
- options.maxSessionsPerIp ?? DEFAULT_MAX_SESSIONS_PER_IP;
41
- this.sessionTimeoutMs =
42
- options.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
43
- this.setupRoutes();
44
- }
45
- /**
46
- * Creates a new per-session transport instance and wires it to the LIOPMcpBridge logic.
47
- */
48
- createSessionTransport(clientIp) {
49
- const transport = new WebStandardStreamableHTTPServerTransport({
50
- sessionIdGenerator: () => randomUUID(),
51
- onsessioninitialized: (sessionId) => {
52
- this.activeSessions.set(sessionId, {
53
- transport,
54
- lastActivity: Date.now(),
55
- clientIp,
56
- });
57
- log.info(`[LIOP-StreamBridge] Session opened: ${sessionId} (IP: ${clientIp})`);
58
- },
59
- });
60
- // Wire the transport's incoming messages to the LiopMcpBridge JSON-RPC router
61
- transport.onmessage = async (message) => {
62
- // Touch activity timestamp on every message
63
- if (transport.sessionId) {
64
- const entry = this.activeSessions.get(transport.sessionId);
65
- if (entry)
66
- entry.lastActivity = Date.now();
67
- }
68
- try {
69
- const result = await this.bridgeLogic.handleJsonRpcRequest(message);
70
- // Notifications return undefined — no response needed
71
- if (result !== undefined) {
72
- await transport.send(result);
73
- }
74
- }
75
- catch (err) {
76
- log.info("[LIOP-StreamBridge] JSON-RPC error:", err.message);
77
- }
78
- };
79
- transport.onclose = () => {
80
- if (transport.sessionId) {
81
- this.activeSessions.delete(transport.sessionId);
82
- log.info(`[LIOP-StreamBridge] Session closed: ${transport.sessionId}`);
83
- }
84
- };
85
- return transport;
86
- }
87
- /**
88
- * Returns the number of active sessions for a given IP.
89
- */
90
- countSessionsByIp(ip) {
91
- let count = 0;
92
- for (const entry of this.activeSessions.values()) {
93
- if (entry.clientIp === ip)
94
- count++;
95
- }
96
- return count;
97
- }
98
- /**
99
- * Extracts client IP from the request (supports X-Forwarded-For for reverse proxies).
100
- */
101
- getClientIp(c) {
102
- return (c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ||
103
- c.req.header("x-real-ip") ||
104
- "unknown");
105
- }
106
- /**
107
- * Evicts sessions that have been idle longer than the configured timeout.
108
- */
109
- evictIdleSessions() {
110
- const now = Date.now();
111
- for (const [sessionId, entry] of this.activeSessions) {
112
- if (now - entry.lastActivity > this.sessionTimeoutMs) {
113
- log.info(`[LIOP-StreamBridge] Evicting idle session: ${sessionId}`);
114
- entry.transport.close().catch(() => {
115
- /* Swallow close errors */
116
- });
117
- this.activeSessions.delete(sessionId);
118
- }
119
- }
120
- }
121
- setupRoutes() {
122
- this.app.use("*", cors());
123
- // Initialize strict zero-trust token if not provided
124
- if (!process.env.ZERO_TRUST_TOKEN) {
125
- process.env.ZERO_TRUST_TOKEN = randomUUID();
126
- log.info("=".repeat(60));
127
- log.info("⚠️ STRICT ZERO-TRUST MODE ENABLED ⚠️");
128
- log.info("No ZERO_TRUST_TOKEN found in environment.");
129
- log.info("A secure ephemeral token has been generated for this session:");
130
- log.info(`Token: ${process.env.ZERO_TRUST_TOKEN}`);
131
- log.info("=".repeat(60));
132
- }
133
- // ZTA (Zero-Trust Architecture) Security Middleware
134
- this.app.use("/mcp", async (c, next) => {
135
- const auth = c.req.header("Authorization");
136
- const expectedToken = process.env.ZERO_TRUST_TOKEN;
137
- if (!auth?.startsWith("Bearer ") ||
138
- auth.split(" ")[1] !== expectedToken) {
139
- log.info("[LIOP-StreamBridge] ALERT: Access denied - Invalid Zero-Trust token.");
140
- return c.json({ error: "Unauthorized: LIOP Zero-Trust Policy Enforced" }, 401);
141
- }
142
- await next();
143
- });
144
- // Multi-Session Streamable HTTP Handler
145
- this.app.all("/mcp", async (c) => {
146
- const sessionId = c.req.header("mcp-session-id");
147
- // Route to existing session if session ID is present
148
- if (sessionId) {
149
- const existing = this.activeSessions.get(sessionId);
150
- if (!existing) {
151
- return c.json({ error: "Session not found" }, 404);
152
- }
153
- // Touch activity on every routed request
154
- existing.lastActivity = Date.now();
155
- const response = await existing.transport.handleRequest(c.req.raw);
156
- // If DELETE, the transport closes internally but onclose may not fire.
157
- // Explicitly clean up the session from the Map.
158
- if (c.req.method === "DELETE") {
159
- this.activeSessions.delete(sessionId);
160
- log.info(`[LIOP-StreamBridge] Session closed (DELETE): ${sessionId}`);
161
- }
162
- return response;
163
- }
164
- // No session ID → New client initializing.
165
- // Rate-limit: enforce max sessions per IP
166
- const clientIp = this.getClientIp(c);
167
- const currentSessions = this.countSessionsByIp(clientIp);
168
- if (currentSessions >= this.maxSessionsPerIp) {
169
- log.info(`[LIOP-StreamBridge] Rate limit hit for IP: ${clientIp} (${currentSessions} sessions)`);
170
- return c.json({ error: "Too Many Sessions: Rate limit exceeded" }, 429);
171
- }
172
- const transport = this.createSessionTransport(clientIp);
173
- return transport.handleRequest(c.req.raw);
174
- });
175
- }
176
- /**
177
- * Starts the LiopStreamBridge HTTP server and session eviction timer.
178
- */
179
- async start(port) {
180
- const listenPort = port ?? this.options.port ?? 3000;
181
- // Start the idle session eviction timer
182
- this.evictionTimer = setInterval(() => this.evictIdleSessions(), EVICTION_INTERVAL_MS);
183
- return new Promise((resolve) => {
184
- this.httpServer = serve({
185
- fetch: this.app.fetch,
186
- port: listenPort,
187
- }, (info) => {
188
- log.info(`[LIOP-StreamBridge] Streamable HTTP Gateway on http://localhost:${info.port}/mcp`);
189
- resolve();
190
- });
191
- });
192
- }
193
- /**
194
- * Graceful shutdown — closes all active sessions, stops timers, and releases port.
195
- */
196
- async stop() {
197
- if (this.evictionTimer) {
198
- clearInterval(this.evictionTimer);
199
- this.evictionTimer = null;
200
- }
201
- for (const [id, entry] of this.activeSessions) {
202
- await entry.transport.close();
203
- this.activeSessions.delete(id);
204
- }
205
- if (this.httpServer) {
206
- this.httpServer.close();
207
- log.info("[LIOP-StreamBridge] HTTP ports released.");
208
- }
209
- }
210
- }