@originals/sdk 1.5.0 → 1.6.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.
Files changed (105) hide show
  1. package/dist/adapters/FeeOracleMock.js +2 -2
  2. package/dist/bitcoin/OrdinalsClient.d.ts +1 -1
  3. package/dist/bitcoin/OrdinalsClient.js +10 -8
  4. package/dist/bitcoin/PSBTBuilder.js +1 -1
  5. package/dist/bitcoin/utxo-selection.js +2 -2
  6. package/dist/cel/ExternalReferenceManager.d.ts +57 -0
  7. package/dist/cel/ExternalReferenceManager.js +73 -0
  8. package/dist/cel/OriginalsCel.d.ts +245 -0
  9. package/dist/cel/OriginalsCel.js +349 -0
  10. package/dist/cel/algorithms/createEventLog.d.ts +32 -0
  11. package/dist/cel/algorithms/createEventLog.js +56 -0
  12. package/dist/cel/algorithms/deactivateEventLog.d.ts +35 -0
  13. package/dist/cel/algorithms/deactivateEventLog.js +91 -0
  14. package/dist/cel/algorithms/index.d.ts +10 -0
  15. package/dist/cel/algorithms/index.js +10 -0
  16. package/dist/cel/algorithms/updateEventLog.d.ts +34 -0
  17. package/dist/cel/algorithms/updateEventLog.js +82 -0
  18. package/dist/cel/algorithms/verifyEventLog.d.ts +45 -0
  19. package/dist/cel/algorithms/verifyEventLog.js +255 -0
  20. package/dist/cel/algorithms/witnessEvent.d.ts +29 -0
  21. package/dist/cel/algorithms/witnessEvent.js +75 -0
  22. package/dist/cel/cli/create.d.ts +36 -0
  23. package/dist/cel/cli/create.js +282 -0
  24. package/dist/cel/cli/index.d.ts +11 -0
  25. package/dist/cel/cli/index.js +351 -0
  26. package/dist/cel/cli/inspect.d.ts +30 -0
  27. package/dist/cel/cli/inspect.js +475 -0
  28. package/dist/cel/cli/migrate.d.ts +41 -0
  29. package/dist/cel/cli/migrate.js +405 -0
  30. package/dist/cel/cli/verify.d.ts +31 -0
  31. package/dist/cel/cli/verify.js +205 -0
  32. package/dist/cel/hash.d.ts +46 -0
  33. package/dist/cel/hash.js +66 -0
  34. package/dist/cel/index.d.ts +15 -0
  35. package/dist/cel/index.js +15 -0
  36. package/dist/cel/layers/BtcoCelManager.d.ts +121 -0
  37. package/dist/cel/layers/BtcoCelManager.js +329 -0
  38. package/dist/cel/layers/PeerCelManager.d.ts +151 -0
  39. package/dist/cel/layers/PeerCelManager.js +299 -0
  40. package/dist/cel/layers/WebVHCelManager.d.ts +122 -0
  41. package/dist/cel/layers/WebVHCelManager.js +291 -0
  42. package/dist/cel/layers/index.d.ts +13 -0
  43. package/dist/cel/layers/index.js +16 -0
  44. package/dist/cel/serialization/cbor.d.ts +42 -0
  45. package/dist/cel/serialization/cbor.js +163 -0
  46. package/dist/cel/serialization/index.d.ts +9 -0
  47. package/dist/cel/serialization/index.js +9 -0
  48. package/dist/cel/serialization/json.d.ts +41 -0
  49. package/dist/cel/serialization/json.js +180 -0
  50. package/dist/cel/types.d.ts +149 -0
  51. package/dist/cel/types.js +7 -0
  52. package/dist/cel/witnesses/BitcoinWitness.d.ts +83 -0
  53. package/dist/cel/witnesses/BitcoinWitness.js +116 -0
  54. package/dist/cel/witnesses/HttpWitness.d.ts +79 -0
  55. package/dist/cel/witnesses/HttpWitness.js +163 -0
  56. package/dist/cel/witnesses/WitnessService.d.ts +49 -0
  57. package/dist/cel/witnesses/WitnessService.js +10 -0
  58. package/dist/cel/witnesses/index.d.ts +10 -0
  59. package/dist/cel/witnesses/index.js +7 -0
  60. package/dist/core/OriginalsSDK.js +5 -1
  61. package/dist/crypto/Signer.js +14 -6
  62. package/dist/crypto/noble-init.js +20 -1
  63. package/dist/did/BtcoDidResolver.d.ts +2 -2
  64. package/dist/did/BtcoDidResolver.js +12 -8
  65. package/dist/did/DIDManager.js +6 -4
  66. package/dist/did/KeyManager.d.ts +1 -1
  67. package/dist/did/KeyManager.js +7 -4
  68. package/dist/did/WebVHManager.js +1 -1
  69. package/dist/did/createBtcoDidDocument.js +2 -1
  70. package/dist/events/types.d.ts +4 -1
  71. package/dist/examples/create-module-original.js +1 -1
  72. package/dist/examples/full-lifecycle-flow.js +2 -2
  73. package/dist/index.d.ts +13 -0
  74. package/dist/index.js +12 -0
  75. package/dist/kinds/KindRegistry.js +59 -29
  76. package/dist/lifecycle/BatchOperations.d.ts +5 -3
  77. package/dist/lifecycle/BatchOperations.js +11 -5
  78. package/dist/lifecycle/LifecycleManager.d.ts +1 -1
  79. package/dist/lifecycle/LifecycleManager.js +42 -33
  80. package/dist/lifecycle/OriginalsAsset.js +2 -2
  81. package/dist/migration/MigrationManager.js +67 -3
  82. package/dist/storage/LocalStorageAdapter.js +4 -1
  83. package/dist/storage/MemoryStorageAdapter.js +7 -7
  84. package/dist/types/network.js +6 -3
  85. package/dist/utils/Logger.d.ts +6 -6
  86. package/dist/utils/Logger.js +5 -3
  87. package/dist/utils/MetricsCollector.js +1 -1
  88. package/dist/utils/bitcoin-address.js +4 -2
  89. package/dist/utils/cbor.js +16 -3
  90. package/dist/utils/encoding.d.ts +4 -4
  91. package/dist/utils/encoding.js +7 -6
  92. package/dist/utils/hash.js +6 -1
  93. package/dist/utils/serialization.d.ts +2 -2
  94. package/dist/utils/serialization.js +7 -5
  95. package/dist/utils/telemetry.js +6 -2
  96. package/dist/utils/validation.js +8 -4
  97. package/dist/vc/CredentialManager.d.ts +8 -8
  98. package/dist/vc/CredentialManager.js +46 -33
  99. package/dist/vc/Issuer.d.ts +2 -2
  100. package/dist/vc/Issuer.js +5 -1
  101. package/dist/vc/Verifier.d.ts +2 -2
  102. package/dist/vc/Verifier.js +12 -6
  103. package/dist/vc/documentLoader.d.ts +5 -3
  104. package/dist/vc/documentLoader.js +5 -4
  105. package/package.json +4 -1
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI Migrate Command
4
+ *
5
+ * Migrates a CEL asset between layers in the Originals Protocol.
6
+ *
7
+ * Migration paths:
8
+ * - peer → webvh: Requires --domain
9
+ * - webvh → btco: Requires --wallet
10
+ *
11
+ * Usage: originals-cel migrate --log <path> --to <layer> [options]
12
+ */
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+ import { WebVHCelManager } from '../layers/WebVHCelManager';
16
+ import { BtcoCelManager } from '../layers/BtcoCelManager';
17
+ import { parseEventLogJson } from '../serialization/json';
18
+ import { parseEventLogCbor } from '../serialization/cbor';
19
+ import { serializeEventLogJson } from '../serialization/json';
20
+ import { serializeEventLogCbor } from '../serialization/cbor';
21
+ import { multikey } from '../../crypto/Multikey';
22
+ /**
23
+ * Valid target layers for migration
24
+ */
25
+ const VALID_LAYERS = ['webvh', 'btco'];
26
+ /**
27
+ * Loads and parses an event log from a file
28
+ * Supports both JSON (.json) and CBOR (.cbor) formats
29
+ */
30
+ function loadEventLog(filePath) {
31
+ if (!fs.existsSync(filePath)) {
32
+ throw new Error(`File not found: ${filePath}`);
33
+ }
34
+ const ext = path.extname(filePath).toLowerCase();
35
+ const content = fs.readFileSync(filePath);
36
+ if (ext === '.cbor') {
37
+ // Parse as CBOR binary
38
+ return parseEventLogCbor(new Uint8Array(content));
39
+ }
40
+ else {
41
+ // Default to JSON parsing
42
+ return parseEventLogJson(content.toString('utf-8'));
43
+ }
44
+ }
45
+ /**
46
+ * Detects the current layer from an event log by examining events
47
+ */
48
+ function detectCurrentLayer(log) {
49
+ if (!log.events || log.events.length === 0) {
50
+ throw new Error('Cannot detect layer from empty event log');
51
+ }
52
+ let currentLayer = 'peer';
53
+ for (const event of log.events) {
54
+ const data = event.data;
55
+ if (event.type === 'create' && data.layer) {
56
+ currentLayer = data.layer;
57
+ }
58
+ else if (event.type === 'update' && data.layer && data.targetDid) {
59
+ // This is a migration event
60
+ currentLayer = data.layer;
61
+ }
62
+ }
63
+ return currentLayer;
64
+ }
65
+ /**
66
+ * Extracts the current DID from an event log
67
+ */
68
+ function getCurrentDid(log) {
69
+ if (!log.events || log.events.length === 0) {
70
+ throw new Error('Cannot extract DID from empty event log');
71
+ }
72
+ let currentDid;
73
+ for (const event of log.events) {
74
+ const data = event.data;
75
+ if (event.type === 'create' && data.did) {
76
+ currentDid = data.did;
77
+ }
78
+ else if (event.type === 'update' && data.targetDid) {
79
+ // This is a migration event
80
+ currentDid = data.targetDid;
81
+ }
82
+ }
83
+ if (!currentDid) {
84
+ throw new Error('Could not determine current DID from event log');
85
+ }
86
+ return currentDid;
87
+ }
88
+ /**
89
+ * Validates the migration path
90
+ */
91
+ function validateMigrationPath(currentLayer, targetLayer) {
92
+ if (currentLayer === 'btco') {
93
+ throw new Error('Cannot migrate from btco layer - it is the final layer');
94
+ }
95
+ if (currentLayer === 'peer' && targetLayer === 'btco') {
96
+ throw new Error('Cannot migrate directly from peer to btco. Must migrate to webvh first.');
97
+ }
98
+ if (currentLayer === 'webvh' && targetLayer === 'webvh') {
99
+ throw new Error('Asset is already at webvh layer');
100
+ }
101
+ if (currentLayer === targetLayer) {
102
+ throw new Error(`Asset is already at ${targetLayer} layer`);
103
+ }
104
+ }
105
+ /**
106
+ * Loads an Ed25519 private key from a wallet file
107
+ * Supports both raw multibase format and JSON format { privateKey: "z..." }
108
+ */
109
+ async function loadWalletKey(walletPath) {
110
+ if (!fs.existsSync(walletPath)) {
111
+ throw new Error(`Wallet file not found: ${walletPath}`);
112
+ }
113
+ const content = fs.readFileSync(walletPath, 'utf-8').trim();
114
+ let privateKey;
115
+ // Try parsing as JSON first
116
+ try {
117
+ const parsed = JSON.parse(content);
118
+ if (parsed.privateKey) {
119
+ privateKey = parsed.privateKey;
120
+ }
121
+ else {
122
+ throw new Error('JSON wallet file must contain "privateKey" field');
123
+ }
124
+ }
125
+ catch (e) {
126
+ // Not JSON, treat as raw multibase key
127
+ if (content.startsWith('z')) {
128
+ privateKey = content;
129
+ }
130
+ else {
131
+ throw new Error('Wallet file must contain a z-base58btc multibase-encoded Ed25519 private key');
132
+ }
133
+ }
134
+ // Validate and decode to get public key
135
+ const decoded = multikey.decodePrivateKey(privateKey);
136
+ if (decoded.type !== 'Ed25519') {
137
+ throw new Error(`Expected Ed25519 key, got ${decoded.type}`);
138
+ }
139
+ // Derive public key from private key
140
+ const ed25519 = await import('@noble/ed25519');
141
+ const publicKeyBytes = await ed25519.getPublicKeyAsync(decoded.key);
142
+ const publicKey = multikey.encodePublicKey(publicKeyBytes, 'Ed25519');
143
+ return { privateKey, publicKey };
144
+ }
145
+ /**
146
+ * Creates a signer function from a private key
147
+ */
148
+ function createSigner(privateKey, publicKey) {
149
+ return async (data) => {
150
+ const ed25519 = await import('@noble/ed25519');
151
+ const decoded = multikey.decodePrivateKey(privateKey);
152
+ // Serialize data for signing (deterministic JSON)
153
+ const dataStr = JSON.stringify(data, Object.keys(data).sort());
154
+ const dataBytes = new TextEncoder().encode(dataStr);
155
+ // Sign the data
156
+ const signature = await ed25519.signAsync(dataBytes, decoded.key);
157
+ const proofValue = multikey.encodeMultibase(new Uint8Array(signature));
158
+ return {
159
+ type: 'DataIntegrityProof',
160
+ cryptosuite: 'eddsa-jcs-2022',
161
+ created: new Date().toISOString(),
162
+ verificationMethod: `did:key:${publicKey}#${publicKey}`,
163
+ proofPurpose: 'assertionMethod',
164
+ proofValue,
165
+ };
166
+ };
167
+ }
168
+ /**
169
+ * Creates a mock BitcoinManager for testing/development
170
+ * In production, this would be a real BitcoinManager instance
171
+ */
172
+ function createMockBitcoinManager() {
173
+ // For CLI use, we create a minimal mock that satisfies the interface
174
+ // Real Bitcoin integration requires additional configuration
175
+ return {
176
+ inscribeData: async (data) => {
177
+ // Generate mock Bitcoin transaction details
178
+ const timestamp = Date.now();
179
+ const mockTxid = `cli_mock_tx_${timestamp.toString(16)}`;
180
+ const mockInscriptionId = `cli_mock_inscription_${timestamp.toString(16)}i0`;
181
+ console.error('\nāš ļø Note: Using mock Bitcoin manager for CLI migration.');
182
+ console.error(' For real Bitcoin inscriptions, use the SDK programmatically.\n');
183
+ return {
184
+ txid: mockTxid,
185
+ inscriptionId: mockInscriptionId,
186
+ satoshi: 10000,
187
+ blockHeight: 0, // Will be set when confirmed
188
+ };
189
+ },
190
+ getInscription: async (inscriptionId) => {
191
+ return { inscriptionId, confirmed: false };
192
+ },
193
+ };
194
+ }
195
+ /**
196
+ * Execute the migrate command
197
+ */
198
+ export async function migrateCommand(flags) {
199
+ // Check for help flag
200
+ if (flags.help || flags.h) {
201
+ return {
202
+ success: true,
203
+ message: 'Use --help with the main CLI for full help text',
204
+ };
205
+ }
206
+ // Validate required arguments
207
+ if (!flags.log) {
208
+ return {
209
+ success: false,
210
+ message: 'Error: --log is required. Usage: originals-cel migrate --log <path> --to <layer>',
211
+ };
212
+ }
213
+ if (!flags.to) {
214
+ return {
215
+ success: false,
216
+ message: 'Error: --to is required. Valid layers: webvh, btco',
217
+ };
218
+ }
219
+ // Validate target layer
220
+ const targetLayer = flags.to.toLowerCase();
221
+ if (!VALID_LAYERS.includes(targetLayer)) {
222
+ return {
223
+ success: false,
224
+ message: `Error: Invalid target layer "${flags.to}". Valid layers: webvh, btco`,
225
+ };
226
+ }
227
+ // Validate layer-specific requirements
228
+ if (targetLayer === 'webvh' && !flags.domain) {
229
+ return {
230
+ success: false,
231
+ message: 'Error: --domain is required for webvh migration. Usage: originals-cel migrate --log <path> --to webvh --domain example.com',
232
+ };
233
+ }
234
+ if (targetLayer === 'btco' && !flags.wallet) {
235
+ return {
236
+ success: false,
237
+ message: 'Error: --wallet is required for btco migration. Usage: originals-cel migrate --log <path> --to btco --wallet ./wallet.key',
238
+ };
239
+ }
240
+ // Load the event log
241
+ let eventLog;
242
+ try {
243
+ eventLog = loadEventLog(flags.log);
244
+ }
245
+ catch (e) {
246
+ return {
247
+ success: false,
248
+ message: `Error: Failed to load event log: ${e.message}`,
249
+ };
250
+ }
251
+ // Detect current layer and validate migration path
252
+ let currentLayer;
253
+ try {
254
+ currentLayer = detectCurrentLayer(eventLog);
255
+ validateMigrationPath(currentLayer, targetLayer);
256
+ }
257
+ catch (e) {
258
+ return {
259
+ success: false,
260
+ message: `Error: Invalid migration path: ${e.message}`,
261
+ };
262
+ }
263
+ // Get source DID
264
+ let sourceDid;
265
+ try {
266
+ sourceDid = getCurrentDid(eventLog);
267
+ }
268
+ catch (e) {
269
+ return {
270
+ success: false,
271
+ message: `Error: ${e.message}`,
272
+ };
273
+ }
274
+ // Load wallet key for signing
275
+ let privateKey;
276
+ let publicKey;
277
+ if (flags.wallet) {
278
+ try {
279
+ const loaded = await loadWalletKey(flags.wallet);
280
+ privateKey = loaded.privateKey;
281
+ publicKey = loaded.publicKey;
282
+ }
283
+ catch (e) {
284
+ return {
285
+ success: false,
286
+ message: `Error: Failed to load wallet: ${e.message}`,
287
+ };
288
+ }
289
+ }
290
+ else {
291
+ // Generate temporary key for signing (for webvh migration without wallet)
292
+ const ed25519 = await import('@noble/ed25519');
293
+ const privateKeyBytes = ed25519.utils.randomPrivateKey();
294
+ const publicKeyBytes = await ed25519.getPublicKeyAsync(privateKeyBytes);
295
+ privateKey = multikey.encodePrivateKey(privateKeyBytes, 'Ed25519');
296
+ publicKey = multikey.encodePublicKey(publicKeyBytes, 'Ed25519');
297
+ console.error('\nāš ļø Generated temporary signing key. Save the key below for future operations:');
298
+ console.error(`Private Key: ${privateKey}`);
299
+ console.error(`Public Key: ${publicKey}\n`);
300
+ }
301
+ // Create signer
302
+ const signer = createSigner(privateKey, publicKey);
303
+ // Perform migration
304
+ let migratedLog;
305
+ let targetDid;
306
+ try {
307
+ if (targetLayer === 'webvh') {
308
+ // Migrate to webvh layer
309
+ const manager = new WebVHCelManager(signer, flags.domain, []);
310
+ migratedLog = await manager.migrate(eventLog);
311
+ // Extract target DID from migration event
312
+ const migrationEvent = migratedLog.events[migratedLog.events.length - 1];
313
+ const migrationData = migrationEvent.data;
314
+ targetDid = migrationData.targetDid;
315
+ }
316
+ else if (targetLayer === 'btco') {
317
+ // Migrate to btco layer
318
+ const bitcoinManager = createMockBitcoinManager();
319
+ const manager = new BtcoCelManager(signer, bitcoinManager);
320
+ migratedLog = await manager.migrate(eventLog);
321
+ // Extract target DID from migration event
322
+ const migrationEvent = migratedLog.events[migratedLog.events.length - 1];
323
+ const migrationData = migrationEvent.data;
324
+ targetDid = migrationData.targetDid;
325
+ }
326
+ else {
327
+ return {
328
+ success: false,
329
+ message: `Error: Unknown target layer: ${targetLayer}`,
330
+ };
331
+ }
332
+ }
333
+ catch (e) {
334
+ return {
335
+ success: false,
336
+ message: `Error: Migration failed: ${e.message}`,
337
+ };
338
+ }
339
+ // Determine output format
340
+ const format = (flags.format || 'json').toLowerCase();
341
+ if (format !== 'json' && format !== 'cbor') {
342
+ return {
343
+ success: false,
344
+ message: 'Error: --format must be "json" or "cbor"',
345
+ };
346
+ }
347
+ // Serialize output
348
+ let output;
349
+ try {
350
+ if (format === 'cbor') {
351
+ output = serializeEventLogCbor(migratedLog);
352
+ }
353
+ else {
354
+ output = serializeEventLogJson(migratedLog);
355
+ }
356
+ }
357
+ catch (e) {
358
+ return {
359
+ success: false,
360
+ message: `Error: Failed to serialize event log: ${e.message}`,
361
+ };
362
+ }
363
+ // Write output
364
+ if (flags.output) {
365
+ try {
366
+ if (format === 'cbor') {
367
+ fs.writeFileSync(flags.output, output);
368
+ }
369
+ else {
370
+ fs.writeFileSync(flags.output, output, 'utf-8');
371
+ }
372
+ }
373
+ catch (e) {
374
+ return {
375
+ success: false,
376
+ message: `Error: Failed to write output file: ${e.message}`,
377
+ };
378
+ }
379
+ }
380
+ else {
381
+ // Write to stdout
382
+ if (format === 'cbor') {
383
+ // For CBOR, output as base64 to stdout since it's binary
384
+ const base64 = Buffer.from(output).toString('base64');
385
+ process.stdout.write(base64);
386
+ }
387
+ else {
388
+ process.stdout.write(output);
389
+ }
390
+ }
391
+ // Build success message
392
+ const layerTransition = `${currentLayer} → ${targetLayer}`;
393
+ const outputInfo = flags.output ? ` and saved to ${flags.output}` : '';
394
+ console.error(`\nāœ… Migration complete: ${layerTransition}`);
395
+ console.error(` Source DID: ${sourceDid}`);
396
+ console.error(` Target DID: ${targetDid}${outputInfo}\n`);
397
+ return {
398
+ success: true,
399
+ message: `Migration complete: ${layerTransition}`,
400
+ log: migratedLog,
401
+ sourceDid,
402
+ targetDid,
403
+ targetLayer,
404
+ };
405
+ }
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI Verify Command
4
+ *
5
+ * Verifies a CEL event log by checking all proofs and the hash chain integrity.
6
+ * Outputs event-by-event breakdown with witness attestation details.
7
+ *
8
+ * Usage: originals-cel verify --log <path> [options]
9
+ */
10
+ import type { VerificationResult } from '../types';
11
+ /**
12
+ * Flags parsed from command line arguments
13
+ */
14
+ export interface VerifyFlags {
15
+ log?: string;
16
+ help?: boolean;
17
+ h?: boolean;
18
+ }
19
+ /**
20
+ * Result of the verify command
21
+ */
22
+ export interface VerifyResult {
23
+ success: boolean;
24
+ message: string;
25
+ verified?: boolean;
26
+ result?: VerificationResult;
27
+ }
28
+ /**
29
+ * Execute the verify command
30
+ */
31
+ export declare function verifyCommand(flags: VerifyFlags): Promise<VerifyResult>;
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI Verify Command
4
+ *
5
+ * Verifies a CEL event log by checking all proofs and the hash chain integrity.
6
+ * Outputs event-by-event breakdown with witness attestation details.
7
+ *
8
+ * Usage: originals-cel verify --log <path> [options]
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { verifyEventLog } from '../algorithms/verifyEventLog';
13
+ import { parseEventLogJson } from '../serialization/json';
14
+ import { parseEventLogCbor } from '../serialization/cbor';
15
+ /**
16
+ * Check if a proof is a WitnessProof (has witnessedAt field)
17
+ */
18
+ function isWitnessProof(proof) {
19
+ return 'witnessedAt' in proof && typeof proof.witnessedAt === 'string';
20
+ }
21
+ /**
22
+ * Loads and parses an event log from a file
23
+ * Supports both JSON (.json) and CBOR (.cbor) formats
24
+ */
25
+ function loadEventLog(filePath) {
26
+ if (!fs.existsSync(filePath)) {
27
+ throw new Error(`File not found: ${filePath}`);
28
+ }
29
+ const ext = path.extname(filePath).toLowerCase();
30
+ const content = fs.readFileSync(filePath);
31
+ if (ext === '.cbor') {
32
+ // Parse as CBOR binary
33
+ return parseEventLogCbor(new Uint8Array(content));
34
+ }
35
+ else {
36
+ // Default to JSON parsing
37
+ return parseEventLogJson(content.toString('utf-8'));
38
+ }
39
+ }
40
+ /**
41
+ * Format a timestamp for display
42
+ */
43
+ function formatTimestamp(timestamp) {
44
+ try {
45
+ const date = new Date(timestamp);
46
+ return date.toISOString();
47
+ }
48
+ catch {
49
+ return timestamp;
50
+ }
51
+ }
52
+ /**
53
+ * Format proof details for display
54
+ */
55
+ function formatProofDetails(proof, indent = ' ') {
56
+ const lines = [];
57
+ lines.push(`${indent}Cryptosuite: ${proof.cryptosuite}`);
58
+ lines.push(`${indent}Created: ${formatTimestamp(proof.created)}`);
59
+ lines.push(`${indent}Verification Method: ${proof.verificationMethod}`);
60
+ lines.push(`${indent}Proof Purpose: ${proof.proofPurpose}`);
61
+ // Truncate proof value for display
62
+ const truncatedProof = proof.proofValue.length > 60
63
+ ? `${proof.proofValue.substring(0, 57)}...`
64
+ : proof.proofValue;
65
+ lines.push(`${indent}Proof Value: ${truncatedProof}`);
66
+ // Add witness-specific info
67
+ if (isWitnessProof(proof)) {
68
+ lines.push(`${indent}šŸ• Witnessed At: ${formatTimestamp(proof.witnessedAt)}`);
69
+ }
70
+ return lines.join('\n');
71
+ }
72
+ /**
73
+ * Output verification result to stdout
74
+ */
75
+ function outputResult(log, result) {
76
+ console.log('\n═══════════════════════════════════════════════════════');
77
+ console.log(' CEL Event Log Verification Report');
78
+ console.log('═══════════════════════════════════════════════════════\n');
79
+ // Overall result
80
+ if (result.verified) {
81
+ console.log('āœ… VERIFICATION PASSED\n');
82
+ }
83
+ else {
84
+ console.log('āŒ VERIFICATION FAILED\n');
85
+ }
86
+ // Summary
87
+ console.log(`šŸ“Š Summary:`);
88
+ console.log(` Total Events: ${result.events.length}`);
89
+ const proofsPassed = result.events.filter(e => e.proofValid).length;
90
+ const chainPassed = result.events.filter(e => e.chainValid).length;
91
+ console.log(` Proofs Valid: ${proofsPassed}/${result.events.length}`);
92
+ console.log(` Chain Valid: ${chainPassed}/${result.events.length}`);
93
+ // Count witness proofs
94
+ let totalWitnessProofs = 0;
95
+ for (const event of log.events) {
96
+ for (const proof of event.proof) {
97
+ if (isWitnessProof(proof)) {
98
+ totalWitnessProofs++;
99
+ }
100
+ }
101
+ }
102
+ if (totalWitnessProofs > 0) {
103
+ console.log(` Witness Attestations: ${totalWitnessProofs}`);
104
+ }
105
+ console.log('');
106
+ // Event-by-event breakdown
107
+ console.log('šŸ“‹ Event-by-Event Breakdown:');
108
+ console.log('───────────────────────────────────────────────────────');
109
+ for (let i = 0; i < result.events.length; i++) {
110
+ const eventResult = result.events[i];
111
+ const event = log.events[i];
112
+ const proofIcon = eventResult.proofValid ? 'āœ…' : 'āŒ';
113
+ const chainIcon = eventResult.chainValid ? 'āœ…' : 'āŒ';
114
+ console.log(`\n Event ${i + 1} (${eventResult.type})`);
115
+ console.log(` ā”œā”€ Proof: ${proofIcon} ${eventResult.proofValid ? 'Valid' : 'Invalid'}`);
116
+ console.log(` └─ Chain: ${chainIcon} ${eventResult.chainValid ? 'Valid' : 'Invalid'}`);
117
+ // Show proof details
118
+ if (event.proof && event.proof.length > 0) {
119
+ console.log(`\n Proofs (${event.proof.length}):`);
120
+ for (let j = 0; j < event.proof.length; j++) {
121
+ const proof = event.proof[j];
122
+ const isWitness = isWitnessProof(proof);
123
+ const proofLabel = isWitness ? 'šŸ” Witness Proof' : 'šŸ” Controller Proof';
124
+ console.log(`\n [${j + 1}] ${proofLabel}`);
125
+ console.log(formatProofDetails(proof, ' '));
126
+ }
127
+ }
128
+ // Show errors for this event
129
+ if (eventResult.errors && eventResult.errors.length > 0) {
130
+ console.log(`\n āš ļø Errors:`);
131
+ for (const error of eventResult.errors) {
132
+ console.log(` - ${error}`);
133
+ }
134
+ }
135
+ }
136
+ // Show global errors if any
137
+ if (result.errors && result.errors.length > 0) {
138
+ console.log('\n───────────────────────────────────────────────────────');
139
+ console.log('āš ļø All Errors:');
140
+ for (const error of result.errors) {
141
+ console.log(` - ${error}`);
142
+ }
143
+ }
144
+ console.log('\n═══════════════════════════════════════════════════════\n');
145
+ }
146
+ /**
147
+ * Execute the verify command
148
+ */
149
+ export async function verifyCommand(flags) {
150
+ // Check for help flag
151
+ if (flags.help || flags.h) {
152
+ return {
153
+ success: true,
154
+ message: 'Use --help with the main CLI for full help text',
155
+ };
156
+ }
157
+ // Validate required arguments
158
+ if (!flags.log) {
159
+ return {
160
+ success: false,
161
+ message: 'Error: --log is required. Usage: originals-cel verify --log <path>',
162
+ };
163
+ }
164
+ // Load the event log
165
+ let eventLog;
166
+ try {
167
+ eventLog = loadEventLog(flags.log);
168
+ }
169
+ catch (e) {
170
+ return {
171
+ success: false,
172
+ message: `Error: Failed to load event log: ${e.message}`,
173
+ };
174
+ }
175
+ // Verify the event log
176
+ let result;
177
+ try {
178
+ result = await verifyEventLog(eventLog);
179
+ }
180
+ catch (e) {
181
+ return {
182
+ success: false,
183
+ message: `Error: Verification failed: ${e.message}`,
184
+ };
185
+ }
186
+ // Output the result
187
+ outputResult(eventLog, result);
188
+ // Return result with exit code information
189
+ if (result.verified) {
190
+ return {
191
+ success: true,
192
+ verified: true,
193
+ message: 'Verification passed: All proofs valid and hash chain intact',
194
+ result,
195
+ };
196
+ }
197
+ else {
198
+ return {
199
+ success: true, // Command ran successfully, but verification failed
200
+ verified: false,
201
+ message: `Verification failed: ${result.errors.length} error(s) found`,
202
+ result,
203
+ };
204
+ }
205
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * CEL Hash Utilities
3
+ *
4
+ * Computes and verifies Multibase-encoded Multihash digests as specified
5
+ * in the CEL specification for external references.
6
+ *
7
+ * @see https://w3c-ccg.github.io/cel-spec/
8
+ */
9
+ /**
10
+ * Computes a CEL-compliant digestMultibase from content.
11
+ *
12
+ * Uses sha2-256 hash algorithm and encodes as Multibase base64url-nopad (prefix 'u').
13
+ *
14
+ * @param content - The raw bytes to hash
15
+ * @returns Multibase-encoded digest string (e.g., "uEiDm9F...")
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const digest = computeDigestMultibase(new TextEncoder().encode("hello"));
20
+ * // Returns: "uLCA..."
21
+ * ```
22
+ */
23
+ export declare function computeDigestMultibase(content: Uint8Array): string;
24
+ /**
25
+ * Verifies that content matches a given digestMultibase.
26
+ *
27
+ * @param content - The raw bytes to verify
28
+ * @param digest - The expected digestMultibase string
29
+ * @returns True if the content hash matches the digest, false otherwise
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const content = new TextEncoder().encode("hello");
34
+ * const digest = computeDigestMultibase(content);
35
+ * const isValid = verifyDigestMultibase(content, digest); // true
36
+ * ```
37
+ */
38
+ export declare function verifyDigestMultibase(content: Uint8Array, digest: string): boolean;
39
+ /**
40
+ * Decodes a digestMultibase string back to raw hash bytes.
41
+ *
42
+ * @param digest - The Multibase-encoded digest string
43
+ * @returns The raw SHA-256 hash bytes
44
+ * @throws Error if the digest is not a valid Multibase base64url string
45
+ */
46
+ export declare function decodeDigestMultibase(digest: string): Uint8Array;