bb-signer 0.5.0 → 0.5.1

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 (2) hide show
  1. package/index.js +66 -31
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -33,7 +33,7 @@ import { dirname, join } from "path";
33
33
  import { fileURLToPath } from "url";
34
34
  import * as ed from "@noble/ed25519";
35
35
  import bs58 from "bs58";
36
- import { getOrCreateIdentity, getProxyUrl, validateProfileName } from "./identity.js";
36
+ import { getOrCreateIdentity, loadIdentity, getProxyUrl, validateProfileName } from "./identity.js";
37
37
  import { signEvent, cleanEvent } from "./crypto.js";
38
38
  import { submitToRelay } from "./submit.js";
39
39
 
@@ -98,6 +98,17 @@ setTimeout(async () => {
98
98
 
99
99
  // --- Helpers ---
100
100
 
101
+ /**
102
+ * Resolve identity for a tool call.
103
+ * If profile is specified, load that profile's identity on-demand.
104
+ * Otherwise, use the server's startup identity.
105
+ */
106
+ function resolveIdentity(profileArg) {
107
+ if (!profileArg) return identity;
108
+ validateProfileName(profileArg);
109
+ return loadIdentity(profileArg);
110
+ }
111
+
101
112
  function validateTopic(topic) {
102
113
  if (!topic || typeof topic !== "string") throw new Error("topic is required");
103
114
  if (topic.length > MAX_TOPIC_LENGTH) throw new Error(`topic exceeds ${MAX_TOPIC_LENGTH} chars`);
@@ -110,11 +121,11 @@ function validateContent(content) {
110
121
  }
111
122
  }
112
123
 
113
- function buildEvent(kind, topic, payload, { refs, tags, to, parents } = {}) {
124
+ function buildEvent(kind, topic, payload, id, { refs, tags, to, parents } = {}) {
114
125
  const event = {
115
126
  v: 1,
116
127
  kind,
117
- agent_pubkey: identity.publicKeyBase58,
128
+ agent_pubkey: id.publicKeyBase58,
118
129
  created_at: Date.now(),
119
130
  topic,
120
131
  payload,
@@ -130,9 +141,9 @@ function roomForKind(kind) {
130
141
  return kind === "INFO" ? "bus" : "requests";
131
142
  }
132
143
 
133
- async function buildSignSubmit(kind, topic, payload, opts = {}) {
134
- const event = buildEvent(kind, topic, payload, opts);
135
- const signed = signEvent(event, identity.secretKey);
144
+ async function buildSignSubmit(kind, topic, payload, id, opts = {}) {
145
+ const event = buildEvent(kind, topic, payload, id, opts);
146
+ const signed = signEvent(event, id.secretKey);
136
147
  const cleaned = cleanEvent(signed);
137
148
  const room = roomForKind(kind);
138
149
  const result = await submitToRelay(proxyUrl, cleaned, room);
@@ -169,6 +180,11 @@ const server = new Server(
169
180
  }
170
181
  );
171
182
 
183
+ // Shared profile property for all tools that sign
184
+ const profileProp = {
185
+ profile: { type: "string", description: "Optional named profile to sign as (e.g., 'critic', 'scout'). Uses default identity if omitted." },
186
+ };
187
+
172
188
  // List available tools
173
189
  server.setRequestHandler(ListToolsRequestSchema, async () => {
174
190
  return {
@@ -183,6 +199,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
183
199
  topic: { type: "string", description: "Hierarchical topic (e.g., 'news.crypto')" },
184
200
  content: { type: "string", description: "Text content to publish" },
185
201
  tags: { type: "object", description: "Optional key-value tags", additionalProperties: { type: "string" } },
202
+ ...profileProp,
186
203
  },
187
204
  required: ["topic", "content"],
188
205
  },
@@ -228,6 +245,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
228
245
  required: ["aeid", "relationship"],
229
246
  },
230
247
  },
248
+ ...profileProp,
231
249
  },
232
250
  required: ["topic", "question"],
233
251
  },
@@ -242,6 +260,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
242
260
  topic: { type: "string", description: "Topic (should match the request's topic)" },
243
261
  content: { type: "string", description: "Your response/answer" },
244
262
  receiver_address: { type: "string", description: "Your on-chain address for bounty payment (if request has bounty)" },
263
+ ...profileProp,
245
264
  },
246
265
  required: ["request_id", "topic", "content"],
247
266
  },
@@ -264,6 +283,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
264
283
  tx_proof: { type: "string", description: "On-chain operation URL" },
265
284
  },
266
285
  },
286
+ ...profileProp,
267
287
  },
268
288
  required: ["request_id", "fulfill_id", "topic"],
269
289
  },
@@ -277,6 +297,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
277
297
  request_id: { type: "string", description: "AEID of the REQUEST to cancel" },
278
298
  topic: { type: "string", description: "Topic (should match the request's topic)" },
279
299
  reason: { type: "string", description: "Optional reason for cancellation" },
300
+ ...profileProp,
280
301
  },
281
302
  required: ["request_id", "topic"],
282
303
  },
@@ -290,6 +311,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
290
311
  parent_aeid: { type: "string", description: "AEID of the event to comment on" },
291
312
  topic: { type: "string", description: "Topic (should match the parent event's topic)" },
292
313
  content: { type: "string", description: "Your comment text" },
314
+ ...profileProp,
293
315
  },
294
316
  required: ["parent_aeid", "topic", "content"],
295
317
  },
@@ -301,6 +323,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
301
323
  type: "object",
302
324
  properties: {
303
325
  aeid: { type: "string", description: "AEID of the event to upvote" },
326
+ ...profileProp,
304
327
  },
305
328
  required: ["aeid"],
306
329
  },
@@ -312,6 +335,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
312
335
  type: "object",
313
336
  properties: {
314
337
  aeid: { type: "string", description: "AEID of the event to downvote" },
338
+ ...profileProp,
315
339
  },
316
340
  required: ["aeid"],
317
341
  },
@@ -326,6 +350,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
326
350
  type: "string",
327
351
  description: "URL of your public post (GitHub gist, Twitter/X, Mastodon) containing 'BB, I claim <pubkey>'"
328
352
  },
353
+ ...profileProp,
329
354
  },
330
355
  required: ["post_url"],
331
356
  },
@@ -340,6 +365,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
340
365
  type: "string",
341
366
  description: "Phone number in E.164 format (e.g., +14155551234)"
342
367
  },
368
+ ...profileProp,
343
369
  },
344
370
  required: ["phone_number"],
345
371
  },
@@ -358,6 +384,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
358
384
  type: "string",
359
385
  description: "The 6-digit verification code from SMS"
360
386
  },
387
+ ...profileProp,
361
388
  },
362
389
  required: ["phone_number", "code"],
363
390
  },
@@ -372,6 +399,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
372
399
  type: "string",
373
400
  description: "Your display name (max 50 chars). Omit or set to null to clear."
374
401
  },
402
+ ...profileProp,
375
403
  },
376
404
  },
377
405
  },
@@ -382,7 +410,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
382
410
  description: "Get your agent's public key.",
383
411
  inputSchema: {
384
412
  type: "object",
385
- properties: {},
413
+ properties: {
414
+ ...profileProp,
415
+ },
386
416
  },
387
417
  },
388
418
 
@@ -409,6 +439,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
409
439
  },
410
440
  required: ["v", "kind", "agent_pubkey", "created_at", "topic", "payload"],
411
441
  },
442
+ ...profileProp,
412
443
  },
413
444
  required: ["unsigned_event"],
414
445
  },
@@ -423,6 +454,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
423
454
  type: "string",
424
455
  description: "The text message to sign",
425
456
  },
457
+ ...profileProp,
426
458
  },
427
459
  required: ["message"],
428
460
  },
@@ -436,10 +468,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
436
468
  const { name, arguments: args } = request.params;
437
469
 
438
470
  try {
471
+ // Resolve identity for this call (profile param overrides default)
472
+ const id = resolveIdentity(args.profile);
473
+
439
474
  // --- Core tools ---
440
475
 
441
476
  if (name === "get_identity") {
442
- return ok({ pubkey: identity.publicKeyBase58 });
477
+ return ok({ pubkey: id.publicKeyBase58, profile: args.profile || null });
443
478
  }
444
479
 
445
480
  if (name === "sign_message") {
@@ -448,19 +483,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
448
483
  return err("missing required parameter 'message'");
449
484
  }
450
485
  const messageBytes = new TextEncoder().encode(message);
451
- const signature = ed.sign(messageBytes, identity.secretKey);
486
+ const signature = ed.sign(messageBytes, id.secretKey);
452
487
  const signatureBase58 = bs58.encode(signature);
453
- return ok({ pubkey: identity.publicKeyBase58, signature: signatureBase58, message });
488
+ return ok({ pubkey: id.publicKeyBase58, signature: signatureBase58, message });
454
489
  }
455
490
 
456
491
  if (name === "sign") {
457
492
  const unsignedEvent = args.unsigned_event;
458
- if (unsignedEvent.agent_pubkey !== identity.publicKeyBase58) {
493
+ if (unsignedEvent.agent_pubkey !== id.publicKeyBase58) {
459
494
  return err(
460
- `Event pubkey (${unsignedEvent.agent_pubkey}) does not match your identity (${identity.publicKeyBase58}). Make sure you're using the correct pubkey when calling bb.publish.`
495
+ `Event pubkey (${unsignedEvent.agent_pubkey}) does not match your identity (${id.publicKeyBase58}). Make sure you're using the correct pubkey when calling bb.publish.`
461
496
  );
462
497
  }
463
- const signedEvent = signEvent(unsignedEvent, identity.secretKey);
498
+ const signedEvent = signEvent(unsignedEvent, id.secretKey);
464
499
  const cleaned = cleanEvent(signedEvent);
465
500
  return ok({ signed_event: cleaned });
466
501
  }
@@ -470,7 +505,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
470
505
  if (name === "publish") {
471
506
  validateTopic(args.topic);
472
507
  validateContent(args.content);
473
- const result = await buildSignSubmit("INFO", args.topic, { type: "text", data: args.content }, {
508
+ const result = await buildSignSubmit("INFO", args.topic, { type: "text", data: args.content }, id, {
474
509
  tags: args.tags,
475
510
  });
476
511
  return ok(result);
@@ -487,7 +522,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
487
522
  if (args.bounty.max_fulfills !== undefined) tags.bounty_max_fulfills = String(args.bounty.max_fulfills);
488
523
  if (args.bounty.split_allowed !== undefined) tags.bounty_split_allowed = String(args.bounty.split_allowed);
489
524
  }
490
- const result = await buildSignSubmit("REQUEST", args.topic, { type: "text", data: args.question }, {
525
+ const result = await buildSignSubmit("REQUEST", args.topic, { type: "text", data: args.question }, id, {
491
526
  to: args.to,
492
527
  tags: Object.keys(tags).length > 0 ? tags : undefined,
493
528
  parents: args.parents,
@@ -501,7 +536,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
501
536
  validateContent(args.content);
502
537
  const tags = {};
503
538
  if (args.receiver_address) tags.bounty_recipient = args.receiver_address;
504
- const result = await buildSignSubmit("FULFILL", args.topic, { type: "text", data: args.content }, {
539
+ const result = await buildSignSubmit("FULFILL", args.topic, { type: "text", data: args.content }, id, {
505
540
  refs: { request_id: args.request_id },
506
541
  tags: Object.keys(tags).length > 0 ? tags : undefined,
507
542
  });
@@ -517,7 +552,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
517
552
  if (args.payment) {
518
553
  if (args.payment.tx_proof) tags.bounty_tx_hash = args.payment.tx_proof;
519
554
  }
520
- const result = await buildSignSubmit("ACK", args.topic, { type: "text", data: "" }, {
555
+ const result = await buildSignSubmit("ACK", args.topic, { type: "text", data: "" }, id, {
521
556
  refs: { request_id: args.request_id, fulfill_id: args.fulfill_id },
522
557
  tags: Object.keys(tags).length > 0 ? tags : undefined,
523
558
  });
@@ -527,7 +562,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
527
562
  if (name === "cancel") {
528
563
  if (!args.request_id) return err("request_id is required");
529
564
  validateTopic(args.topic);
530
- const result = await buildSignSubmit("CANCEL", args.topic, { type: "text", data: args.reason || "" }, {
565
+ const result = await buildSignSubmit("CANCEL", args.topic, { type: "text", data: args.reason || "" }, id, {
531
566
  refs: { request_id: args.request_id },
532
567
  });
533
568
  return ok(result);
@@ -537,7 +572,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
537
572
  if (!args.parent_aeid) return err("parent_aeid is required");
538
573
  validateTopic(args.topic);
539
574
  validateContent(args.content);
540
- const result = await buildSignSubmit("COMMENT", args.topic, { type: "text", data: args.content }, {
575
+ const result = await buildSignSubmit("COMMENT", args.topic, { type: "text", data: args.content }, id, {
541
576
  refs: { parent_aeid: args.parent_aeid },
542
577
  });
543
578
  return ok(result);
@@ -548,7 +583,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
548
583
  const reaction = name === "upvote" ? "like" : "dislike";
549
584
  const message = `REACT:${args.aeid}:${reaction}`;
550
585
  const messageBytes = new TextEncoder().encode(message);
551
- const signature = ed.sign(messageBytes, identity.secretKey);
586
+ const signature = ed.sign(messageBytes, id.secretKey);
552
587
  const signatureBase58 = bs58.encode(signature);
553
588
 
554
589
  const resp = await fetch(`${proxyUrl}/react`, {
@@ -557,7 +592,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
557
592
  body: JSON.stringify({
558
593
  aeid: args.aeid,
559
594
  reaction,
560
- reactor_pubkey: identity.publicKeyBase58,
595
+ reactor_pubkey: id.publicKeyBase58,
561
596
  signature: signatureBase58,
562
597
  }),
563
598
  });
@@ -571,9 +606,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
571
606
  const postUrl = args.post_url;
572
607
 
573
608
  // Sign the verification message
574
- const message = `VERIFY_SOCIAL:${identity.publicKeyBase58}:${postUrl}`;
609
+ const message = `VERIFY_SOCIAL:${id.publicKeyBase58}:${postUrl}`;
575
610
  const messageBytes = new TextEncoder().encode(message);
576
- const signature = ed.sign(messageBytes, identity.secretKey);
611
+ const signature = ed.sign(messageBytes, id.secretKey);
577
612
  const signatureBase58 = bs58.encode(signature);
578
613
 
579
614
  // Get indexer URL (derive from proxy URL)
@@ -583,7 +618,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
583
618
  method: "POST",
584
619
  headers: { "Content-Type": "application/json" },
585
620
  body: JSON.stringify({
586
- pubkey: identity.publicKeyBase58,
621
+ pubkey: id.publicKeyBase58,
587
622
  post_url: postUrl,
588
623
  signature: signatureBase58,
589
624
  }),
@@ -631,9 +666,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
631
666
  const code = args.code;
632
667
 
633
668
  // Sign the verification message
634
- const message = `VERIFY_PHONE:${identity.publicKeyBase58}:${phoneNumber}`;
669
+ const message = `VERIFY_PHONE:${id.publicKeyBase58}:${phoneNumber}`;
635
670
  const messageBytes = new TextEncoder().encode(message);
636
- const signature = ed.sign(messageBytes, identity.secretKey);
671
+ const signature = ed.sign(messageBytes, id.secretKey);
637
672
  const signatureBase58 = bs58.encode(signature);
638
673
 
639
674
  // Get indexer URL (derive from proxy URL)
@@ -645,7 +680,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
645
680
  body: JSON.stringify({
646
681
  phone_number: phoneNumber,
647
682
  code: code,
648
- pubkey: identity.publicKeyBase58,
683
+ pubkey: id.publicKeyBase58,
649
684
  signature: signatureBase58,
650
685
  }),
651
686
  });
@@ -669,9 +704,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
669
704
 
670
705
  // Sign the message: SET_DISPLAY_NAME:{pubkey}:{display_name}
671
706
  const displayNameStr = displayName || "";
672
- const message = `SET_DISPLAY_NAME:${identity.publicKeyBase58}:${displayNameStr}`;
707
+ const message = `SET_DISPLAY_NAME:${id.publicKeyBase58}:${displayNameStr}`;
673
708
  const messageBytes = new TextEncoder().encode(message);
674
- const signature = ed.sign(messageBytes, identity.secretKey);
709
+ const signature = ed.sign(messageBytes, id.secretKey);
675
710
  const signatureBase58 = bs58.encode(signature);
676
711
 
677
712
  // Get indexer URL (derive from proxy URL)
@@ -681,7 +716,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
681
716
  method: "POST",
682
717
  headers: { "Content-Type": "application/json" },
683
718
  body: JSON.stringify({
684
- pubkey: identity.publicKeyBase58,
719
+ pubkey: id.publicKeyBase58,
685
720
  display_name: displayName,
686
721
  signature: signatureBase58,
687
722
  }),
@@ -691,7 +726,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
691
726
 
692
727
  return ok({
693
728
  success: true,
694
- pubkey: identity.publicKeyBase58,
729
+ pubkey: id.publicKeyBase58,
695
730
  display_name: result.display_name,
696
731
  });
697
732
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bb-signer",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Minimal local signer for BB - signs events for the agent collaboration network",
5
5
  "type": "module",
6
6
  "main": "index.js",