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.
- package/README.md +162 -60
- package/catalog.js +756 -0
- package/cli.js +274 -0
- package/index.js +8 -1
- package/lib/stats.json +1 -1
- package/package.json +44 -2
- package/vault/deed-collector.js +286 -0
- package/vault/deed-importer.js +277 -0
- package/vault/deed.js +319 -0
- package/vault/tools-deed.js +257 -0
|
@@ -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;
|