@nekzus/liop 1.2.0 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -61
- package/dist/bridge/stream.js +14 -6
- package/dist/client/index.js +7 -7
- package/dist/crypto/verifier.d.ts +1 -1
- package/dist/crypto/verifier.js +2 -1
- package/dist/gateway/router.d.ts +7 -0
- package/dist/gateway/router.js +21 -3
- package/dist/sandbox/guardian.js +27 -4
- package/dist/sandbox/wasi.js +25 -0
- package/dist/security/zk.d.ts +1 -1
- package/dist/security/zk.js +11 -1
- package/dist/server/index.d.ts +23 -1
- package/dist/server/index.js +140 -30
- package/dist/server/ner-scanner.d.ts +29 -0
- package/dist/server/ner-scanner.js +141 -0
- package/dist/server/pii.d.ts +27 -1
- package/dist/server/pii.js +167 -5
- package/dist/workers/logic-execution.js +4 -2
- package/dist/workers/zk-verifier.d.ts +2 -0
- package/dist/workers/zk-verifier.js +15 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -4,19 +4,19 @@
|
|
|
4
4
|
<img alt="Logic-Injection-on-Origin Protocol Logo" src="https://res.cloudinary.com/dsvsl0b0b/image/upload/v1774702621/Neural-Mesh-Protocol/hoanw0m6tybpz5fbl12n.svg?v=20260328" width="700">
|
|
5
5
|
</picture>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
<h1>Logic-Injection-on-Origin Protocol (LIOP) — TypeScript SDK</h1>
|
|
8
8
|
<p align="center">
|
|
9
|
-
<a href="https://github.com/Nekzus/
|
|
9
|
+
<a href="https://github.com/Nekzus/LIOP/actions/workflows/ci.yml"><img src="https://github.com/Nekzus/LIOP/actions/workflows/ci.yml/badge.svg?event=push" alt="Github Workflow"></a>
|
|
10
10
|
<a href="https://www.npmjs.com/package/@nekzus/liop"><img src="https://img.shields.io/npm/v/@nekzus/liop.svg" alt="npm version"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/@nekzus/liop"><img src="https://img.shields.io/npm/dm/@nekzus/liop.svg" alt="npm-month"></a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/@nekzus/liop"><img src="https://img.shields.io/npm/dt/@nekzus/liop.svg?style=flat" alt="npm-total"></a>
|
|
13
|
-
<a href="https://github.com/Nekzus/
|
|
14
|
-
<a href="https://
|
|
13
|
+
<a href="https://github.com/Nekzus/LIOP/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Nekzus/LIOP.svg" alt="License"></a>
|
|
14
|
+
<a href="https://nekzus-32.mintlify.app/"><img src="https://img.shields.io/badge/docs-mintlify-0D9373?style=flat" alt="Docs"></a>
|
|
15
15
|
<a href="https://deepwiki.com/Nekzus/LIOP"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
16
16
|
<a href="https://paypal.me/maseortega"><img src="https://img.shields.io/badge/donate-paypal-blue.svg?style=flat-square" alt="Donate"></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
<p><strong>The official TypeScript SDK for the Logic-Injection-on-Origin Protocol.</strong></p>
|
|
20
20
|
<p>Deploy Logic-on-Origin with WebAssembly sandboxing, gRPC-speed execution, and full MCP backward compatibility.</p>
|
|
21
21
|
</div>
|
|
22
22
|
|
|
@@ -30,19 +30,19 @@ This fundamentally solves the data privacy, bandwidth, and latency challenges of
|
|
|
30
30
|
|
|
31
31
|
### Key Capabilities
|
|
32
32
|
|
|
33
|
-
| Feature
|
|
34
|
-
|
|
35
|
-
| **Logic-Injection-on-Origin** | LLMs send code, not queries. Data never leaves the origin server.
|
|
36
|
-
| **MCP Drop-in Replacement**
|
|
37
|
-
| **Guardian AST**
|
|
38
|
-
| **WASI Sandbox**
|
|
39
|
-
| **PII Shield**
|
|
40
|
-
| **ZK-Receipts**
|
|
41
|
-
| **Worker Pool**
|
|
42
|
-
| **Cross-AI Adapters
|
|
43
|
-
| **MCP Bridge**
|
|
44
|
-
| **Post-Quantum Ready**
|
|
45
|
-
| **P2P Mesh**
|
|
33
|
+
| Feature | Description |
|
|
34
|
+
| :---------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| **Logic-Injection-on-Origin** | LLMs send code, not queries. Data never leaves the origin server. |
|
|
36
|
+
| **MCP Drop-in Replacement** | `LiopServer` mirrors the Anthropic MCP `Server` API — tools, resources, and prompts with `Zod` schemas. |
|
|
37
|
+
| **Guardian AST** | Zero-time heuristic inspection blocks sandbox escapes (`require`, `fs`, `eval`, `fetch`, prototype pollution). |
|
|
38
|
+
| **WASI Sandbox** | JavaScript payloads execute inside V8 isolates with CPU fuel limits and no access to Node.js globals. |
|
|
39
|
+
| **PII Shield** | Multi-layer egress filter with Regional Presets (Email, Credit Card with Luhn, IP, Phone, SSN, IBAN Mod-97, Passport MRZ) and custom keys. |
|
|
40
|
+
| **ZK-Receipts** | Cryptographic proof (SHA-256 ImageID + HMAC-SHA256 seal) that the returned result was computed honestly from the injected logic. |
|
|
41
|
+
| **Worker Pool** | Heavy computation (crypto, sandboxing) dispatched to OS threads via `piscina`, unblocking the V8 event loop. |
|
|
42
|
+
| **Cross-AI Adapters** | Zero-Shot system prompts automatically adapt instructions for Claude (XML-heavy) vs OpenAI/Gemini (JSON-schema). |
|
|
43
|
+
| **MCP Bridge** | `LiopMcpBridge` adapts any `LiopServer` to the JSON-RPC 2.0 / stdio protocol used by Claude Desktop, Cursor, etc. |
|
|
44
|
+
| **Post-Quantum Ready** | ML-KEM-768 (Kyber) handshake + AES-256-GCM symmetric encryption for transport-layer security. |
|
|
45
|
+
| **P2P Mesh** | Kademlia DHT discovery via `libp2p` with TCP + WebSocket + Yamux multiplexing and Noise encryption. |
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
@@ -91,6 +91,7 @@ To use the Neural Mesh inside Claude Desktop, update your `claude_desktop_config
|
|
|
91
91
|
### Persistence & Identity
|
|
92
92
|
|
|
93
93
|
The agent automatically manages your P2P identity:
|
|
94
|
+
|
|
94
95
|
- **Identity Path**: `~/.liop/identity.json`. This file contains your unique PeerID. Keep it safe if you want to maintain a consistent identity in the mesh.
|
|
95
96
|
- **Bootstrap Nodes**: By default, the agent connects to the **LIOP Alpha Nexus**. You can provide custom bootstrap addresses as CLI arguments:
|
|
96
97
|
```bash
|
|
@@ -187,9 +188,24 @@ new LiopServer(
|
|
|
187
188
|
serverInfo: { name: string; version: string },
|
|
188
189
|
config?: {
|
|
189
190
|
capabilities?: Record<string, unknown>;
|
|
191
|
+
workerPool?: {
|
|
192
|
+
enabled?: boolean; // Enable OS-thread sandboxing (default: true)
|
|
193
|
+
maxThreads?: number; // Max worker threads (default: CPU count)
|
|
194
|
+
maxHeapMb?: number; // V8 heap limit per worker (default: 64, env: LIOP_WORKER_MAX_HEAP_MB)
|
|
195
|
+
};
|
|
190
196
|
security?: {
|
|
191
197
|
piiPatterns?: PiiRule[]; // Regex/validator rules for PII detection
|
|
192
198
|
forbiddenKeys?: string[]; // Keys stripped from outgoing responses
|
|
199
|
+
enableNerScanning?: boolean; // NLP entity detection via compromise (default: false)
|
|
200
|
+
rateLimit?: { // Sliding window rate limiter per tool
|
|
201
|
+
maxPerWindow?: number; // Max calls per window (default: 30)
|
|
202
|
+
windowMs?: number; // Window duration in ms (default: 60000)
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
taxonomy?: { // Data domain classification
|
|
206
|
+
domain?: string; // e.g., "finance", "healthcare"
|
|
207
|
+
clearanceTier?: string; // e.g., "tier-0", "tier-1"
|
|
208
|
+
executionTypes?: string[]; // e.g., ["aggregation", "analytics"]
|
|
193
209
|
};
|
|
194
210
|
}
|
|
195
211
|
)
|
|
@@ -197,24 +213,24 @@ new LiopServer(
|
|
|
197
213
|
|
|
198
214
|
#### Methods
|
|
199
215
|
|
|
200
|
-
| Method
|
|
201
|
-
|
|
202
|
-
| `tool()`
|
|
203
|
-
| `prompt()`
|
|
204
|
-
| `resource()`
|
|
205
|
-
| `dataDictionary()`
|
|
206
|
-
| `setSandboxData()`
|
|
207
|
-
| `enableZeroShotAutonomy()` | `()`
|
|
208
|
-
| `callTool()`
|
|
209
|
-
| `listTools()`
|
|
210
|
-
| `listPrompts()`
|
|
211
|
-
| `getPrompt()`
|
|
212
|
-
| `listResources()`
|
|
213
|
-
| `readResource()`
|
|
214
|
-
| `getServerInfo()`
|
|
215
|
-
| `connectToMesh()`
|
|
216
|
-
| `clearAstCache()`
|
|
217
|
-
| `close()`
|
|
216
|
+
| Method | Signature | Description |
|
|
217
|
+
| :--------------------------- | :------------------------------------------------- | :---------------------------------------------------------------------------------- |
|
|
218
|
+
| `tool()` | `(name, description, zodSchema, handler)` | Registers a callable tool with Zod input validation. |
|
|
219
|
+
| `prompt()` | `(name, description, args, handler)` | Registers a dynamic prompt template. |
|
|
220
|
+
| `resource()` | `(name, uri, description?, mimeType?, content?)` | Registers a readable resource. |
|
|
221
|
+
| `dataDictionary()` | `(schema, name?, uri?, description?)` | Broadcasts a data schema so LLMs can write accurate Logic-Injection-on-Origin code. |
|
|
222
|
+
| `setSandboxData()` | `(records: Record[])` | Injects data into the sandbox as `env.records` for Logic-on-Origin tools. |
|
|
223
|
+
| `enableZeroShotAutonomy()` | `()` | Registers the "Blind Analyst" prompt for autonomous code generation. |
|
|
224
|
+
| `callTool()` | `(request: CallToolRequest)` | Invokes a registered tool (used locally or via MCP Bridge). |
|
|
225
|
+
| `listTools()` | `()` | Returns all registered tools. |
|
|
226
|
+
| `listPrompts()` | `()` | Returns all registered prompts. |
|
|
227
|
+
| `getPrompt()` | `(request: GetPromptRequest)` | Returns a specific prompt by name. |
|
|
228
|
+
| `listResources()` | `()` | Returns all registered resources. |
|
|
229
|
+
| `readResource()` | `(uri: string)` | Reads a resource by URI. |
|
|
230
|
+
| `getServerInfo()` | `()` | Returns the server's name and version. |
|
|
231
|
+
| `connectToMesh()` | `()` | Connects to the libp2p Kademlia DHT. |
|
|
232
|
+
| `clearAstCache()` | `()` | Invalidates the Guardian AST logic cache. |
|
|
233
|
+
| `close()` | `()` | Destroys the worker pool and releases threads. |
|
|
218
234
|
|
|
219
235
|
### `LiopMcpBridge`
|
|
220
236
|
|
|
@@ -226,6 +242,7 @@ await bridge.connect();
|
|
|
226
242
|
```
|
|
227
243
|
|
|
228
244
|
**Supported JSON-RPC methods:**
|
|
245
|
+
|
|
229
246
|
- `initialize` — Returns server capabilities and info
|
|
230
247
|
- `tools/list` — Lists available tools
|
|
231
248
|
- `tools/call` — Calls a tool (with ZK-Receipt verification)
|
|
@@ -241,22 +258,31 @@ await bridge.connect();
|
|
|
241
258
|
### The Shield — Multi-Layer Defense
|
|
242
259
|
|
|
243
260
|
```
|
|
244
|
-
|
|
245
|
-
│ Layer 1: Guardian AST (Zero-Time Static Analysis)
|
|
246
|
-
│ Blocks: require, import(), fs, eval, fetch,
|
|
247
|
-
│
|
|
248
|
-
|
|
249
|
-
│ Layer 2: WASI Sandbox (V8 Isolate)
|
|
250
|
-
│
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
│
|
|
254
|
-
│
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
│
|
|
258
|
-
│
|
|
259
|
-
|
|
261
|
+
┌───────────────────────────────────────────────────────────┐
|
|
262
|
+
│ Layer 1: Guardian AST (Zero-Time Static Analysis) │
|
|
263
|
+
│ Blocks: require, import(), fs, eval, fetch, process, │
|
|
264
|
+
│ global, __proto__, XMLHttpRequest • 128 import cap │
|
|
265
|
+
├───────────────────────────────────────────────────────────┤
|
|
266
|
+
│ Layer 2: WASI Sandbox (V8 Isolate) │
|
|
267
|
+
│ 25 poisoned globals (incl. Date, TypedArrays) • │
|
|
268
|
+
│ CPU Fuel limits • 5s timeout • maxHeapMb (64MB default) │
|
|
269
|
+
├───────────────────────────────────────────────────────────┤
|
|
270
|
+
│ Layer 3: Prototype Pollution Defense │
|
|
271
|
+
│ Object.freeze() on 6 core prototypes (Object, Array, │
|
|
272
|
+
│ String, Number, Boolean, Function) inside sandbox IIFE │
|
|
273
|
+
├───────────────────────────────────────────────────────────┤
|
|
274
|
+
│ Layer 4: PII Shield (Egress Filter) │
|
|
275
|
+
│ Scans output for Email, SSN, Credit Card, IP, IBAN, │
|
|
276
|
+
│ Passport MRZ • Strips forbidden keys • NER opt-in │
|
|
277
|
+
├───────────────────────────────────────────────────────────┤
|
|
278
|
+
│ Layer 5: Aggregation-First Policy │
|
|
279
|
+
│ Blocks raw row export • maxOutputRows (default: 10) • │
|
|
280
|
+
│ Conditional error: detailed (dev) vs opaque (production) │
|
|
281
|
+
├───────────────────────────────────────────────────────────┤
|
|
282
|
+
│ Layer 6: ZK-Receipt (Integrity Verification) │
|
|
283
|
+
│ SHA-256 ImageID + HMAC-SHA256 Seal (Kyber768-derived) │
|
|
284
|
+
│ LiopMcpBridge verifies before forwarding to LLM │
|
|
285
|
+
└───────────────────────────────────────────────────────────┘
|
|
260
286
|
```
|
|
261
287
|
|
|
262
288
|
### PII Patterns
|
|
@@ -388,26 +414,27 @@ await server.connectToMesh();
|
|
|
388
414
|
|
|
389
415
|
This package is continuously tested across multiple platforms and Node.js versions via CI/CD:
|
|
390
416
|
|
|
391
|
-
- **
|
|
417
|
+
- **227+ tests** spanning unit, integration, conformance, adversarial, and crossnet suites
|
|
392
418
|
- **Multi-OS matrix:** Ubuntu, Windows, macOS
|
|
393
419
|
- **Node.js versions:** 22.x, 24.x
|
|
394
420
|
- **Code quality:** Enforced by [Biome.js](https://biomejs.dev/) (linting + formatting)
|
|
421
|
+
- **Security:** Verified defense-in-depth architecture — see [Security Architecture](https://nekzus-32.mintlify.app/typescript-sdk/security)
|
|
395
422
|
|
|
396
|
-
> To run tests locally or contribute, clone the [repository](https://github.com/Nekzus/
|
|
423
|
+
> To run tests locally or contribute, clone the [repository](https://github.com/Nekzus/LIOP) and follow the [Contributing Guide](https://github.com/Nekzus/LIOP/blob/main/CONTRIBUTING.md).
|
|
397
424
|
|
|
398
425
|
---
|
|
399
426
|
|
|
400
427
|
## Related
|
|
401
428
|
|
|
402
|
-
- [LIOP Documentation](https://
|
|
403
|
-
- [LIOP Specification](https://github.com/Nekzus/
|
|
404
|
-
- [LIOP Manifesto](https://github.com/Nekzus/
|
|
405
|
-
- [Contributing Guide](https://github.com/Nekzus/
|
|
406
|
-
- [Rust Mesh Node](https://github.com/Nekzus/
|
|
407
|
-
- [LIOP CLI](https://github.com/Nekzus/
|
|
429
|
+
- [LIOP Documentation](https://nekzus-32.mintlify.app/) — Full conceptual and API documentation
|
|
430
|
+
- [LIOP Specification](https://github.com/Nekzus/LIOP/blob/main/protocol/SPECIFICATION.md) — Technical specification
|
|
431
|
+
- [LIOP Manifesto](https://github.com/Nekzus/LIOP/blob/main/MANIFESTO.md) — Project philosophy
|
|
432
|
+
- [Contributing Guide](https://github.com/Nekzus/LIOP/blob/main/CONTRIBUTING.md) — How to contribute
|
|
433
|
+
- [Rust Mesh Node](https://github.com/Nekzus/LIOP/tree/main/servers/liop-node) — Native high-performance backend
|
|
434
|
+
- [LIOP CLI](https://github.com/Nekzus/LIOP/tree/main/tools/liop-cli) — Developer diagnostics
|
|
408
435
|
|
|
409
436
|
---
|
|
410
437
|
|
|
411
438
|
## License
|
|
412
439
|
|
|
413
|
-
[MIT](https://github.com/Nekzus/
|
|
440
|
+
[MIT](https://github.com/Nekzus/LIOP/blob/main/LICENSE) © [Nekzus](https://github.com/Nekzus)
|
package/dist/bridge/stream.js
CHANGED
|
@@ -120,16 +120,24 @@ export class LiopStreamBridge {
|
|
|
120
120
|
}
|
|
121
121
|
setupRoutes() {
|
|
122
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
|
+
}
|
|
123
133
|
// ZTA (Zero-Trust Architecture) Security Middleware
|
|
124
134
|
this.app.use("/mcp", async (c, next) => {
|
|
125
135
|
const auth = c.req.header("Authorization");
|
|
126
136
|
const expectedToken = process.env.ZERO_TRUST_TOKEN;
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return c.json({ error: "Unauthorized: LIOP Zero-Trust Policy Enforced" }, 401);
|
|
132
|
-
}
|
|
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);
|
|
133
141
|
}
|
|
134
142
|
await next();
|
|
135
143
|
});
|
package/dist/client/index.js
CHANGED
|
@@ -147,19 +147,19 @@ export class LiopClient {
|
|
|
147
147
|
const _safePayload = _wasmPayload || Buffer.from("");
|
|
148
148
|
// Encrypt WASM binary
|
|
149
149
|
const { ciphertext: encryptedWasm, nonce: aesNonce } = AesGcmWrapper.encryptPayload(_safePayload, sharedSecret);
|
|
150
|
-
// Encrypt inputs using
|
|
150
|
+
// Encrypt inputs using a fresh random nonce per input to prevent AES-GCM nonce reuse
|
|
151
151
|
const encryptedInputs = {};
|
|
152
|
+
const crypto = await import("node:crypto");
|
|
152
153
|
for (const [key, value] of Object.entries(request.arguments || {})) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const crypto = await import("node:crypto");
|
|
156
|
-
const cipher = crypto.createCipheriv("aes-256-gcm", sharedSecret, aesNonce);
|
|
154
|
+
const inputNonce = crypto.randomBytes(12);
|
|
155
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", sharedSecret, inputNonce);
|
|
157
156
|
const encrypted = Buffer.concat([
|
|
158
157
|
cipher.update(JSON.stringify(value)),
|
|
159
158
|
cipher.final(),
|
|
160
159
|
]);
|
|
161
160
|
const authTag = cipher.getAuthTag();
|
|
162
|
-
|
|
161
|
+
// Prepend the 12-byte nonce to the ciphertext
|
|
162
|
+
encryptedInputs[key] = Buffer.concat([inputNonce, encrypted, authTag]);
|
|
163
163
|
}
|
|
164
164
|
// 4. Assemble and Execute gRPC LogicRequest
|
|
165
165
|
const logicRequest = {
|
|
@@ -183,7 +183,7 @@ export class LiopClient {
|
|
|
183
183
|
hasReceivedData = true;
|
|
184
184
|
log.info("[LiopClient] Logic Executed. Verification in progress...");
|
|
185
185
|
try {
|
|
186
|
-
const isValid = await this.verifier.verifyZkReceipt(_safePayload, Buffer.from(response.cryptographic_proof).toString("hex"), Buffer.from(response.zk_receipt));
|
|
186
|
+
const isValid = await this.verifier.verifyZkReceipt(_safePayload, Buffer.from(response.cryptographic_proof).toString("hex"), Buffer.from(response.zk_receipt), Buffer.from(sharedSecret));
|
|
187
187
|
if (!isValid) {
|
|
188
188
|
reject(new Error("PROTOCOL INTEGRITY VIOLATION: ZK-Receipt verification failed."));
|
|
189
189
|
return;
|
|
@@ -15,7 +15,7 @@ export declare class LiopVerifier {
|
|
|
15
15
|
* @param remoteImageIdHex The ImageID reported by the provider (must match our local calculation).
|
|
16
16
|
* @param zkReceipt The mathematical proof (Seal + Journal) from the zkVM.
|
|
17
17
|
*/
|
|
18
|
-
verifyZkReceipt(logicPayload: Buffer, remoteImageIdHex: string, zkReceipt: Buffer): Promise<boolean>;
|
|
18
|
+
verifyZkReceipt(logicPayload: Buffer, remoteImageIdHex: string, zkReceipt: Buffer, sessionSecret?: Buffer): Promise<boolean>;
|
|
19
19
|
/**
|
|
20
20
|
* Verifies if a node is running inside an authenticated TEE (e.g. AWS Nitro).
|
|
21
21
|
*
|
package/dist/crypto/verifier.js
CHANGED
|
@@ -49,7 +49,7 @@ export class LiopVerifier {
|
|
|
49
49
|
* @param remoteImageIdHex The ImageID reported by the provider (must match our local calculation).
|
|
50
50
|
* @param zkReceipt The mathematical proof (Seal + Journal) from the zkVM.
|
|
51
51
|
*/
|
|
52
|
-
async verifyZkReceipt(logicPayload, remoteImageIdHex, zkReceipt) {
|
|
52
|
+
async verifyZkReceipt(logicPayload, remoteImageIdHex, zkReceipt, sessionSecret) {
|
|
53
53
|
const pool = this.getZkPool();
|
|
54
54
|
if (!pool)
|
|
55
55
|
throw new Error("Worker pool initialization failed");
|
|
@@ -58,6 +58,7 @@ export class LiopVerifier {
|
|
|
58
58
|
logicPayload: new Uint8Array(logicPayload),
|
|
59
59
|
remoteImageIdHex,
|
|
60
60
|
zkReceipt: new Uint8Array(zkReceipt),
|
|
61
|
+
sessionSecret: sessionSecret ? new Uint8Array(sessionSecret) : undefined,
|
|
61
62
|
});
|
|
62
63
|
if (result.verified) {
|
|
63
64
|
log.info(`[LiopVerifier] ${result.message}`);
|
package/dist/gateway/router.d.ts
CHANGED
|
@@ -62,6 +62,13 @@ export declare class LiopMcpRouter {
|
|
|
62
62
|
* by searching the manifest cache. Supports exact names and suffixed names.
|
|
63
63
|
*/
|
|
64
64
|
private resolveManifestTarget;
|
|
65
|
+
/**
|
|
66
|
+
* Redacts a PeerID for external-facing diagnostics.
|
|
67
|
+
* LIOP_DIAGNOSTIC_LEVEL controls verbosity:
|
|
68
|
+
* - "redacted" (default): truncated to last 8 chars
|
|
69
|
+
* - "full": complete PeerID (development only)
|
|
70
|
+
*/
|
|
71
|
+
private redactPeerId;
|
|
65
72
|
private transcodeMcpToLiop;
|
|
66
73
|
private routeToRemoteProvider;
|
|
67
74
|
private performTranscoding;
|
package/dist/gateway/router.js
CHANGED
|
@@ -72,6 +72,11 @@ export class LiopMcpRouter {
|
|
|
72
72
|
log.info(`[LIOP-Router] Failed to announce manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
+
// [OWASP-A01] Startup warning when diagnostic level exposes full topology
|
|
76
|
+
if (process.env.LIOP_DIAGNOSTIC_LEVEL === "full") {
|
|
77
|
+
process.stderr.write("⚠️ [LIOP-Security] Diagnostic level set to FULL — " +
|
|
78
|
+
"PeerIDs and network topology are exposed. Do NOT use in production.\n");
|
|
79
|
+
}
|
|
75
80
|
}
|
|
76
81
|
shouldSkipManifestQuery(peerId) {
|
|
77
82
|
const state = this.manifestFailureState.get(peerId);
|
|
@@ -691,6 +696,18 @@ export class LiopMcpRouter {
|
|
|
691
696
|
}
|
|
692
697
|
return null;
|
|
693
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
* Redacts a PeerID for external-facing diagnostics.
|
|
701
|
+
* LIOP_DIAGNOSTIC_LEVEL controls verbosity:
|
|
702
|
+
* - "redacted" (default): truncated to last 8 chars
|
|
703
|
+
* - "full": complete PeerID (development only)
|
|
704
|
+
*/
|
|
705
|
+
redactPeerId(peerId) {
|
|
706
|
+
const level = process.env.LIOP_DIAGNOSTIC_LEVEL || "redacted";
|
|
707
|
+
if (level === "full")
|
|
708
|
+
return peerId;
|
|
709
|
+
return `***${peerId.slice(-8)}`;
|
|
710
|
+
}
|
|
694
711
|
// biome-ignore lint/suspicious/noExplicitAny: MCP JSON-RPC params/id are polymorphic
|
|
695
712
|
async transcodeMcpToLiop(id, params) {
|
|
696
713
|
const toolName = params.name;
|
|
@@ -724,16 +741,17 @@ export class LiopMcpRouter {
|
|
|
724
741
|
.map((addr) => {
|
|
725
742
|
const parts = addr.split("/");
|
|
726
743
|
const id = parts[parts.length - 1];
|
|
727
|
-
return ` • ${id ? id.slice(-8) : "Unknown"} (
|
|
744
|
+
return ` • ${id ? id.slice(-8) : "Unknown"} (bootstrap)`;
|
|
728
745
|
})
|
|
729
746
|
.join("\n");
|
|
730
747
|
const routingTableSize = this.meshNode
|
|
731
748
|
? // biome-ignore lint/suspicious/noExplicitAny: access internal nodes
|
|
732
749
|
this.meshNode.getRoutingTableSize()
|
|
733
750
|
: 0;
|
|
734
|
-
const
|
|
751
|
+
const rawPeerId = this.meshNode?.getPeerId() || "Offline";
|
|
752
|
+
const localPeerId = rawPeerId === "Offline" ? rawPeerId : this.redactPeerId(rawPeerId);
|
|
735
753
|
const cachedToolList = Array.from(this.manifestCache.entries())
|
|
736
|
-
.flatMap(([peerId, { manifest }]) => manifest.tools.map((t) => ` • ${t.name} (from origin: ${peerId})`))
|
|
754
|
+
.flatMap(([peerId, { manifest }]) => manifest.tools.map((t) => ` • ${t.name} (from origin: ${this.redactPeerId(peerId)})`))
|
|
737
755
|
.join("\n");
|
|
738
756
|
const statusText = [
|
|
739
757
|
`LIOP Mesh Status: ${meshState === "Active" ? "Active" : "Offline"}`,
|
package/dist/sandbox/guardian.js
CHANGED
|
@@ -20,13 +20,36 @@ export const ASTGuardian = {
|
|
|
20
20
|
analyze(module) {
|
|
21
21
|
const imports = WebAssembly.Module.imports(module);
|
|
22
22
|
let _importCount = 0;
|
|
23
|
+
const ALLOWED_WASI_FUNCTIONS = new Set([
|
|
24
|
+
"fd_write",
|
|
25
|
+
"fd_read",
|
|
26
|
+
"fd_close",
|
|
27
|
+
"fd_seek",
|
|
28
|
+
"environ_get",
|
|
29
|
+
"environ_sizes_get",
|
|
30
|
+
"args_get",
|
|
31
|
+
"args_sizes_get",
|
|
32
|
+
"clock_time_get",
|
|
33
|
+
"random_get",
|
|
34
|
+
"proc_exit",
|
|
35
|
+
"fd_prestat_get",
|
|
36
|
+
"fd_prestat_dir_name",
|
|
37
|
+
"fd_fdstat_get",
|
|
38
|
+
]);
|
|
23
39
|
for (const imp of imports) {
|
|
24
|
-
// Strict Sandbox Validation: Only allow WASI preview 1
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
// Strict Sandbox Validation: Only allow WASI preview 1 specific whitelisted functions.
|
|
41
|
+
if (imp.module === "wasi_snapshot_preview1") {
|
|
42
|
+
if (!ALLOWED_WASI_FUNCTIONS.has(imp.name)) {
|
|
43
|
+
throw new GuardianError(`Banned WASI Import Detected: ${imp.module}/${imp.name}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new GuardianError(`Banned Host Import Module Detected: ${imp.module}`);
|
|
28
48
|
}
|
|
29
49
|
_importCount++;
|
|
50
|
+
if (_importCount > 128) {
|
|
51
|
+
throw new GuardianError("Import limit exceeded. Possible resource exhaustion attack.");
|
|
52
|
+
}
|
|
30
53
|
}
|
|
31
54
|
// In Node.js / V8, the maximum module size and function limits
|
|
32
55
|
// are natively enforced by the engine during compilation.
|
package/dist/sandbox/wasi.js
CHANGED
|
@@ -116,6 +116,24 @@ export class WasiSandbox {
|
|
|
116
116
|
sandboxEnv.queueMicrotask = undefined;
|
|
117
117
|
sandboxEnv.eval = undefined;
|
|
118
118
|
sandboxEnv.Function = undefined;
|
|
119
|
+
sandboxEnv.SharedArrayBuffer = undefined;
|
|
120
|
+
sandboxEnv.Date = undefined;
|
|
121
|
+
// [DoS Defense] Block off-heap memory allocation vectors.
|
|
122
|
+
// Logic-on-Origin operates on JSON data (env.records) — binary buffers
|
|
123
|
+
// serve no legitimate purpose and enable memory exhaustion DoS.
|
|
124
|
+
// (Uint8Array(2GB) bypassed Piscina's maxOldGenerationSizeMb limit)
|
|
125
|
+
sandboxEnv.ArrayBuffer = undefined;
|
|
126
|
+
sandboxEnv.Uint8Array = undefined;
|
|
127
|
+
sandboxEnv.Int8Array = undefined;
|
|
128
|
+
sandboxEnv.Uint16Array = undefined;
|
|
129
|
+
sandboxEnv.Int16Array = undefined;
|
|
130
|
+
sandboxEnv.Uint32Array = undefined;
|
|
131
|
+
sandboxEnv.Int32Array = undefined;
|
|
132
|
+
sandboxEnv.Float32Array = undefined;
|
|
133
|
+
sandboxEnv.Float64Array = undefined;
|
|
134
|
+
sandboxEnv.BigInt64Array = undefined;
|
|
135
|
+
sandboxEnv.BigUint64Array = undefined;
|
|
136
|
+
sandboxEnv.DataView = undefined;
|
|
119
137
|
// Inject strictly monitored globals
|
|
120
138
|
sandboxEnv.records = JSON.parse(JSON.stringify(records)); // Deep copy safety
|
|
121
139
|
sandboxEnv.env = JSON.parse(JSON.stringify(env));
|
|
@@ -154,6 +172,13 @@ export class WasiSandbox {
|
|
|
154
172
|
const scriptCode = `
|
|
155
173
|
(function() {
|
|
156
174
|
try {
|
|
175
|
+
Object.freeze(Object.prototype);
|
|
176
|
+
Object.freeze(Array.prototype);
|
|
177
|
+
Object.freeze(String.prototype);
|
|
178
|
+
Object.freeze(Number.prototype);
|
|
179
|
+
Object.freeze(Boolean.prototype);
|
|
180
|
+
Object.freeze(Object.getPrototypeOf(function(){}));
|
|
181
|
+
|
|
157
182
|
${processedLogic}
|
|
158
183
|
if (typeof liop_main === 'function') {
|
|
159
184
|
return liop_main(env);
|
package/dist/security/zk.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare const ZkVerifier: {
|
|
|
26
26
|
* @throws ZkVerificationError if the proof is invalid or image IDs mismatch
|
|
27
27
|
* @returns true if the proof mathematically verifies the execution
|
|
28
28
|
*/
|
|
29
|
-
verify(receipt: ZkReceipt, expectedImageId: Buffer): boolean;
|
|
29
|
+
verify(receipt: ZkReceipt, expectedImageId: Buffer, sessionSecret?: Buffer): boolean;
|
|
30
30
|
/**
|
|
31
31
|
* Derives a predictable ImageID (usually a Hash) from a raw WASM binary.
|
|
32
32
|
*
|
package/dist/security/zk.js
CHANGED
|
@@ -22,7 +22,7 @@ export const ZkVerifier = {
|
|
|
22
22
|
* @throws ZkVerificationError if the proof is invalid or image IDs mismatch
|
|
23
23
|
* @returns true if the proof mathematically verifies the execution
|
|
24
24
|
*/
|
|
25
|
-
verify(receipt, expectedImageId) {
|
|
25
|
+
verify(receipt, expectedImageId, sessionSecret) {
|
|
26
26
|
// 1. Verify Image ID (Ensures the host executed the exact logic we sent, not a malicious one)
|
|
27
27
|
if (!receipt.imageId.equals(expectedImageId)) {
|
|
28
28
|
throw new ZkVerificationError("ImageID mismatch. The remote origin executed a different WASM payload.");
|
|
@@ -52,6 +52,16 @@ export const ZkVerifier = {
|
|
|
52
52
|
catch (_e) {
|
|
53
53
|
throw new ZkVerificationError("Failed to parse journal data.");
|
|
54
54
|
}
|
|
55
|
+
// 4. Mathematical Verification (HMAC-SHA256)
|
|
56
|
+
if (sessionSecret && sessionSecret.length > 0) {
|
|
57
|
+
const expectedSeal = crypto
|
|
58
|
+
.createHmac("sha256", sessionSecret)
|
|
59
|
+
.update(journal)
|
|
60
|
+
.digest();
|
|
61
|
+
if (!crypto.timingSafeEqual(seal, expectedSeal)) {
|
|
62
|
+
throw new ZkVerificationError("Invalid seal: HMAC verification failed.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
55
65
|
return true;
|
|
56
66
|
},
|
|
57
67
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { MeshNode } from "../mesh/node.js";
|
|
3
3
|
import type { CallToolRequest, CallToolResult, GetPromptRequest, GetPromptResult, Prompt, Resource, ServerInfo, Tool } from "../types.js";
|
|
4
|
+
import { NerScanner } from "./ner-scanner.js";
|
|
4
5
|
import { PII_PATTERNS, PII_PRESETS, type PiiRule, PiiScanner } from "./pii.js";
|
|
5
|
-
export { PII_PATTERNS, PII_PRESETS, type PiiRule, PiiScanner };
|
|
6
|
+
export { NerScanner, PII_PATTERNS, PII_PRESETS, type PiiRule, PiiScanner };
|
|
6
7
|
export type ToolHandler<T extends z.ZodRawShape = z.ZodRawShape> = (args: z.infer<z.ZodObject<T>>, extra: {
|
|
7
8
|
signal?: AbortSignal;
|
|
8
9
|
}) => Promise<CallToolResult>;
|
|
@@ -13,10 +14,21 @@ export interface LiopServerOptions {
|
|
|
13
14
|
minThreads?: number;
|
|
14
15
|
maxThreads?: number;
|
|
15
16
|
idleTimeout?: number;
|
|
17
|
+
/** Max heap memory per worker in MB (default: 64). Prevents heap bomb DoS. */
|
|
18
|
+
maxHeapMb?: number;
|
|
16
19
|
};
|
|
17
20
|
security?: {
|
|
18
21
|
piiPatterns?: PiiRule[];
|
|
19
22
|
forbiddenKeys?: string[];
|
|
23
|
+
/** Enable NLP-based Named Entity Recognition scanning on output values. */
|
|
24
|
+
enableNerScanning?: boolean;
|
|
25
|
+
/** Rate limiting configuration for tool calls (OWASP A01). */
|
|
26
|
+
rateLimit?: {
|
|
27
|
+
/** Maximum calls per window per tool (default: 30). */
|
|
28
|
+
maxPerWindow?: number;
|
|
29
|
+
/** Sliding window duration in milliseconds (default: 60000 = 1 min). */
|
|
30
|
+
windowMs?: number;
|
|
31
|
+
};
|
|
20
32
|
};
|
|
21
33
|
taxonomy?: {
|
|
22
34
|
domain?: string;
|
|
@@ -53,6 +65,9 @@ export declare class LiopServer {
|
|
|
53
65
|
private readonly CACHE_TTL_MS;
|
|
54
66
|
private readonly THROTTLE_THRESHOLD;
|
|
55
67
|
private readonly THROTTLE_COOLDOWN_MS;
|
|
68
|
+
private toolCallWindows;
|
|
69
|
+
private readonly toolCallMaxPerWindow;
|
|
70
|
+
private readonly toolCallWindowMs;
|
|
56
71
|
private tools;
|
|
57
72
|
private resources;
|
|
58
73
|
private prompts;
|
|
@@ -128,6 +143,13 @@ export declare class LiopServer {
|
|
|
128
143
|
* Manually invalidates the AST Logic Cache (e.g. for Zero-Day patches).
|
|
129
144
|
*/
|
|
130
145
|
clearAstCache(): void;
|
|
146
|
+
/**
|
|
147
|
+
* Sliding window rate limiter for tool call frequency.
|
|
148
|
+
* Prevents micro-query exfiltration attacks where an attacker
|
|
149
|
+
* makes hundreds of individually-legitimate calls to reconstruct
|
|
150
|
+
* the full dataset field by field. (OWASP A01)
|
|
151
|
+
*/
|
|
152
|
+
private checkToolCallRateLimit;
|
|
131
153
|
/**
|
|
132
154
|
* Emulates calling a tool (used locally or via LIOPMcpBridge)
|
|
133
155
|
*/
|