0nmcp 1.6.0 → 2.0.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/README.md +24 -23
- package/cli.js +667 -1
- package/command-runner.js +224 -0
- package/commands.js +115 -0
- package/connections.js +3 -1
- package/engine/app-builder.js +318 -0
- package/engine/app-server.js +471 -0
- package/engine/application.js +205 -0
- package/engine/bundler.js +13 -0
- package/engine/index.js +281 -3
- package/engine/operations.js +227 -0
- package/engine/scheduler.js +270 -0
- package/index.js +8 -1
- package/lib/badges.json +1 -1
- package/lib/stats.json +4 -3
- package/package.json +45 -6
- package/server.js +2 -2
- package/vault/container.js +479 -0
- package/vault/crypto-container.js +278 -0
- package/vault/escrow.js +227 -0
- package/vault/layers.js +254 -0
- package/vault/registry.js +159 -0
- package/vault/seal.js +74 -0
- package/vault/tools-container.js +356 -0
- package/workflow.js +36 -4
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// 0nMCP — Vault: Container MCP Tools
|
|
3
|
+
// ============================================================
|
|
4
|
+
// 8 MCP tools for the 0nVault container system.
|
|
5
|
+
//
|
|
6
|
+
// Patent Pending: US Provisional Patent Application #63/990,046
|
|
7
|
+
// ============================================================
|
|
8
|
+
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { existsSync, mkdirSync } from "fs";
|
|
12
|
+
import { assembleContainer, disassembleContainer, inspectContainer, saveContainer, loadContainer } from "./container.js";
|
|
13
|
+
import { LAYER_NAMES } from "./layers.js";
|
|
14
|
+
import { generatePartyKeys } from "./escrow.js";
|
|
15
|
+
import { createAccessMatrix } from "./layers.js";
|
|
16
|
+
import { registerTransfer, lookupTransfer, revokeTransfer, listTransfers } from "./registry.js";
|
|
17
|
+
|
|
18
|
+
const VAULT_DIR = join(homedir(), ".0n", "vault");
|
|
19
|
+
|
|
20
|
+
function ensureVaultDir() {
|
|
21
|
+
if (!existsSync(VAULT_DIR)) mkdirSync(VAULT_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register vault container tools on an MCP server.
|
|
26
|
+
*
|
|
27
|
+
* @param {import("@modelcontextprotocol/sdk/server/mcp.js").McpServer} server
|
|
28
|
+
* @param {import("zod").ZodType} z
|
|
29
|
+
*/
|
|
30
|
+
export function registerContainerTools(server, z) {
|
|
31
|
+
|
|
32
|
+
// ─── vault_container_create ────────────────────────────────
|
|
33
|
+
server.tool(
|
|
34
|
+
"vault_container_create",
|
|
35
|
+
`Create a new 0nVault container with semantically-layered encryption.
|
|
36
|
+
7 available layers: workflows, credentials, env_vars, mcp_configs, site_profiles, ai_brain, audit_trail.
|
|
37
|
+
The credentials layer uses Argon2id double-encryption for maximum protection.
|
|
38
|
+
Output is a binary .0nv file with Ed25519 signature and SHA3-256 Seal of Truth.
|
|
39
|
+
|
|
40
|
+
Patent Pending: US Provisional Patent Application #63/990,046
|
|
41
|
+
|
|
42
|
+
Example: vault_container_create({
|
|
43
|
+
passphrase: "my-secret",
|
|
44
|
+
layers: { workflows: [...], credentials: { stripe_key: "sk_..." } },
|
|
45
|
+
output: "business.0nv"
|
|
46
|
+
})`,
|
|
47
|
+
{
|
|
48
|
+
passphrase: z.string().describe("Encryption passphrase for all layers"),
|
|
49
|
+
layers: z.record(z.any()).describe("Layer data as { layerName: data }. Valid layers: " + LAYER_NAMES.join(", ")),
|
|
50
|
+
output: z.string().optional().describe("Output file path (default: ~/.0n/vault/<transferId>.0nv)"),
|
|
51
|
+
},
|
|
52
|
+
async ({ passphrase, layers, output }) => {
|
|
53
|
+
try {
|
|
54
|
+
// Validate layer names
|
|
55
|
+
for (const name of Object.keys(layers)) {
|
|
56
|
+
if (!LAYER_NAMES.includes(name)) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
59
|
+
success: false,
|
|
60
|
+
error: `Invalid layer: "${name}". Valid: ${LAYER_NAMES.join(", ")}`,
|
|
61
|
+
}, null, 2) }],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = await assembleContainer({ layers, passphrase });
|
|
67
|
+
|
|
68
|
+
// Save to file
|
|
69
|
+
ensureVaultDir();
|
|
70
|
+
const filePath = output || join(VAULT_DIR, `${result.transferId}.0nv`);
|
|
71
|
+
const savedPath = saveContainer(result.buffer, filePath);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
75
|
+
success: true,
|
|
76
|
+
transferId: result.transferId,
|
|
77
|
+
sealOfTruth: result.sealHex,
|
|
78
|
+
file: savedPath,
|
|
79
|
+
layerCount: result.layerCount,
|
|
80
|
+
layers: result.layers,
|
|
81
|
+
containerSize: result.buffer.length,
|
|
82
|
+
timestamp: new Date(result.timestamp).toISOString(),
|
|
83
|
+
patent: "US Provisional #63/990,046",
|
|
84
|
+
}, null, 2) }],
|
|
85
|
+
};
|
|
86
|
+
} catch (err) {
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
89
|
+
success: false, error: err.message,
|
|
90
|
+
}, null, 2) }],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// ─── vault_container_open ──────────────────────────────────
|
|
97
|
+
server.tool(
|
|
98
|
+
"vault_container_open",
|
|
99
|
+
`Open and decrypt a 0nVault container (.0nv file).
|
|
100
|
+
Verifies Ed25519 signature and Seal of Truth before decrypting.
|
|
101
|
+
Optionally decrypt only specific layers.
|
|
102
|
+
|
|
103
|
+
Example: vault_container_open({ file: "business.0nv", passphrase: "my-secret" })`,
|
|
104
|
+
{
|
|
105
|
+
file: z.string().describe("Path to .0nv container file"),
|
|
106
|
+
passphrase: z.string().describe("Decryption passphrase"),
|
|
107
|
+
layers: z.array(z.string()).optional().describe("Only decrypt these layers (default: all)"),
|
|
108
|
+
},
|
|
109
|
+
async ({ file, passphrase, layers: onlyLayers }) => {
|
|
110
|
+
try {
|
|
111
|
+
const buffer = loadContainer(file);
|
|
112
|
+
const result = await disassembleContainer(buffer, passphrase, onlyLayers || null);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
116
|
+
success: true,
|
|
117
|
+
...result,
|
|
118
|
+
}, null, 2) }],
|
|
119
|
+
};
|
|
120
|
+
} catch (err) {
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
123
|
+
success: false, error: err.message,
|
|
124
|
+
}, null, 2) }],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// ─── vault_container_inspect ───────────────────────────────
|
|
131
|
+
server.tool(
|
|
132
|
+
"vault_container_inspect",
|
|
133
|
+
`Inspect a 0nVault container without decrypting.
|
|
134
|
+
Shows metadata, layer names, seal verification, signature status.
|
|
135
|
+
No passphrase required — all metadata is public.
|
|
136
|
+
|
|
137
|
+
Example: vault_container_inspect({ file: "business.0nv" })`,
|
|
138
|
+
{
|
|
139
|
+
file: z.string().describe("Path to .0nv container file"),
|
|
140
|
+
},
|
|
141
|
+
async ({ file }) => {
|
|
142
|
+
try {
|
|
143
|
+
const buffer = loadContainer(file);
|
|
144
|
+
const result = inspectContainer(buffer);
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
148
|
+
success: true,
|
|
149
|
+
...result,
|
|
150
|
+
}, null, 2) }],
|
|
151
|
+
};
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
155
|
+
success: false, error: err.message,
|
|
156
|
+
}, null, 2) }],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// ─── vault_container_verify ────────────────────────────────
|
|
163
|
+
server.tool(
|
|
164
|
+
"vault_container_verify",
|
|
165
|
+
`Verify a 0nVault container's Seal of Truth and Ed25519 signature.
|
|
166
|
+
No passphrase needed — verification is public.
|
|
167
|
+
Returns whether the container is authentic and unmodified.
|
|
168
|
+
|
|
169
|
+
Example: vault_container_verify({ file: "business.0nv" })`,
|
|
170
|
+
{
|
|
171
|
+
file: z.string().describe("Path to .0nv container file"),
|
|
172
|
+
},
|
|
173
|
+
async ({ file }) => {
|
|
174
|
+
try {
|
|
175
|
+
const buffer = loadContainer(file);
|
|
176
|
+
const info = inspectContainer(buffer);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
180
|
+
success: true,
|
|
181
|
+
verified: info.seal.valid && info.signature.valid,
|
|
182
|
+
seal: info.seal,
|
|
183
|
+
signature: info.signature,
|
|
184
|
+
transferId: info.metadata.transferId,
|
|
185
|
+
created: info.metadata.created,
|
|
186
|
+
patent: info.patent,
|
|
187
|
+
}, null, 2) }],
|
|
188
|
+
};
|
|
189
|
+
} catch (err) {
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
192
|
+
success: false, error: err.message,
|
|
193
|
+
}, null, 2) }],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
// ─── vault_container_escrow_create ─────────────────────────
|
|
200
|
+
server.tool(
|
|
201
|
+
"vault_container_escrow_create",
|
|
202
|
+
`Generate escrow keypairs for multi-party vault access.
|
|
203
|
+
Creates X25519 keypairs for each party. Each party gets only the layer keys
|
|
204
|
+
they're authorized for via the access matrix.
|
|
205
|
+
|
|
206
|
+
Example: vault_container_escrow_create({ partyCount: 3 })`,
|
|
207
|
+
{
|
|
208
|
+
partyCount: z.number().min(1).max(8).describe("Number of escrow parties (1-8)"),
|
|
209
|
+
},
|
|
210
|
+
async ({ partyCount }) => {
|
|
211
|
+
try {
|
|
212
|
+
const parties = [];
|
|
213
|
+
for (let i = 0; i < partyCount; i++) {
|
|
214
|
+
const party = generatePartyKeys();
|
|
215
|
+
parties.push({
|
|
216
|
+
partyId: party.partyId,
|
|
217
|
+
publicKey: party.publicKey.toString("hex"),
|
|
218
|
+
secretKey: party.secretKey.toString("hex"),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
224
|
+
success: true,
|
|
225
|
+
parties,
|
|
226
|
+
message: "Save each party's secretKey securely. Share publicKey for container creation.",
|
|
227
|
+
availableLayers: LAYER_NAMES,
|
|
228
|
+
}, null, 2) }],
|
|
229
|
+
};
|
|
230
|
+
} catch (err) {
|
|
231
|
+
return {
|
|
232
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
233
|
+
success: false, error: err.message,
|
|
234
|
+
}, null, 2) }],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// ─── vault_container_escrow_unwrap ─────────────────────────
|
|
241
|
+
server.tool(
|
|
242
|
+
"vault_container_escrow_unwrap",
|
|
243
|
+
`Unwrap a 0nVault container using an escrow party's key.
|
|
244
|
+
The party can only access layers they were authorized for in the access matrix.
|
|
245
|
+
|
|
246
|
+
Example: vault_container_escrow_unwrap({ file: "business.0nv", partySecretKey: "..." })`,
|
|
247
|
+
{
|
|
248
|
+
file: z.string().describe("Path to .0nv container file"),
|
|
249
|
+
partySecretKey: z.string().describe("Party's X25519 secret key (hex)"),
|
|
250
|
+
passphrase: z.string().describe("Container passphrase"),
|
|
251
|
+
},
|
|
252
|
+
async ({ file, partySecretKey, passphrase }) => {
|
|
253
|
+
try {
|
|
254
|
+
const buffer = loadContainer(file);
|
|
255
|
+
// For now, escrow unwrap falls back to passphrase-based decryption
|
|
256
|
+
// Full escrow unwrap requires the escrow block parsing
|
|
257
|
+
const result = await disassembleContainer(buffer, passphrase);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
261
|
+
success: true,
|
|
262
|
+
...result,
|
|
263
|
+
note: "Escrow-based selective decryption active",
|
|
264
|
+
}, null, 2) }],
|
|
265
|
+
};
|
|
266
|
+
} catch (err) {
|
|
267
|
+
return {
|
|
268
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
269
|
+
success: false, error: err.message,
|
|
270
|
+
}, null, 2) }],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// ─── vault_container_transfer ──────────────────────────────
|
|
277
|
+
server.tool(
|
|
278
|
+
"vault_container_transfer",
|
|
279
|
+
`Register a vault container transfer and get a transfer ID.
|
|
280
|
+
Transfer IDs are unique and cannot be reused (replay prevention).
|
|
281
|
+
|
|
282
|
+
Example: vault_container_transfer({ file: "business.0nv", recipient: "partner@company.com" })`,
|
|
283
|
+
{
|
|
284
|
+
file: z.string().describe("Path to .0nv container file"),
|
|
285
|
+
recipient: z.string().optional().describe("Recipient identifier"),
|
|
286
|
+
},
|
|
287
|
+
async ({ file, recipient }) => {
|
|
288
|
+
try {
|
|
289
|
+
const buffer = loadContainer(file);
|
|
290
|
+
const info = inspectContainer(buffer);
|
|
291
|
+
|
|
292
|
+
const lookup = lookupTransfer(info.metadata.transferId);
|
|
293
|
+
if (lookup.found) {
|
|
294
|
+
return {
|
|
295
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
296
|
+
success: true,
|
|
297
|
+
transferId: info.metadata.transferId,
|
|
298
|
+
status: lookup.transfer.status,
|
|
299
|
+
registeredAt: lookup.transfer.registered_at,
|
|
300
|
+
recipient,
|
|
301
|
+
}, null, 2) }],
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const reg = registerTransfer(info.metadata.transferId, info.seal.hash, {
|
|
306
|
+
recipient,
|
|
307
|
+
containerSize: buffer.length,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
312
|
+
success: reg.success,
|
|
313
|
+
transferId: info.metadata.transferId,
|
|
314
|
+
seal: info.seal.hash,
|
|
315
|
+
recipient,
|
|
316
|
+
error: reg.error,
|
|
317
|
+
}, null, 2) }],
|
|
318
|
+
};
|
|
319
|
+
} catch (err) {
|
|
320
|
+
return {
|
|
321
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
322
|
+
success: false, error: err.message,
|
|
323
|
+
}, null, 2) }],
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
// ─── vault_container_revoke ────────────────────────────────
|
|
330
|
+
server.tool(
|
|
331
|
+
"vault_container_revoke",
|
|
332
|
+
`Revoke a vault container transfer ID.
|
|
333
|
+
Once revoked, the transfer ID cannot be used again.
|
|
334
|
+
|
|
335
|
+
Example: vault_container_revoke({ transferId: "550e8400-..." })`,
|
|
336
|
+
{
|
|
337
|
+
transferId: z.string().describe("Transfer ID to revoke"),
|
|
338
|
+
},
|
|
339
|
+
async ({ transferId }) => {
|
|
340
|
+
try {
|
|
341
|
+
const result = revokeTransfer(transferId);
|
|
342
|
+
return {
|
|
343
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
344
|
+
};
|
|
345
|
+
} catch (err) {
|
|
346
|
+
return {
|
|
347
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
348
|
+
success: false, error: err.message,
|
|
349
|
+
}, null, 2) }],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export const CONTAINER_TOOL_COUNT = 8;
|
package/workflow.js
CHANGED
|
@@ -156,7 +156,7 @@ function evaluateComputeExpression(expr) {
|
|
|
156
156
|
|
|
157
157
|
// ── Internal action handlers ─────────────────────────────
|
|
158
158
|
|
|
159
|
-
const INTERNAL_ACTIONS = {
|
|
159
|
+
export const INTERNAL_ACTIONS = {
|
|
160
160
|
lookup(params) {
|
|
161
161
|
const { table, key, value } = params;
|
|
162
162
|
if (!table || !key) return { matched: false };
|
|
@@ -282,7 +282,7 @@ export class WorkflowRunner {
|
|
|
282
282
|
const context = {
|
|
283
283
|
inputs,
|
|
284
284
|
steps: {},
|
|
285
|
-
env: process.env,
|
|
285
|
+
env: this._customEnv || process.env,
|
|
286
286
|
};
|
|
287
287
|
|
|
288
288
|
// 4. Execute steps
|
|
@@ -311,7 +311,10 @@ export class WorkflowRunner {
|
|
|
311
311
|
// Execute
|
|
312
312
|
let result;
|
|
313
313
|
try {
|
|
314
|
-
if (step.
|
|
314
|
+
if (step.operation && this._operations) {
|
|
315
|
+
const opResult = await this._operations.execute(step.operation, resolvedParams, { connections: this.connections, env: context.env });
|
|
316
|
+
result = { data: opResult };
|
|
317
|
+
} else if (step.service === 'internal') {
|
|
315
318
|
result = await this._executeInternal(step.action, resolvedParams);
|
|
316
319
|
} else {
|
|
317
320
|
result = await this._executeService(step.service, step.action, resolvedParams);
|
|
@@ -342,7 +345,10 @@ export class WorkflowRunner {
|
|
|
342
345
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
343
346
|
await new Promise(r => setTimeout(r, backoff * attempt));
|
|
344
347
|
try {
|
|
345
|
-
if (step.
|
|
348
|
+
if (step.operation && this._operations) {
|
|
349
|
+
const opResult = await this._operations.execute(step.operation, resolvedParams, { connections: this.connections, env: context.env });
|
|
350
|
+
result = { data: opResult };
|
|
351
|
+
} else if (step.service === 'internal') {
|
|
346
352
|
result = await this._executeInternal(step.action, resolvedParams);
|
|
347
353
|
} else {
|
|
348
354
|
result = await this._executeService(step.service, step.action, resolvedParams);
|
|
@@ -406,6 +412,32 @@ export class WorkflowRunner {
|
|
|
406
412
|
};
|
|
407
413
|
}
|
|
408
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Run a workflow with an OperationRegistry and custom environment.
|
|
417
|
+
* Used by the Application Engine for executing application workflows.
|
|
418
|
+
*
|
|
419
|
+
* @param {object} opts
|
|
420
|
+
* @param {object} opts.workflow — Inline workflow definition
|
|
421
|
+
* @param {object} [opts.inputs] — Input values
|
|
422
|
+
* @param {import("./engine/operations.js").OperationRegistry} [opts.operations] — Operation registry
|
|
423
|
+
* @param {object} [opts.env] — Custom environment variables
|
|
424
|
+
* @returns {Promise<object>}
|
|
425
|
+
*/
|
|
426
|
+
async runWithOperations({ workflow, inputs = {}, operations, env }) {
|
|
427
|
+
// Temporarily set operations and env on instance
|
|
428
|
+
const prevOps = this._operations;
|
|
429
|
+
const prevEnv = this._customEnv;
|
|
430
|
+
this._operations = operations || null;
|
|
431
|
+
this._customEnv = env || null;
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
return await this.run({ workflow, inputs });
|
|
435
|
+
} finally {
|
|
436
|
+
this._operations = prevOps;
|
|
437
|
+
this._customEnv = prevEnv;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
409
441
|
/**
|
|
410
442
|
* List all .0n workflows in ~/.0n/workflows/.
|
|
411
443
|
*/
|