@tjamescouch/agentchat 0.13.0 → 0.14.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/bin/agentchat.js CHANGED
@@ -550,6 +550,8 @@ program
550
550
  .option('-e, --export', 'Export public key for sharing (JSON to stdout)')
551
551
  .option('-r, --rotate', 'Rotate to new keypair (signs new key with old key)')
552
552
  .option('--verify-chain', 'Verify the rotation chain')
553
+ .option('--revoke [reason]', 'Generate signed revocation notice (outputs JSON)')
554
+ .option('--verify-revocation <file>', 'Verify a revocation notice file')
553
555
  .option('-f, --file <path>', 'Identity file path', DEFAULT_IDENTITY_PATH)
554
556
  .option('-n, --name <name>', 'Agent name (for --generate)', `agent-${process.pid}`)
555
557
  .option('--force', 'Overwrite existing identity')
@@ -638,6 +640,43 @@ program
638
640
  process.exit(1);
639
641
  }
640
642
 
643
+ } else if (options.revoke) {
644
+ // Generate revocation notice
645
+ const identity = await Identity.load(options.file);
646
+ const reason = typeof options.revoke === 'string' ? options.revoke : 'revoked';
647
+
648
+ console.error(`Generating revocation notice for identity...`);
649
+ console.error(` Agent ID: ${identity.getAgentId()}`);
650
+ console.error(` Reason: ${reason}`);
651
+ console.error('');
652
+ console.error('WARNING: Publishing this notice declares your key as untrusted.');
653
+ console.error('');
654
+
655
+ const notice = identity.revoke(reason);
656
+ console.log(JSON.stringify(notice, null, 2));
657
+
658
+ } else if (options.verifyRevocation) {
659
+ // Verify a revocation notice file
660
+ const noticeData = await fs.readFile(options.verifyRevocation, 'utf-8');
661
+ const notice = JSON.parse(noticeData);
662
+
663
+ console.log('Verifying revocation notice...');
664
+ const isValid = Identity.verifyRevocation(notice);
665
+
666
+ if (isValid) {
667
+ console.log('Revocation notice is VALID');
668
+ console.log(` Agent ID: ${notice.agent_id}`);
669
+ console.log(` Fingerprint: ${notice.fingerprint}`);
670
+ console.log(` Reason: ${notice.reason}`);
671
+ console.log(` Timestamp: ${notice.timestamp}`);
672
+ if (notice.original_agent_id) {
673
+ console.log(` Original Agent ID: ${notice.original_agent_id}`);
674
+ }
675
+ } else {
676
+ console.error('Revocation notice is INVALID');
677
+ process.exit(1);
678
+ }
679
+
641
680
  } else {
642
681
  // Default: show if exists, otherwise show help
643
682
  const exists = await Identity.exists(options.file);
@@ -648,6 +687,10 @@ program
648
687
  console.log(` Fingerprint: ${identity.getFingerprint()}`);
649
688
  console.log(` Agent ID: ${identity.getAgentId()}`);
650
689
  console.log(` Created: ${identity.created}`);
690
+ if (identity.rotations.length > 0) {
691
+ console.log(` Rotations: ${identity.rotations.length}`);
692
+ console.log(` Original Agent ID: ${identity.getOriginalAgentId()}`);
693
+ }
651
694
  } else {
652
695
  console.log('No identity found.');
653
696
  console.log(`Use --generate to create one at ${options.file}`);
package/lib/identity.js CHANGED
@@ -295,4 +295,82 @@ export class Identity {
295
295
  getOriginalAgentId() {
296
296
  return pubkeyToAgentId(this.getOriginalPubkey());
297
297
  }
298
+
299
+ /**
300
+ * Generate a signed revocation notice for this identity
301
+ * A revocation notice declares that the key should no longer be trusted
302
+ * @param {string} reason - Reason for revocation (e.g., "compromised", "retired", "lost")
303
+ * @returns {object} Revocation notice with pubkey, reason, signature, timestamp
304
+ */
305
+ revoke(reason = 'revoked') {
306
+ if (!this.privkey) {
307
+ throw new Error('Private key not available - cannot create revocation notice');
308
+ }
309
+
310
+ const timestamp = new Date().toISOString();
311
+
312
+ // Create revocation content to sign
313
+ const revocationContent = JSON.stringify({
314
+ type: 'REVOCATION',
315
+ pubkey: this.pubkey,
316
+ agent_id: this.getAgentId(),
317
+ reason,
318
+ timestamp
319
+ });
320
+
321
+ // Sign with the key being revoked (proves ownership)
322
+ const signature = this.sign(revocationContent);
323
+
324
+ return {
325
+ type: 'REVOCATION',
326
+ pubkey: this.pubkey,
327
+ agent_id: this.getAgentId(),
328
+ fingerprint: this.getFingerprint(),
329
+ reason,
330
+ timestamp,
331
+ signature,
332
+ // Include rotation history for full chain verification
333
+ rotations: this.rotations.length > 0 ? this.rotations : undefined,
334
+ original_agent_id: this.rotations.length > 0 ? this.getOriginalAgentId() : undefined
335
+ };
336
+ }
337
+
338
+ /**
339
+ * Verify a revocation notice
340
+ * Checks that the signature is valid using the pubkey in the notice
341
+ * @param {object} notice - Revocation notice to verify
342
+ * @returns {boolean} True if signature is valid
343
+ */
344
+ static verifyRevocation(notice) {
345
+ if (!notice || notice.type !== 'REVOCATION') {
346
+ return false;
347
+ }
348
+
349
+ try {
350
+ const revocationContent = JSON.stringify({
351
+ type: 'REVOCATION',
352
+ pubkey: notice.pubkey,
353
+ agent_id: notice.agent_id,
354
+ reason: notice.reason,
355
+ timestamp: notice.timestamp
356
+ });
357
+
358
+ return Identity.verify(revocationContent, notice.signature, notice.pubkey);
359
+ } catch {
360
+ return false;
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Check if a pubkey has been revoked by checking against a revocation notice
366
+ * @param {string} pubkey - Public key to check
367
+ * @param {object} notice - Revocation notice
368
+ * @returns {boolean} True if the pubkey matches the revoked key
369
+ */
370
+ static isRevoked(pubkey, notice) {
371
+ if (!Identity.verifyRevocation(notice)) {
372
+ return false;
373
+ }
374
+ return notice.pubkey === pubkey;
375
+ }
298
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/agentchat",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Real-time IRC-like communication protocol for AI agents",
5
5
  "main": "lib/client.js",
6
6
  "files": [