forge-trust-chain 0.3.0 → 0.5.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 +88 -14
- package/package.json +1 -1
- package/src/cli/index.js +468 -35
- package/src/core/chain.js +3 -1
- package/src/core/identity.js +467 -0
- package/src/core/keys.js +237 -0
- package/src/core/trust-atom.js +107 -9
- package/src/mcp/server.js +159 -22
- package/src/scanner/index.js +8 -5
- package/src/store/store.js +84 -2
- package/src/test.js +219 -4
package/src/cli/index.js
CHANGED
|
@@ -18,10 +18,24 @@ import { scan, formatScanResults } from "../scanner/index.js";
|
|
|
18
18
|
import { TrustChain, findDivergence } from "../core/chain.js";
|
|
19
19
|
import { Store } from "../store/store.js";
|
|
20
20
|
import { hash } from "../core/trust-pixel.js";
|
|
21
|
-
import { verifyAtom, formatAtom, createAtom } from "../core/trust-atom.js";
|
|
21
|
+
import { verifyAtom, formatAtom, createAtom, createSignedAtom, verifyAtomSignature, verifyChain } from "../core/trust-atom.js";
|
|
22
|
+
import { initializeKeys, loadKeys, hasKeys, exportIdentity as exportKeysIdentity, importTrustedKey, listTrustedKeys, getFingerprint } from "../core/keys.js";
|
|
22
23
|
import { execSync } from "node:child_process";
|
|
23
24
|
import { hostname, userInfo } from "node:os";
|
|
24
25
|
import { submitToOTS, checkOTSUpgrade, witnessSummary, createBilateralWitness, witnessLevel } from "../core/witness.js";
|
|
26
|
+
import {
|
|
27
|
+
createGenesis,
|
|
28
|
+
registerInstance,
|
|
29
|
+
loadGenesis,
|
|
30
|
+
loadCurrentInstance,
|
|
31
|
+
loadInstances,
|
|
32
|
+
verifyIdentityChain,
|
|
33
|
+
exportIdentity,
|
|
34
|
+
getForgeIdentity,
|
|
35
|
+
hasForgeId,
|
|
36
|
+
hasInstance,
|
|
37
|
+
signAtomProof,
|
|
38
|
+
} from "../core/identity.js";
|
|
25
39
|
|
|
26
40
|
/* ================================================================
|
|
27
41
|
STATE SNAPSHOT — captures system state for from/to fields
|
|
@@ -46,6 +60,11 @@ function stateSnapshot() {
|
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
function getIdentity() {
|
|
63
|
+
// Use ForgeID when initialized and instance registered
|
|
64
|
+
const forgeIdentity = getForgeIdentity();
|
|
65
|
+
if (forgeIdentity !== null) return forgeIdentity;
|
|
66
|
+
|
|
67
|
+
// Fallback to legacy identity
|
|
49
68
|
try {
|
|
50
69
|
const user = userInfo().username;
|
|
51
70
|
const host = hostname();
|
|
@@ -59,6 +78,134 @@ function getIdentity() {
|
|
|
59
78
|
COMMANDS
|
|
60
79
|
================================================================ */
|
|
61
80
|
|
|
81
|
+
function cmdInit(name) {
|
|
82
|
+
console.log("");
|
|
83
|
+
console.log(" ── FORGE Identity Initialization ──");
|
|
84
|
+
console.log("");
|
|
85
|
+
|
|
86
|
+
if (hasKeys()) {
|
|
87
|
+
const keys = loadKeys();
|
|
88
|
+
console.log(" ⚠️ Keys already exist!");
|
|
89
|
+
console.log(` Fingerprint: ${keys.fingerprint}`);
|
|
90
|
+
console.log("");
|
|
91
|
+
console.log(" To regenerate (WARNING: invalidates all your signed atoms):");
|
|
92
|
+
console.log(" rm -rf ~/.forge/keys && forge init");
|
|
93
|
+
console.log("");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const identity = {
|
|
98
|
+
name: name || getIdentity(),
|
|
99
|
+
created_by: "forge-cli",
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const result = initializeKeys(identity);
|
|
104
|
+
|
|
105
|
+
console.log(" ✓ Ed25519 key pair generated");
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log(` Fingerprint: ${result.fingerprint}`);
|
|
108
|
+
console.log(` Identity: ${identity.name}`);
|
|
109
|
+
console.log("");
|
|
110
|
+
console.log(" Files created:");
|
|
111
|
+
console.log(" ~/.forge/keys/private.key (NEVER share this!)");
|
|
112
|
+
console.log(" ~/.forge/keys/public.key (share for verification)");
|
|
113
|
+
console.log("");
|
|
114
|
+
console.log(" From now on, all atoms will be signed with your private key.");
|
|
115
|
+
console.log(" Others can verify your atoms using your public key.");
|
|
116
|
+
console.log("");
|
|
117
|
+
console.log(" Share your identity:");
|
|
118
|
+
console.log(" forge identity --export > my-identity.json");
|
|
119
|
+
console.log("");
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.error(` ✗ Error: ${err.message}`);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function cmdIdentity(subArgs) {
|
|
127
|
+
if (subArgs.includes("--export")) {
|
|
128
|
+
const identity = exportKeysIdentity();
|
|
129
|
+
if (!identity) {
|
|
130
|
+
console.error(" No identity found. Run 'forge init' first.");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
console.log(JSON.stringify(identity, null, 2));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (subArgs.includes("--import")) {
|
|
138
|
+
const filePath = subArgs[subArgs.indexOf("--import") + 1];
|
|
139
|
+
if (!filePath) {
|
|
140
|
+
console.error(" Usage: forge identity --import <file.json> [--alias <name>]");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const { readFileSync } = require("node:fs");
|
|
146
|
+
const data = JSON.parse(readFileSync(filePath, "utf8"));
|
|
147
|
+
const alias = subArgs.includes("--alias")
|
|
148
|
+
? subArgs[subArgs.indexOf("--alias") + 1]
|
|
149
|
+
: data.identity?.name || null;
|
|
150
|
+
|
|
151
|
+
const result = importTrustedKey(data.public_key, alias);
|
|
152
|
+
console.log("");
|
|
153
|
+
console.log(" ✓ Trusted identity imported");
|
|
154
|
+
console.log(` Fingerprint: ${result.fingerprint}`);
|
|
155
|
+
console.log(` Alias: ${result.alias || "(none)"}`);
|
|
156
|
+
console.log("");
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.error(` ✗ Error: ${err.message}`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (subArgs.includes("--list")) {
|
|
165
|
+
const trusted = listTrustedKeys();
|
|
166
|
+
console.log("");
|
|
167
|
+
console.log(" ── Trusted Identities ──");
|
|
168
|
+
console.log("");
|
|
169
|
+
|
|
170
|
+
if (trusted.length === 0) {
|
|
171
|
+
console.log(" No trusted identities. Import with:");
|
|
172
|
+
console.log(" forge identity --import <file.json>");
|
|
173
|
+
} else {
|
|
174
|
+
for (const t of trusted) {
|
|
175
|
+
console.log(` ${t.fingerprint}`);
|
|
176
|
+
if (t.alias) console.log(` Alias: ${t.alias}`);
|
|
177
|
+
console.log(` Imported: ${new Date(t.imported_at).toISOString()}`);
|
|
178
|
+
console.log("");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Show current identity
|
|
185
|
+
if (!hasKeys()) {
|
|
186
|
+
console.log("");
|
|
187
|
+
console.log(" No identity found. Create one with:");
|
|
188
|
+
console.log(" forge init");
|
|
189
|
+
console.log("");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const keys = loadKeys();
|
|
194
|
+
console.log("");
|
|
195
|
+
console.log(" ── Your FORGE Identity ──");
|
|
196
|
+
console.log("");
|
|
197
|
+
console.log(` Fingerprint: ${keys.fingerprint}`);
|
|
198
|
+
console.log(` Name: ${keys.identity?.name || "(not set)"}`);
|
|
199
|
+
console.log(` Created: ${new Date(keys.identity?.created_at).toISOString()}`);
|
|
200
|
+
console.log(` Algorithm: Ed25519`);
|
|
201
|
+
console.log("");
|
|
202
|
+
console.log(" Commands:");
|
|
203
|
+
console.log(" forge identity --export Export public key for sharing");
|
|
204
|
+
console.log(" forge identity --import Import trusted identity");
|
|
205
|
+
console.log(" forge identity --list List trusted identities");
|
|
206
|
+
console.log("");
|
|
207
|
+
}
|
|
208
|
+
|
|
62
209
|
function cmdScan() {
|
|
63
210
|
const results = scan();
|
|
64
211
|
console.log(formatScanResults(results));
|
|
@@ -78,24 +225,65 @@ function cmdLog(action) {
|
|
|
78
225
|
|
|
79
226
|
const prev = store.lastProof();
|
|
80
227
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
228
|
+
// Signing priority: ForgeID instance > keys.js > unsigned
|
|
229
|
+
let atom;
|
|
230
|
+
let signed = false;
|
|
231
|
+
let signerInfo = null;
|
|
232
|
+
|
|
233
|
+
if (hasInstance()) {
|
|
234
|
+
atom = createAtom({
|
|
235
|
+
who: identity,
|
|
236
|
+
from: stateBefore,
|
|
237
|
+
action,
|
|
238
|
+
to: stateSnapshot(),
|
|
239
|
+
prev,
|
|
240
|
+
sign: signAtomProof,
|
|
241
|
+
});
|
|
242
|
+
signed = !!atom.signature;
|
|
243
|
+
signerInfo = "ForgeID";
|
|
244
|
+
} else {
|
|
245
|
+
const keys = loadKeys();
|
|
246
|
+
if (keys) {
|
|
247
|
+
atom = createSignedAtom({
|
|
248
|
+
who: identity,
|
|
249
|
+
from: stateBefore,
|
|
250
|
+
action,
|
|
251
|
+
to: stateSnapshot(),
|
|
252
|
+
prev,
|
|
253
|
+
});
|
|
254
|
+
signed = true;
|
|
255
|
+
signerInfo = keys.fingerprint;
|
|
256
|
+
} else {
|
|
257
|
+
atom = createAtom({
|
|
258
|
+
who: identity,
|
|
259
|
+
from: stateBefore,
|
|
260
|
+
action,
|
|
261
|
+
to: stateSnapshot(),
|
|
262
|
+
prev,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
88
266
|
|
|
89
267
|
const index = store.appendAtom(atom);
|
|
90
268
|
|
|
269
|
+
// Save plaintext action to local index (never exported)
|
|
270
|
+
store.saveAction(atom.action, action, { who: identity });
|
|
271
|
+
|
|
91
272
|
console.log("");
|
|
92
273
|
console.log(" ✓ TrustAtom recorded");
|
|
93
274
|
console.log(` Index: #${index}`);
|
|
94
|
-
console.log(` Who: ${identity} → ${atom.who.slice(0, 16)}…`);
|
|
275
|
+
console.log(` Who: ${identity.length > 32 ? identity.slice(0, 16) + "…" : identity} → ${atom.who.slice(0, 16)}…`);
|
|
95
276
|
console.log(` Action: ${action}`);
|
|
96
277
|
console.log(` Proof: ${atom.proof.slice(0, 32)}…`);
|
|
97
278
|
console.log(` Chain: ${store.atomCount} atoms total`);
|
|
98
279
|
console.log(` Prev: ${prev === "genesis" ? "genesis" : prev.slice(0, 16) + "…"}`);
|
|
280
|
+
|
|
281
|
+
if (signed) {
|
|
282
|
+
console.log(` Signed: ✓ ${signerInfo}`);
|
|
283
|
+
} else {
|
|
284
|
+
console.log(` Signed: ✗ (run 'forge init' or 'forge id init' to enable signing)`);
|
|
285
|
+
}
|
|
286
|
+
|
|
99
287
|
console.log("");
|
|
100
288
|
console.log(" Witness: self (local only)");
|
|
101
289
|
console.log(" → Run 'forge seal' to create Merkle block");
|
|
@@ -115,25 +303,40 @@ function cmdVerify() {
|
|
|
115
303
|
console.log(" Verifying chain integrity…");
|
|
116
304
|
console.log(` Atoms: ${atoms.length}`);
|
|
117
305
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
for (let i = 0; i < atoms.length; i++) {
|
|
121
|
-
if (!verifyAtom(atoms[i])) {
|
|
122
|
-
broken = i;
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
if (i > 0 && !atoms[i].prev.includes(atoms[i - 1].proof)) {
|
|
126
|
-
broken = i;
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
306
|
+
// Use new verifyChain with signature support
|
|
307
|
+
const result = verifyChain(atoms);
|
|
130
308
|
|
|
131
|
-
if (
|
|
132
|
-
console.log(` ✗ CHAIN BROKEN at atom #${
|
|
309
|
+
if (!result.valid) {
|
|
310
|
+
console.log(` ✗ CHAIN BROKEN at atom #${result.broken_at}`);
|
|
311
|
+
console.log(` Reason: ${result.reason}`);
|
|
133
312
|
console.log(` This means records have been tampered with.`);
|
|
134
313
|
} else {
|
|
135
314
|
console.log(" ✓ CHAIN VALID — all atoms verified, all links intact.");
|
|
136
315
|
}
|
|
316
|
+
|
|
317
|
+
// Signature summary
|
|
318
|
+
const signed = atoms.filter(a => a.signature).length;
|
|
319
|
+
const unsigned = atoms.length - signed;
|
|
320
|
+
|
|
321
|
+
console.log("");
|
|
322
|
+
console.log(" Signature status:");
|
|
323
|
+
console.log(` Signed: ${signed} atoms`);
|
|
324
|
+
console.log(` Unsigned: ${unsigned} atoms`);
|
|
325
|
+
|
|
326
|
+
if (result.signatures && result.signatures.length > 0) {
|
|
327
|
+
const valid = result.signatures.filter(s => s.valid).length;
|
|
328
|
+
const invalid = result.signatures.filter(s => !s.valid).length;
|
|
329
|
+
|
|
330
|
+
if (invalid > 0) {
|
|
331
|
+
console.log(` ⚠️ ${invalid} signature(s) could not be verified`);
|
|
332
|
+
for (const sig of result.signatures.filter(s => !s.valid)) {
|
|
333
|
+
console.log(` #${sig.index}: ${sig.reason}${sig.signer ? ` (${sig.signer})` : ""}`);
|
|
334
|
+
}
|
|
335
|
+
} else if (valid > 0) {
|
|
336
|
+
console.log(` ✓ All ${valid} signature(s) verified`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
137
340
|
console.log("");
|
|
138
341
|
}
|
|
139
342
|
|
|
@@ -177,7 +380,19 @@ function cmdStatus() {
|
|
|
177
380
|
|
|
178
381
|
console.log("");
|
|
179
382
|
console.log(" ── FORGE Chain Status ──");
|
|
180
|
-
|
|
383
|
+
|
|
384
|
+
const genesis = loadGenesis();
|
|
385
|
+
if (genesis) {
|
|
386
|
+
console.log(` ForgeID: ${genesis.forge_id.slice(0, 16)}…`);
|
|
387
|
+
const inst = loadCurrentInstance();
|
|
388
|
+
if (inst) {
|
|
389
|
+
console.log(` Instance: #${inst.instance_seq} (${inst.label})`);
|
|
390
|
+
} else {
|
|
391
|
+
console.log(` Instance: not registered on this server`);
|
|
392
|
+
}
|
|
393
|
+
} else {
|
|
394
|
+
console.log(` Identity: ${getIdentity()} (legacy — run 'forge id init' for ForgeID)`);
|
|
395
|
+
}
|
|
181
396
|
console.log(` Atoms: ${atoms.length}`);
|
|
182
397
|
console.log(` Blocks: ${blocks.length}`);
|
|
183
398
|
console.log(` Store: ~/.forge/chain.json`);
|
|
@@ -307,6 +522,29 @@ function cmdWitness(subArgs) {
|
|
|
307
522
|
}
|
|
308
523
|
}
|
|
309
524
|
|
|
525
|
+
function cmdHistory(limit = 20) {
|
|
526
|
+
const store = new Store();
|
|
527
|
+
const history = store.getHistory(parseInt(limit) || 20);
|
|
528
|
+
|
|
529
|
+
if (history.length === 0) {
|
|
530
|
+
console.log("\n No history. Run 'forge log' first.\n");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
console.log("");
|
|
535
|
+
console.log(" ── FORGE History (local plaintext) ──");
|
|
536
|
+
console.log(" ⚠️ This data is LOCAL ONLY. Never share actions.json.");
|
|
537
|
+
console.log("");
|
|
538
|
+
|
|
539
|
+
for (const entry of history) {
|
|
540
|
+
const time = new Date(entry.when).toISOString().slice(0, 19);
|
|
541
|
+
console.log(` #${entry.index} [${time}]`);
|
|
542
|
+
console.log(` ${entry.action_text}`);
|
|
543
|
+
console.log(` proof: ${entry.proof.slice(0, 24)}…`);
|
|
544
|
+
console.log("");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
310
548
|
function cmdDemo() {
|
|
311
549
|
console.log("");
|
|
312
550
|
console.log("╔══════════════════════════════════════════════════════╗");
|
|
@@ -463,6 +701,165 @@ function cmdDemo() {
|
|
|
463
701
|
console.log("");
|
|
464
702
|
}
|
|
465
703
|
|
|
704
|
+
/* ================================================================
|
|
705
|
+
IDENTITY COMMANDS
|
|
706
|
+
================================================================ */
|
|
707
|
+
|
|
708
|
+
function cmdIdInit(args) {
|
|
709
|
+
const proofIdx = args.indexOf("--proof");
|
|
710
|
+
const proofText = proofIdx >= 0 ? args.slice(proofIdx + 1).join(" ") : "";
|
|
711
|
+
|
|
712
|
+
try {
|
|
713
|
+
const genesis = createGenesis(proofText);
|
|
714
|
+
console.log("");
|
|
715
|
+
console.log(" ✓ ForgeID Genesis Created");
|
|
716
|
+
console.log(` ForgeID: ${genesis.forge_id}`);
|
|
717
|
+
console.log(` Master Key: ${genesis.master_public_key.slice(0, 24)}…`);
|
|
718
|
+
if (proofText) console.log(` PoE: ${proofText}`);
|
|
719
|
+
console.log(` Created: ${genesis.created_at}`);
|
|
720
|
+
console.log("");
|
|
721
|
+
console.log(" ⚠️ ~/.forge/identity/master.key contains your master private key.");
|
|
722
|
+
console.log(" Move it to COLD STORAGE after registering all initial instances.");
|
|
723
|
+
console.log(" Anyone with this key can register new instances under your ForgeID.");
|
|
724
|
+
console.log("");
|
|
725
|
+
console.log(" Next: forge id register [label]");
|
|
726
|
+
console.log("");
|
|
727
|
+
} catch (err) {
|
|
728
|
+
console.error(` Error: ${err.message}`);
|
|
729
|
+
process.exit(1);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function cmdIdRegister(args) {
|
|
734
|
+
const label = args.join(" ") || "";
|
|
735
|
+
|
|
736
|
+
try {
|
|
737
|
+
const instance = registerInstance(label);
|
|
738
|
+
console.log("");
|
|
739
|
+
console.log(" ✓ Instance Registered");
|
|
740
|
+
console.log(` ForgeID: ${instance.forge_id.slice(0, 16)}…`);
|
|
741
|
+
console.log(` Instance: #${instance.instance_seq}`);
|
|
742
|
+
console.log(` Label: ${instance.label}`);
|
|
743
|
+
console.log(` Instance ID: ${instance.instance_id.slice(0, 32)}…`);
|
|
744
|
+
console.log(` Registered: ${instance.registered_at}`);
|
|
745
|
+
console.log("");
|
|
746
|
+
console.log(" All TrustAtoms will now be signed with this instance's Ed25519 key.");
|
|
747
|
+
console.log(" The master.key can now be moved to cold storage.");
|
|
748
|
+
console.log("");
|
|
749
|
+
} catch (err) {
|
|
750
|
+
console.error(` Error: ${err.message}`);
|
|
751
|
+
process.exit(1);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function cmdIdShow() {
|
|
756
|
+
const genesis = loadGenesis();
|
|
757
|
+
if (!genesis) {
|
|
758
|
+
console.log("\n No ForgeID found. Run 'forge id init' first.\n");
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
console.log("");
|
|
763
|
+
console.log(" ── ForgeID ──");
|
|
764
|
+
console.log(` ForgeID: ${genesis.forge_id}`);
|
|
765
|
+
console.log(` Master: ${genesis.master_public_key.slice(0, 24)}…`);
|
|
766
|
+
if (genesis.proof_of_existence) console.log(` PoE: ${genesis.proof_of_existence}`);
|
|
767
|
+
console.log(` Created: ${genesis.created_at}`);
|
|
768
|
+
|
|
769
|
+
const instance = loadCurrentInstance();
|
|
770
|
+
if (instance) {
|
|
771
|
+
console.log("");
|
|
772
|
+
console.log(" ── Current Instance ──");
|
|
773
|
+
console.log(` Seq: #${instance.instance_seq}`);
|
|
774
|
+
console.log(` Label: ${instance.label}`);
|
|
775
|
+
console.log(` ID: ${instance.instance_id}`);
|
|
776
|
+
} else {
|
|
777
|
+
console.log("\n No instance registered on this server.");
|
|
778
|
+
console.log(" Run 'forge id register [label]' to register.");
|
|
779
|
+
}
|
|
780
|
+
console.log("");
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function cmdIdList() {
|
|
784
|
+
const genesis = loadGenesis();
|
|
785
|
+
if (!genesis) {
|
|
786
|
+
console.log("\n No ForgeID found. Run 'forge id init' first.\n");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const instances = loadInstances();
|
|
791
|
+
console.log("");
|
|
792
|
+
console.log(` ForgeID: ${genesis.forge_id.slice(0, 32)}…`);
|
|
793
|
+
console.log(` Instances: ${instances.length}`);
|
|
794
|
+
console.log("");
|
|
795
|
+
|
|
796
|
+
if (instances.length === 0) {
|
|
797
|
+
console.log(" No instances registered.");
|
|
798
|
+
} else {
|
|
799
|
+
const current = loadCurrentInstance();
|
|
800
|
+
for (const inst of instances) {
|
|
801
|
+
const marker = current && current.instance_seq === inst.instance_seq ? " ← this server" : "";
|
|
802
|
+
console.log(` #${inst.instance_seq} ${inst.label.padEnd(20)} ${inst.instance_id.slice(0, 16)}…${marker}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
console.log("");
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function cmdIdVerify() {
|
|
809
|
+
const result = verifyIdentityChain();
|
|
810
|
+
console.log("");
|
|
811
|
+
if (result.valid) {
|
|
812
|
+
console.log(" ✓ Identity chain VALID");
|
|
813
|
+
console.log(` Genesis signature verified`);
|
|
814
|
+
const instances = loadInstances();
|
|
815
|
+
console.log(` ${instances.length} instance registration(s) verified`);
|
|
816
|
+
} else {
|
|
817
|
+
console.log(" ✗ Identity chain BROKEN");
|
|
818
|
+
for (const err of result.errors) {
|
|
819
|
+
console.log(` ✗ ${err}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
console.log("");
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function cmdIdExport() {
|
|
826
|
+
const data = exportIdentity();
|
|
827
|
+
if (!data) {
|
|
828
|
+
console.log("\n No ForgeID found. Run 'forge id init' first.\n");
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
console.log(JSON.stringify(data, null, 2));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function cmdId(subArgs) {
|
|
835
|
+
const subCmd = subArgs[0] || "show";
|
|
836
|
+
const rest = subArgs.slice(1);
|
|
837
|
+
switch (subCmd) {
|
|
838
|
+
case "init":
|
|
839
|
+
cmdIdInit(rest);
|
|
840
|
+
break;
|
|
841
|
+
case "register":
|
|
842
|
+
cmdIdRegister(rest);
|
|
843
|
+
break;
|
|
844
|
+
case "show":
|
|
845
|
+
cmdIdShow();
|
|
846
|
+
break;
|
|
847
|
+
case "list":
|
|
848
|
+
cmdIdList();
|
|
849
|
+
break;
|
|
850
|
+
case "verify":
|
|
851
|
+
cmdIdVerify();
|
|
852
|
+
break;
|
|
853
|
+
case "export":
|
|
854
|
+
cmdIdExport();
|
|
855
|
+
break;
|
|
856
|
+
default:
|
|
857
|
+
console.log(` Unknown id command: ${subCmd}`);
|
|
858
|
+
console.log(" Available: init, register, show, list, verify, export");
|
|
859
|
+
break;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
466
863
|
/* ================================================================
|
|
467
864
|
MAIN
|
|
468
865
|
================================================================ */
|
|
@@ -471,6 +868,14 @@ const args = process.argv.slice(2);
|
|
|
471
868
|
const command = args[0] || "help";
|
|
472
869
|
|
|
473
870
|
switch (command) {
|
|
871
|
+
case "init":
|
|
872
|
+
cmdInit(args[1]);
|
|
873
|
+
break;
|
|
874
|
+
|
|
875
|
+
case "identity":
|
|
876
|
+
cmdIdentity(args.slice(1));
|
|
877
|
+
break;
|
|
878
|
+
|
|
474
879
|
case "scan":
|
|
475
880
|
cmdScan();
|
|
476
881
|
break;
|
|
@@ -507,17 +912,45 @@ switch (command) {
|
|
|
507
912
|
cmdDemo();
|
|
508
913
|
break;
|
|
509
914
|
|
|
915
|
+
case "history":
|
|
916
|
+
cmdHistory(args[1]);
|
|
917
|
+
break;
|
|
918
|
+
|
|
919
|
+
case "mcp":
|
|
920
|
+
// Start MCP server
|
|
921
|
+
import("../mcp/server.js");
|
|
922
|
+
break;
|
|
923
|
+
|
|
924
|
+
case "id":
|
|
925
|
+
cmdId(args.slice(1));
|
|
926
|
+
break;
|
|
927
|
+
|
|
510
928
|
case "help":
|
|
511
929
|
default:
|
|
512
930
|
console.log(`
|
|
513
|
-
FORGE — Trust Chain Protocol v0.
|
|
931
|
+
FORGE — Trust Chain Protocol v0.4
|
|
514
932
|
|
|
515
933
|
Trust = Certainty × Existence
|
|
516
934
|
Every operation produces a verifiable, undeniable fact.
|
|
517
935
|
|
|
518
|
-
|
|
519
|
-
forge
|
|
520
|
-
forge
|
|
936
|
+
Signing (Ed25519 — simple):
|
|
937
|
+
forge init [name] Generate signing key pair
|
|
938
|
+
forge identity Show your identity
|
|
939
|
+
forge identity --export Export public key for sharing
|
|
940
|
+
forge identity --import <file> Import trusted identity
|
|
941
|
+
forge identity --list List trusted identities
|
|
942
|
+
|
|
943
|
+
Identity (ForgeID — genesis/instance):
|
|
944
|
+
forge id init [--proof "text"] Create ForgeID genesis (master keypair)
|
|
945
|
+
forge id register [label] Register this server as instance
|
|
946
|
+
forge id show Show ForgeID and current instance
|
|
947
|
+
forge id list List all registered instances
|
|
948
|
+
forge id verify Verify identity chain integrity
|
|
949
|
+
forge id export Export public identity (no private keys)
|
|
950
|
+
|
|
951
|
+
Operations:
|
|
952
|
+
forge scan Capture trust baseline (system assumptions)
|
|
953
|
+
forge log <action> Record a TrustAtom (signed if keys/ForgeID exist)
|
|
521
954
|
forge verify Verify chain integrity
|
|
522
955
|
forge seal Seal atoms into a Merkle block
|
|
523
956
|
forge anchor Submit Merkle root to OTS calendars (Level 3)
|
|
@@ -525,8 +958,10 @@ switch (command) {
|
|
|
525
958
|
forge witness Show witness status for latest block
|
|
526
959
|
forge witness --bilateral <id> Create bilateral witness receipt
|
|
527
960
|
forge status Show chain status
|
|
528
|
-
forge
|
|
961
|
+
forge history [n] Show recent operations with plaintext (local only)
|
|
962
|
+
forge export Export chain as JSON (no plaintext)
|
|
529
963
|
forge demo Run full demonstration
|
|
964
|
+
forge mcp Start MCP server (for Claude Code)
|
|
530
965
|
|
|
531
966
|
Witness Hierarchy:
|
|
532
967
|
Level 1: Self — Only you hold the hash (can be deleted)
|
|
@@ -535,13 +970,11 @@ switch (command) {
|
|
|
535
970
|
Level 4: Anchored — Bitcoin blockchain (computationally undeletable)
|
|
536
971
|
|
|
537
972
|
Examples:
|
|
538
|
-
forge
|
|
973
|
+
forge init # simple signing
|
|
974
|
+
forge id init --proof "BBC News 2026" # ForgeID genesis
|
|
975
|
+
forge id register "prod-server"
|
|
539
976
|
forge log "deployed nginx config"
|
|
540
|
-
forge
|
|
541
|
-
forge verify
|
|
542
|
-
forge seal
|
|
543
|
-
forge anchor
|
|
544
|
-
forge witness --bilateral ops@provider.com
|
|
977
|
+
forge seal && forge anchor
|
|
545
978
|
`);
|
|
546
979
|
break;
|
|
547
980
|
}
|
package/src/core/chain.js
CHANGED
|
@@ -11,8 +11,9 @@ import { createAtom, verifyAtom, verifyChain, formatAtom } from "./trust-atom.js
|
|
|
11
11
|
import { treeFromAtoms, createBlock, getMerkleProof, verifyMerkleProof } from "./merkle.js";
|
|
12
12
|
|
|
13
13
|
export class TrustChain {
|
|
14
|
-
constructor(identity) {
|
|
14
|
+
constructor(identity, signFn = null) {
|
|
15
15
|
this.identity = identity;
|
|
16
|
+
this.signFn = signFn;
|
|
16
17
|
this.atoms = [];
|
|
17
18
|
this.blocks = [];
|
|
18
19
|
}
|
|
@@ -34,6 +35,7 @@ export class TrustChain {
|
|
|
34
35
|
action,
|
|
35
36
|
to,
|
|
36
37
|
prev,
|
|
38
|
+
sign: this.signFn,
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
this.atoms.push(atom);
|