0nmcp 2.0.0 → 2.2.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.
@@ -0,0 +1,277 @@
1
+ // ============================================================
2
+ // 0nMCP — Vault: Deed Importer
3
+ // ============================================================
4
+ // Writes decrypted deed contents back to live system config.
5
+ // Generates .0n connection files, .env files, MCP platform
6
+ // configs, and restores workflows and AI brain data.
7
+ //
8
+ // Patent Pending: US Provisional Patent Application #63/990,046
9
+ // ============================================================
10
+
11
+ import { existsSync, mkdirSync, writeFileSync, readdirSync } from "fs";
12
+ import { join } from "path";
13
+ import { homedir } from "os";
14
+
15
+ const DOT_ON = join(homedir(), ".0n");
16
+ const CONNECTIONS_DIR = join(DOT_ON, "connections");
17
+ const WORKFLOWS_DIR = join(DOT_ON, "workflows");
18
+ const BRAIN_DIR = join(DOT_ON, "brain");
19
+
20
+ function ensureDir(dir) {
21
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
22
+ }
23
+
24
+ // ── Write .0n Connection Files ──────────────────────────────
25
+
26
+ /**
27
+ * Write service credentials as .0n connection files.
28
+ *
29
+ * @param {Object} credentials - { service: { field: value } }
30
+ * @param {string} dir - Target connections directory
31
+ * @returns {{ written: string[], skipped: string[] }}
32
+ */
33
+ function writeConnectionFiles(credentials, dir) {
34
+ ensureDir(dir);
35
+ const written = [];
36
+ const skipped = [];
37
+
38
+ for (const [service, creds] of Object.entries(credentials)) {
39
+ try {
40
+ const connection = {
41
+ $0n: {
42
+ type: "connection",
43
+ version: "1.0.0",
44
+ service,
45
+ created: new Date().toISOString(),
46
+ source: "business_deed_import",
47
+ },
48
+ credentials: creds,
49
+ };
50
+
51
+ const filePath = join(dir, `${service}.0n`);
52
+ writeFileSync(filePath, JSON.stringify(connection, null, 2));
53
+ written.push(service);
54
+ } catch (err) {
55
+ skipped.push(`${service}: ${err.message}`);
56
+ }
57
+ }
58
+
59
+ return { written, skipped };
60
+ }
61
+
62
+ // ── Generate .env File ──────────────────────────────────────
63
+
64
+ /**
65
+ * Write environment variables to a .env file.
66
+ *
67
+ * @param {Object} envVars - { KEY: value }
68
+ * @param {string} filePath - Output .env path
69
+ * @returns {{ written: number, file: string }}
70
+ */
71
+ function writeEnvFile(envVars, filePath) {
72
+ const lines = [
73
+ `# Generated by 0nMCP Business Deed Import`,
74
+ `# ${new Date().toISOString()}`,
75
+ "",
76
+ ];
77
+
78
+ for (const [key, value] of Object.entries(envVars)) {
79
+ // Quote values that contain spaces or special chars
80
+ const needsQuotes = /[\s#"'\\]/.test(value);
81
+ lines.push(`${key}=${needsQuotes ? `"${value}"` : value}`);
82
+ }
83
+
84
+ writeFileSync(filePath, lines.join("\n") + "\n");
85
+ return { written: Object.keys(envVars).length, file: filePath };
86
+ }
87
+
88
+ // ── Write Workflow Files ────────────────────────────────────
89
+
90
+ /**
91
+ * Write workflow definitions to ~/.0n/workflows/.
92
+ *
93
+ * @param {Array|Object} workflows - Workflow definitions
94
+ * @param {string} dir - Target workflows directory
95
+ * @returns {{ written: string[], count: number }}
96
+ */
97
+ function writeWorkflowFiles(workflows, dir) {
98
+ ensureDir(dir);
99
+ const written = [];
100
+
101
+ const items = Array.isArray(workflows) ? workflows : [workflows];
102
+
103
+ for (const workflow of items) {
104
+ try {
105
+ const name = workflow?.$0n?.name || workflow?.name || `workflow-${Date.now()}`;
106
+ const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
107
+ const filePath = join(dir, `${safeName}.0n`);
108
+ writeFileSync(filePath, JSON.stringify(workflow, null, 2));
109
+ written.push(safeName);
110
+ } catch {
111
+ // Skip invalid workflows
112
+ }
113
+ }
114
+
115
+ return { written, count: written.length };
116
+ }
117
+
118
+ // ── Write MCP Configs ───────────────────────────────────────
119
+
120
+ /**
121
+ * Write MCP platform configurations.
122
+ *
123
+ * @param {Object} mcpConfigs - MCP configuration data
124
+ * @param {string} targetDir - Target directory
125
+ * @returns {{ written: string[] }}
126
+ */
127
+ function writeMcpConfigs(mcpConfigs, targetDir) {
128
+ const written = [];
129
+
130
+ if (mcpConfigs.platforms && typeof mcpConfigs.platforms === "object") {
131
+ for (const [platform, config] of Object.entries(mcpConfigs.platforms)) {
132
+ try {
133
+ const filePath = join(targetDir, `mcp-${platform}.json`);
134
+ writeFileSync(filePath, JSON.stringify(config, null, 2));
135
+ written.push(platform);
136
+ } catch {
137
+ // Skip
138
+ }
139
+ }
140
+ }
141
+
142
+ // Write raw config if present
143
+ if (mcpConfigs.servers) {
144
+ try {
145
+ const filePath = join(targetDir, "mcp-config.json");
146
+ writeFileSync(filePath, JSON.stringify(mcpConfigs, null, 2));
147
+ written.push("mcp-config");
148
+ } catch {
149
+ // Skip
150
+ }
151
+ }
152
+
153
+ return { written };
154
+ }
155
+
156
+ // ── Write AI Brain Data ─────────────────────────────────────
157
+
158
+ /**
159
+ * Write AI brain data to ~/.0n/brain/.
160
+ *
161
+ * @param {Object} aiBrain - AI brain data
162
+ * @param {string} dir - Target brain directory
163
+ * @returns {{ written: string[] }}
164
+ */
165
+ function writeBrainData(aiBrain, dir) {
166
+ ensureDir(dir);
167
+ const written = [];
168
+
169
+ // Write each brain section as a separate file
170
+ for (const [section, data] of Object.entries(aiBrain)) {
171
+ try {
172
+ const filePath = join(dir, `${section}.json`);
173
+ writeFileSync(filePath, JSON.stringify(data, null, 2));
174
+ written.push(section);
175
+ } catch {
176
+ // Skip
177
+ }
178
+ }
179
+
180
+ return { written };
181
+ }
182
+
183
+ // ── Master Import Function ──────────────────────────────────
184
+
185
+ /**
186
+ * Import all deed layers into the live system.
187
+ *
188
+ * @param {Object} layers - Decrypted layer data
189
+ * @param {string} [targetDir] - Base target directory (default: ~/.0n/)
190
+ * @returns {Object} Import report
191
+ */
192
+ export function importDeedToSystem(layers, targetDir = null) {
193
+ const baseDir = targetDir || DOT_ON;
194
+ const connectionsDir = join(baseDir, "connections");
195
+ const workflowsDir = join(baseDir, "workflows");
196
+ const brainDir = join(baseDir, "brain");
197
+
198
+ ensureDir(baseDir);
199
+
200
+ const report = {
201
+ success: true,
202
+ timestamp: new Date().toISOString(),
203
+ connections: { written: [], skipped: [] },
204
+ envFile: null,
205
+ workflows: { written: [], count: 0 },
206
+ mcpConfigs: { written: [] },
207
+ brain: { written: [] },
208
+ siteProfiles: null,
209
+ deed: null,
210
+ errors: [],
211
+ };
212
+
213
+ // 1. Import credentials as .0n connection files
214
+ if (layers.credentials && typeof layers.credentials === "object" && !layers.credentials.error) {
215
+ try {
216
+ report.connections = writeConnectionFiles(layers.credentials, connectionsDir);
217
+ } catch (err) {
218
+ report.errors.push(`credentials: ${err.message}`);
219
+ }
220
+ }
221
+
222
+ // 2. Generate .env file from env_vars
223
+ if (layers.env_vars && typeof layers.env_vars === "object" && !layers.env_vars.error) {
224
+ try {
225
+ const envPath = join(baseDir, ".env.deed");
226
+ report.envFile = writeEnvFile(layers.env_vars, envPath);
227
+ } catch (err) {
228
+ report.errors.push(`env_vars: ${err.message}`);
229
+ }
230
+ }
231
+
232
+ // 3. Write workflows
233
+ if (layers.workflows && !layers.workflows.error) {
234
+ try {
235
+ report.workflows = writeWorkflowFiles(layers.workflows, workflowsDir);
236
+ } catch (err) {
237
+ report.errors.push(`workflows: ${err.message}`);
238
+ }
239
+ }
240
+
241
+ // 4. Write MCP configs
242
+ if (layers.mcp_configs && typeof layers.mcp_configs === "object" && !layers.mcp_configs.error) {
243
+ try {
244
+ report.mcpConfigs = writeMcpConfigs(layers.mcp_configs, baseDir);
245
+ } catch (err) {
246
+ report.errors.push(`mcp_configs: ${err.message}`);
247
+ }
248
+ }
249
+
250
+ // 5. Write site profiles
251
+ if (layers.site_profiles && typeof layers.site_profiles === "object" && !layers.site_profiles.error) {
252
+ try {
253
+ const profilePath = join(baseDir, "site-profiles.json");
254
+ writeFileSync(profilePath, JSON.stringify(layers.site_profiles, null, 2));
255
+ report.siteProfiles = { file: profilePath };
256
+ } catch (err) {
257
+ report.errors.push(`site_profiles: ${err.message}`);
258
+ }
259
+ }
260
+
261
+ // 6. Write AI brain data
262
+ if (layers.ai_brain && typeof layers.ai_brain === "object" && !layers.ai_brain.error) {
263
+ try {
264
+ report.brain = writeBrainData(layers.ai_brain, brainDir);
265
+ } catch (err) {
266
+ report.errors.push(`ai_brain: ${err.message}`);
267
+ }
268
+ }
269
+
270
+ // 7. Extract deed metadata from audit_trail
271
+ if (layers.audit_trail?.deed) {
272
+ report.deed = layers.audit_trail.deed;
273
+ }
274
+
275
+ report.success = report.errors.length === 0;
276
+ return report;
277
+ }
package/vault/deed.js ADDED
@@ -0,0 +1,319 @@
1
+ // ============================================================
2
+ // 0nMCP — Vault: Business Deed
3
+ // ============================================================
4
+ // Digital Business Asset Transfer System.
5
+ // Packages an entire business's digital assets (API keys, logins,
6
+ // credentials, configurations, workflows, AI prompts) into a
7
+ // single verifiable, transferable, encrypted .0nv container.
8
+ //
9
+ // Lifecycle: CREATE > PACKAGE > ESCROW > ACCEPT > IMPORT > FLIP
10
+ //
11
+ // Patent Pending: US Provisional Patent Application #63/990,046
12
+ // ============================================================
13
+
14
+ import { join } from "path";
15
+ import { homedir } from "os";
16
+ import { existsSync, mkdirSync } from "fs";
17
+ import { assembleContainer, disassembleContainer, inspectContainer, saveContainer, loadContainer } from "./container.js";
18
+ import { LAYER_NAMES } from "./layers.js";
19
+ import { createSeal } from "./seal.js";
20
+ import { generateSigningKeyPair, sign, sha3Hex } from "./crypto-container.js";
21
+ import { registerTransfer, lookupTransfer } from "./registry.js";
22
+ import { collectCredentials } from "./deed-collector.js";
23
+ import { importDeedToSystem } from "./deed-importer.js";
24
+
25
+ const VAULT_DIR = join(homedir(), ".0n", "vault");
26
+ const DEED_VERSION = "1.0.0";
27
+
28
+ function ensureVaultDir() {
29
+ if (!existsSync(VAULT_DIR)) mkdirSync(VAULT_DIR, { recursive: true });
30
+ }
31
+
32
+ // ── Deed Metadata Builder ───────────────────────────────────
33
+
34
+ function buildDeedMetadata(options, services, credentialCount) {
35
+ return {
36
+ deed: {
37
+ version: DEED_VERSION,
38
+ type: "business_deed",
39
+ business: {
40
+ name: options.name || "Unnamed Business",
41
+ domain: options.domain || null,
42
+ description: options.description || null,
43
+ valuation: options.valuation || null,
44
+ currency: options.currency || "USD",
45
+ },
46
+ creator: {
47
+ name: options.creatorName || null,
48
+ email: options.creatorEmail || null,
49
+ },
50
+ services,
51
+ credential_count: credentialCount,
52
+ transfer_history: [],
53
+ created: new Date().toISOString(),
54
+ },
55
+ };
56
+ }
57
+
58
+ // ── BusinessDeed Class ──────────────────────────────────────
59
+
60
+ export class BusinessDeed {
61
+
62
+ /**
63
+ * Create a new Business Deed — package credentials into a .0nv file.
64
+ *
65
+ * @param {Object} options
66
+ * @param {string} options.name - Business name
67
+ * @param {string} [options.domain] - Business domain
68
+ * @param {string} [options.description] - Business description
69
+ * @param {number} [options.valuation] - Business valuation
70
+ * @param {string} [options.currency] - Currency code (default: USD)
71
+ * @param {string} [options.creatorName] - Creator's name
72
+ * @param {string} [options.creatorEmail] - Creator's email
73
+ * @param {Object} [options.credentials] - Pre-structured credentials { service: { field: value } }
74
+ * @param {Array} [options.workflows] - Workflow definitions
75
+ * @param {Object} [options.envVars] - Environment variables
76
+ * @param {Object} [options.mcpConfigs] - MCP server configurations
77
+ * @param {Object} [options.siteProfiles] - Site/CRO profiles
78
+ * @param {Object} [options.aiBrain] - AI agent data, prompts, memory
79
+ * @param {string} options.passphrase - Encryption passphrase
80
+ * @param {string} [options.output] - Output file path
81
+ * @param {Array} [options.escrowParties] - Escrow party definitions
82
+ * @param {Object} [options.accessMatrix] - Per-party layer access
83
+ * @returns {Promise<Object>} Created deed info
84
+ */
85
+ async create(options) {
86
+ const {
87
+ credentials = {},
88
+ workflows = [],
89
+ envVars = {},
90
+ mcpConfigs = {},
91
+ siteProfiles = {},
92
+ aiBrain = {},
93
+ passphrase,
94
+ output,
95
+ escrowParties = [],
96
+ accessMatrix = {},
97
+ } = options;
98
+
99
+ if (!passphrase) throw new Error("Passphrase is required");
100
+
101
+ // Determine services from credentials
102
+ const services = Object.keys(credentials);
103
+ const credentialCount = services.reduce((sum, svc) => sum + Object.keys(credentials[svc]).length, 0);
104
+
105
+ // Build deed metadata
106
+ const deedMeta = buildDeedMetadata(options, services, credentialCount);
107
+
108
+ // Assemble all 7 layers
109
+ const layers = {};
110
+ if (workflows.length > 0) layers.workflows = workflows;
111
+ if (Object.keys(credentials).length > 0) layers.credentials = credentials;
112
+ if (Object.keys(envVars).length > 0) layers.env_vars = envVars;
113
+ if (Object.keys(mcpConfigs).length > 0) layers.mcp_configs = mcpConfigs;
114
+ if (Object.keys(siteProfiles).length > 0) layers.site_profiles = siteProfiles;
115
+ if (Object.keys(aiBrain).length > 0) layers.ai_brain = aiBrain;
116
+
117
+ // Audit trail always includes deed metadata
118
+ layers.audit_trail = deedMeta;
119
+
120
+ // Ensure at least credentials or some layer has data
121
+ if (Object.keys(layers).length <= 1) {
122
+ throw new Error("Deed must contain at least one data layer (credentials, workflows, env_vars, etc.)");
123
+ }
124
+
125
+ const result = await assembleContainer({
126
+ layers,
127
+ passphrase,
128
+ escrowParties,
129
+ accessMatrix,
130
+ metadata: { type: "business_deed", business: options.name },
131
+ });
132
+
133
+ // Save to file
134
+ ensureVaultDir();
135
+ const filePath = output || join(VAULT_DIR, `deed-${result.transferId}.0nv`);
136
+ const savedPath = saveContainer(result.buffer, filePath);
137
+
138
+ return {
139
+ file: savedPath,
140
+ transferId: result.transferId,
141
+ sealHex: result.sealHex,
142
+ publicKey: result.publicKey.toString("hex"),
143
+ services,
144
+ credentialCount,
145
+ layerCount: result.layerCount,
146
+ layers: result.layers,
147
+ containerSize: result.buffer.length,
148
+ business: deedMeta.deed.business,
149
+ timestamp: new Date(result.timestamp).toISOString(),
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Open a deed — decrypt and return all layer data.
155
+ *
156
+ * @param {string} filePath - Path to .0nv file
157
+ * @param {string} passphrase - Decryption passphrase
158
+ * @param {string[]} [onlyLayers] - Only decrypt these layers
159
+ * @returns {Promise<Object>}
160
+ */
161
+ async open(filePath, passphrase, onlyLayers = null) {
162
+ const buffer = loadContainer(filePath);
163
+ const result = await disassembleContainer(buffer, passphrase, onlyLayers);
164
+
165
+ // Extract deed metadata from audit_trail
166
+ const deedMeta = result.layers.audit_trail?.deed || null;
167
+
168
+ return {
169
+ ...result,
170
+ deed: deedMeta,
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Inspect a deed without decrypting — metadata + seal only.
176
+ *
177
+ * @param {string} filePath
178
+ * @returns {Object}
179
+ */
180
+ inspect(filePath) {
181
+ const buffer = loadContainer(filePath);
182
+ const info = inspectContainer(buffer);
183
+
184
+ // Check registry for transfer history
185
+ const transfer = lookupTransfer(info.metadata.transferId);
186
+
187
+ return {
188
+ ...info,
189
+ type: "business_deed",
190
+ transfer: transfer.found ? transfer.transfer : null,
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Verify deed integrity — Seal of Truth + Ed25519 signature.
196
+ *
197
+ * @param {string} filePath
198
+ * @returns {Object}
199
+ */
200
+ verify(filePath) {
201
+ const buffer = loadContainer(filePath);
202
+ const info = inspectContainer(buffer);
203
+ const transfer = lookupTransfer(info.metadata.transferId);
204
+
205
+ return {
206
+ verified: info.seal.valid && info.signature.valid,
207
+ seal: info.seal,
208
+ signature: info.signature,
209
+ transferId: info.metadata.transferId,
210
+ created: info.metadata.created,
211
+ transfer: transfer.found ? transfer.transfer : null,
212
+ patent: info.patent,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Accept a deed transfer — buyer signs acceptance with chain of custody.
218
+ * Opens the deed, records the acceptance in the audit trail,
219
+ * and creates a new container with updated transfer history.
220
+ *
221
+ * @param {string} filePath - Path to .0nv deed
222
+ * @param {string} passphrase - Deed passphrase
223
+ * @param {Object} buyer - { name, email }
224
+ * @param {string} [newPassphrase] - Optional new passphrase for re-encrypted deed
225
+ * @param {string} [outputPath] - Optional output path for accepted deed
226
+ * @returns {Promise<Object>}
227
+ */
228
+ async accept(filePath, passphrase, buyer, newPassphrase = null, outputPath = null) {
229
+ // Open existing deed
230
+ const buffer = loadContainer(filePath);
231
+ const result = await disassembleContainer(buffer, passphrase);
232
+
233
+ if (!result.seal.valid || !result.signature.valid) {
234
+ throw new Error("Cannot accept deed — seal or signature verification failed");
235
+ }
236
+
237
+ const deedMeta = result.layers.audit_trail?.deed;
238
+ if (!deedMeta) {
239
+ throw new Error("Not a valid business deed — no deed metadata found in audit_trail");
240
+ }
241
+
242
+ // Record acceptance in transfer history
243
+ const acceptanceRecord = {
244
+ action: "accept",
245
+ from: deedMeta.creator,
246
+ to: { name: buyer.name, email: buyer.email },
247
+ timestamp: new Date().toISOString(),
248
+ originalSeal: result.seal.hash,
249
+ originalTransferId: result.metadata.transferId,
250
+ };
251
+
252
+ deedMeta.transfer_history.push(acceptanceRecord);
253
+ deedMeta.creator = { name: buyer.name, email: buyer.email };
254
+ result.layers.audit_trail.deed = deedMeta;
255
+
256
+ // Re-create container with updated audit trail (optionally with new passphrase)
257
+ const usePassphrase = newPassphrase || passphrase;
258
+ const newResult = await assembleContainer({
259
+ layers: result.layers,
260
+ passphrase: usePassphrase,
261
+ metadata: { type: "business_deed_accepted", buyer: buyer.name },
262
+ });
263
+
264
+ ensureVaultDir();
265
+ const outPath = outputPath || join(VAULT_DIR, `deed-accepted-${newResult.transferId}.0nv`);
266
+ const savedPath = saveContainer(newResult.buffer, outPath);
267
+
268
+ return {
269
+ file: savedPath,
270
+ transferId: newResult.transferId,
271
+ sealHex: newResult.sealHex,
272
+ previousTransferId: result.metadata.transferId,
273
+ previousSeal: result.seal.hash,
274
+ buyer,
275
+ services: deedMeta.services,
276
+ credentialCount: deedMeta.credential_count,
277
+ transferHistory: deedMeta.transfer_history,
278
+ };
279
+ }
280
+
281
+ /**
282
+ * Import a deed — decrypt and write to live system configuration.
283
+ *
284
+ * @param {string} filePath - Path to .0nv deed
285
+ * @param {string} passphrase - Decryption passphrase
286
+ * @param {string} [targetDir] - Target ~/.0n/ directory
287
+ * @returns {Promise<Object>} Import report
288
+ */
289
+ async importDeed(filePath, passphrase, targetDir = null) {
290
+ const buffer = loadContainer(filePath);
291
+ const result = await disassembleContainer(buffer, passphrase);
292
+
293
+ if (!result.seal.valid || !result.signature.valid) {
294
+ throw new Error("Cannot import deed — seal or signature verification failed");
295
+ }
296
+
297
+ return importDeedToSystem(result.layers, targetDir);
298
+ }
299
+
300
+ /**
301
+ * Export — collect credentials from sources and package as deed in one step.
302
+ *
303
+ * @param {Object} sources - { envFile, jsonFile, csvFile, connectionsDir, manual }
304
+ * @param {Object} options - Same as create() options
305
+ * @returns {Promise<Object>}
306
+ */
307
+ async export(sources, options) {
308
+ const collected = await collectCredentials(sources);
309
+
310
+ return this.create({
311
+ ...options,
312
+ credentials: collected.credentials,
313
+ envVars: collected.envVars,
314
+ services: collected.services,
315
+ });
316
+ }
317
+ }
318
+
319
+ export const DEED_VERSION_STR = DEED_VERSION;