@bcts/frost-hubert 1.0.0-alpha.23 → 1.0.0-beta.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 (154) hide show
  1. package/dist/bin/frost.cjs +345 -71
  2. package/dist/bin/frost.cjs.map +1 -1
  3. package/dist/bin/frost.mjs +345 -71
  4. package/dist/bin/frost.mjs.map +1 -1
  5. package/dist/busy-DkM2jAIZ.mjs +27 -0
  6. package/dist/busy-DkM2jAIZ.mjs.map +1 -0
  7. package/dist/busy-EZU7EKr6.cjs +38 -0
  8. package/dist/busy-EZU7EKr6.cjs.map +1 -0
  9. package/dist/cmd/index.cjs +28 -22
  10. package/dist/cmd/index.d.cts +2 -2
  11. package/dist/cmd/index.d.mts +2 -2
  12. package/dist/cmd/index.mjs +7 -3
  13. package/dist/cmd-Bw9_i2_f.cjs +130 -0
  14. package/dist/cmd-Bw9_i2_f.cjs.map +1 -0
  15. package/dist/cmd-CS1uJtuD.mjs +113 -0
  16. package/dist/cmd-CS1uJtuD.mjs.map +1 -0
  17. package/dist/common-CvH6dFvQ.mjs +282 -0
  18. package/dist/common-CvH6dFvQ.mjs.map +1 -0
  19. package/dist/common-DUWvtc08.mjs +96 -0
  20. package/dist/common-DUWvtc08.mjs.map +1 -0
  21. package/dist/common-lKP5EzHy.cjs +372 -0
  22. package/dist/common-lKP5EzHy.cjs.map +1 -0
  23. package/dist/common-lThIvJmZ.cjs +114 -0
  24. package/dist/common-lThIvJmZ.cjs.map +1 -0
  25. package/dist/dkg/index.cjs +6 -102
  26. package/dist/dkg/index.cjs.map +1 -1
  27. package/dist/dkg/index.d.cts +2 -2
  28. package/dist/dkg/index.d.mts +2 -2
  29. package/dist/dkg/index.mjs +4 -101
  30. package/dist/dkg/index.mjs.map +1 -1
  31. package/dist/finalize-BRgJK-Xv.cjs +402 -0
  32. package/dist/finalize-BRgJK-Xv.cjs.map +1 -0
  33. package/dist/finalize-BfLgzn8f.cjs +303 -0
  34. package/dist/finalize-BfLgzn8f.cjs.map +1 -0
  35. package/dist/finalize-CNTDj6aS.mjs +389 -0
  36. package/dist/finalize-CNTDj6aS.mjs.map +1 -0
  37. package/dist/finalize-EC3ikHQq.mjs +252 -0
  38. package/dist/finalize-EC3ikHQq.mjs.map +1 -0
  39. package/dist/finalize-IA01t_Qq.mjs +290 -0
  40. package/dist/finalize-IA01t_Qq.mjs.map +1 -0
  41. package/dist/finalize-UPyI1yb1.cjs +265 -0
  42. package/dist/finalize-UPyI1yb1.cjs.map +1 -0
  43. package/dist/{index-BkqLimZT.d.mts → index-B3c-80VS.d.cts} +26 -3
  44. package/dist/index-B3c-80VS.d.cts.map +1 -0
  45. package/dist/{index-BJlwbPYu.d.cts → index-BgbSGpxn.d.mts} +102 -80
  46. package/dist/index-BgbSGpxn.d.mts.map +1 -0
  47. package/dist/{index-BMbPgH0W.d.cts → index-C8QeHNwa.d.cts} +46 -2
  48. package/dist/{index-BMbPgH0W.d.cts.map → index-C8QeHNwa.d.cts.map} +1 -1
  49. package/dist/{index-DmxfT59Y.d.cts → index-D3QTWkEm.d.mts} +26 -3
  50. package/dist/index-D3QTWkEm.d.mts.map +1 -0
  51. package/dist/{index-DoV5HFvV.d.mts → index-DVbWyOs7.d.mts} +46 -2
  52. package/dist/{index-DoV5HFvV.d.mts.map → index-DVbWyOs7.d.mts.map} +1 -1
  53. package/dist/{index-Dzm1v4_4.d.mts → index-F1iNEAJR.d.cts} +102 -80
  54. package/dist/index-F1iNEAJR.d.cts.map +1 -0
  55. package/dist/index.cjs +31 -23
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +4 -4
  58. package/dist/index.d.mts +4 -4
  59. package/dist/index.mjs +9 -4
  60. package/dist/index.mjs.map +1 -1
  61. package/dist/invite-5277FQVT.cjs +274 -0
  62. package/dist/invite-5277FQVT.cjs.map +1 -0
  63. package/dist/invite-DUTcfTgX.cjs +109 -0
  64. package/dist/invite-DUTcfTgX.cjs.map +1 -0
  65. package/dist/invite-IU4n0dq2.mjs +96 -0
  66. package/dist/invite-IU4n0dq2.mjs.map +1 -0
  67. package/dist/invite-RU-OXTNS.mjs +219 -0
  68. package/dist/invite-RU-OXTNS.mjs.map +1 -0
  69. package/dist/parallel-D1R6ZGlY.cjs +318 -0
  70. package/dist/parallel-D1R6ZGlY.cjs.map +1 -0
  71. package/dist/parallel-D6zc6VW4.mjs +235 -0
  72. package/dist/parallel-D6zc6VW4.mjs.map +1 -0
  73. package/dist/proposed-participant-Dm1Eq6mX.cjs +141 -0
  74. package/dist/proposed-participant-Dm1Eq6mX.cjs.map +1 -0
  75. package/dist/proposed-participant-cWM7iUrO.mjs +129 -0
  76. package/dist/proposed-participant-cWM7iUrO.mjs.map +1 -0
  77. package/dist/receive-CAI-x4II.cjs +213 -0
  78. package/dist/receive-CAI-x4II.cjs.map +1 -0
  79. package/dist/receive-D2Nn68L7.mjs +188 -0
  80. package/dist/receive-D2Nn68L7.mjs.map +1 -0
  81. package/dist/receive-DA_KQEgk.mjs +177 -0
  82. package/dist/receive-DA_KQEgk.mjs.map +1 -0
  83. package/dist/receive-kZMsXhbK.cjs +190 -0
  84. package/dist/receive-kZMsXhbK.cjs.map +1 -0
  85. package/dist/registry/index.cjs +85 -10
  86. package/dist/registry/index.cjs.map +1 -1
  87. package/dist/registry/index.d.cts +1 -1
  88. package/dist/registry/index.d.mts +1 -1
  89. package/dist/registry/index.mjs +85 -10
  90. package/dist/registry/index.mjs.map +1 -1
  91. package/dist/{registry-loI1_Mh1.cjs → registry-9puTaRrD.cjs} +1 -1
  92. package/dist/{registry-loI1_Mh1.cjs.map → registry-9puTaRrD.cjs.map} +1 -1
  93. package/dist/{registry-CgrCZ4En.mjs → registry-BpCwtrRt.mjs} +1 -1
  94. package/dist/{registry-CgrCZ4En.mjs.map → registry-BpCwtrRt.mjs.map} +1 -1
  95. package/dist/round1-4Hyx8w0x.cjs +422 -0
  96. package/dist/round1-4Hyx8w0x.cjs.map +1 -0
  97. package/dist/round1-7v9LlE11.mjs +373 -0
  98. package/dist/round1-7v9LlE11.mjs.map +1 -0
  99. package/dist/round1-BHBjru1m.cjs +465 -0
  100. package/dist/round1-BHBjru1m.cjs.map +1 -0
  101. package/dist/round1-CMLKN2RR.mjs +195 -0
  102. package/dist/round1-CMLKN2RR.mjs.map +1 -0
  103. package/dist/round1-CWSXZx5R.cjs +208 -0
  104. package/dist/round1-CWSXZx5R.cjs.map +1 -0
  105. package/dist/round1-CcQCGlIT.mjs +208 -0
  106. package/dist/round1-CcQCGlIT.mjs.map +1 -0
  107. package/dist/round1-Cgm7j1kI.mjs +452 -0
  108. package/dist/round1-Cgm7j1kI.mjs.map +1 -0
  109. package/dist/round1-DQ0fnc1H.cjs +221 -0
  110. package/dist/round1-DQ0fnc1H.cjs.map +1 -0
  111. package/dist/round2-BWz9SQIi.cjs +305 -0
  112. package/dist/round2-BWz9SQIi.cjs.map +1 -0
  113. package/dist/round2-BkNRCXgS.mjs +292 -0
  114. package/dist/round2-BkNRCXgS.mjs.map +1 -0
  115. package/dist/round2-Bl2uK93U.mjs +450 -0
  116. package/dist/round2-Bl2uK93U.mjs.map +1 -0
  117. package/dist/round2-CdUT-AhH.cjs +499 -0
  118. package/dist/round2-CdUT-AhH.cjs.map +1 -0
  119. package/dist/round2-DOA3rnV-.mjs +280 -0
  120. package/dist/round2-DOA3rnV-.mjs.map +1 -0
  121. package/dist/round2-Dg24w-TU.mjs +397 -0
  122. package/dist/round2-Dg24w-TU.mjs.map +1 -0
  123. package/dist/round2-LylCa84n.cjs +293 -0
  124. package/dist/round2-LylCa84n.cjs.map +1 -0
  125. package/dist/round2-o2Q-GMbX.cjs +410 -0
  126. package/dist/round2-o2Q-GMbX.cjs.map +1 -0
  127. package/dist/storage-B-Gu68-O.cjs +79 -0
  128. package/dist/storage-B-Gu68-O.cjs.map +1 -0
  129. package/dist/storage-Bkkliz0K.mjs +74 -0
  130. package/dist/storage-Bkkliz0K.mjs.map +1 -0
  131. package/package.json +10 -10
  132. package/src/bin/frost.ts +849 -128
  133. package/src/cmd/common.ts +19 -1
  134. package/src/cmd/dkg/common.ts +97 -10
  135. package/src/cmd/dkg/coordinator/invite.ts +5 -2
  136. package/src/cmd/dkg/participant/finalize.ts +51 -17
  137. package/src/cmd/dkg/participant/round1.ts +39 -38
  138. package/src/cmd/dkg/participant/round2.ts +60 -26
  139. package/src/cmd/sign/coordinator/round2.ts +5 -1
  140. package/src/cmd/sign/participant/finalize.ts +6 -2
  141. package/src/cmd/sign/participant/receive.ts +5 -2
  142. package/src/dkg/group-invite.ts +12 -2
  143. package/src/dkg/proposed-participant.ts +32 -3
  144. package/src/registry/owner-record.ts +12 -0
  145. package/src/registry/participant-record.ts +35 -2
  146. package/src/registry/registry-impl.ts +74 -18
  147. package/dist/cmd-5yLeC_QL.mjs +0 -4708
  148. package/dist/cmd-5yLeC_QL.mjs.map +0 -1
  149. package/dist/cmd-BfZjC3Uh.cjs +0 -4847
  150. package/dist/cmd-BfZjC3Uh.cjs.map +0 -1
  151. package/dist/index-BJlwbPYu.d.cts.map +0 -1
  152. package/dist/index-BkqLimZT.d.mts.map +0 -1
  153. package/dist/index-DmxfT59Y.d.cts.map +0 -1
  154. package/dist/index-Dzm1v4_4.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receive-DA_KQEgk.mjs","names":[],"sources":["../src/cmd/sign/participant/receive.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * Sign participant receive command.\n *\n * Port of cmd/sign/participant/receive.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { type ARID, type XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport type { Envelope } from \"@bcts/envelope\";\n\nimport { Registry, resolveRegistryPath, type OwnerRecord } from \"../../../registry/index.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { parseAridUr, parseEnvelopeUr, formatNameWithOwnerMarker } from \"../../dkg/common.js\";\nimport { signingStateDir } from \"../common.js\";\n\n/**\n * Options for the sign receive command.\n */\nexport interface SignReceiveOptions {\n registryPath?: string;\n /** ARID or envelope UR string */\n request: string;\n timeoutSeconds?: number;\n /** Show request details only (info mode) */\n info?: boolean;\n /** Expected sender (XID UR or pet name) */\n sender?: string;\n}\n\n/**\n * Result of the sign receive command.\n */\nexport interface SignReceiveResult {\n sessionId: string;\n groupId: string;\n targetUr: string;\n coordinatorName: string;\n minSigners: number;\n participantNames: string[];\n}\n\n/**\n * Resolve sender from XID UR or pet name in registry.\n *\n * Port of `resolve_sender()` from cmd/dkg/common.rs lines 76-94.\n */\nfunction resolveSenderFromInput(registry: Registry, input: string): { xid: () => XID } {\n const trimmed = input.trim();\n if (trimmed === \"\") {\n throw new Error(\"Sender is required\");\n }\n\n // Try parsing as XID UR first\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, no-undef\n const { XID: XIDClass } = require(\"@bcts/components\");\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const xid = XIDClass.fromURString(trimmed) as XID;\n const record = registry.participant(xid);\n if (!record) {\n throw new Error(`Sender with XID ${xid.urString()} not found`);\n }\n return record.xidDocument();\n } catch {\n // Try looking up by pet name\n const result = registry.participantByPetName(trimmed);\n if (!result) {\n throw new Error(`Sender with pet name '${trimmed}' not found`);\n }\n return result[1].xidDocument();\n }\n}\n\n/**\n * Resolve sign invite from ARID or envelope UR.\n *\n * Port of `resolve_sign_request()` from cmd/sign/participant/receive.rs lines 250-284.\n */\nasync function resolveSignInviteEnvelope(\n client: StorageClient | undefined,\n selection: StorageSelection | undefined,\n request: string,\n timeout: number | undefined,\n): Promise<Envelope> {\n if (selection !== undefined && client !== undefined) {\n // Try to parse as ARID\n try {\n const arid = parseAridUr(request);\n const envelope = await getWithIndicator(client, arid, \"Sign invite\", timeout, false);\n if (envelope === undefined || envelope === null) {\n throw new Error(\"signInvite request not found in Hubert storage\");\n }\n return envelope;\n } catch {\n // Not an ARID, try as envelope\n }\n\n if (timeout !== undefined) {\n throw new Error(\"--timeout is only valid when retrieving requests from Hubert\");\n }\n return parseEnvelopeUr(request);\n }\n\n // No storage selection\n try {\n parseAridUr(request);\n throw new Error(\"Hubert storage parameters are required to retrieve requests by ARID\");\n } catch (e) {\n if (e instanceof Error && e.message.includes(\"Hubert storage parameters\")) {\n throw e;\n }\n // Not an ARID, parse as envelope\n }\n return parseEnvelopeUr(request);\n}\n\n/**\n * Get display name for sender from registry.\n *\n * Port of `resolve_sender_name()` from cmd/dkg/common.rs lines 96-116.\n */\nfunction resolveSenderName(registry: Registry, senderXid: XID): string | undefined {\n const owner = registry.owner();\n\n // Check if sender is the owner\n if (owner?.xid().urString() === senderXid.urString()) {\n const name = owner.petName() ?? senderXid.urString();\n return formatNameWithOwnerMarker(name, true);\n }\n\n // Look up in participants\n const record = registry.participant(senderXid);\n if (record) {\n const name = record.petName() ?? record.xid().urString();\n return formatNameWithOwnerMarker(name, false);\n }\n\n return undefined;\n}\n\n/**\n * Format participant names with owner marker.\n *\n * Port of `format_participant_names()` from cmd/sign/participant/receive.rs lines 286-309.\n */\nfunction formatParticipantNames(\n registry: Registry,\n participants: XID[],\n owner: OwnerRecord,\n): string[] {\n return participants.map((xid) => {\n const isOwner = xid.urString() === owner.xid().urString();\n let name: string;\n\n if (isOwner) {\n name = owner.petName() ?? xid.urString();\n } else {\n const record = registry.participant(xid);\n name = record?.petName() ?? xid.urString();\n }\n\n return formatNameWithOwnerMarker(name, isOwner);\n });\n}\n\n/**\n * Execute the sign participant receive command.\n *\n * Fetches and validates a sign invite from the coordinator.\n *\n * Port of `CommandArgs::exec()` from cmd/sign/participant/receive.rs lines 56-247.\n */\nexport async function receive(\n client: StorageClient | undefined,\n selection: StorageSelection | undefined,\n options: SignReceiveOptions,\n cwd: string,\n): Promise<SignReceiveResult> {\n // Validate timeout requires storage\n if (selection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n const owner = registry.owner();\n\n if (!owner) {\n throw new Error(\"Registry owner with private keys is required\");\n }\n\n // Resolve expected sender if provided\n let expectedSender: { xid: () => XID } | undefined;\n if (options.sender !== undefined && options.sender !== \"\") {\n expectedSender = resolveSenderFromInput(registry, options.sender);\n }\n\n // Resolve the invite envelope\n const envelope = await resolveSignInviteEnvelope(\n client,\n selection,\n options.request,\n options.timeoutSeconds,\n );\n\n const now: CborDate = CborDate.now();\n const recipientKeys = owner.xidDocument().inceptionPrivateKeys();\n\n if (recipientKeys === null || recipientKeys === undefined) {\n throw new Error(\"Owner XID document has no inception private keys\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef\n const { SealedRequest: SealedRequestClass } = require(\"@bcts/gstp\") as {\n SealedRequest: {\n tryFromEnvelope: (\n envelope: Envelope,\n expectedSender: XID | undefined,\n now: CborDate,\n recipientPrivateKeys: unknown,\n ) => SealedRequestInstance;\n };\n };\n\n interface SealedRequestInstance {\n sender: () => { xid: () => XID };\n function: () => { equals?: (other: unknown) => boolean; toString?: () => string };\n extractObjectForParameter: <T>(name: string) => T;\n objectForParameter: (name: string) => Envelope;\n objectsForParameter: (name: string) => ParticipantEntry[];\n }\n\n interface ParticipantEntry {\n extractSubject: () => XID;\n objectForPredicate: (name: string) => {\n decryptToRecipient: (keys: unknown) => {\n extractSubject: () => ARID;\n };\n };\n }\n\n const sealedRequest: SealedRequestInstance = SealedRequestClass.tryFromEnvelope(\n envelope,\n undefined,\n now,\n recipientKeys,\n );\n\n // Validate sender\n const senderXid = sealedRequest.sender().xid();\n\n if (expectedSender !== undefined) {\n if (senderXid.urString() !== expectedSender.xid().urString()) {\n throw new Error(\n `Request sender does not match expected sender (got ${senderXid.urString()}, expected ${expectedSender.xid().urString()})`,\n );\n }\n } else {\n const knownOwner = owner.xid().urString() === senderXid.urString();\n const knownParticipant = registry.participant(senderXid) !== undefined;\n if (!knownOwner && !knownParticipant) {\n throw new Error(`Request sender not found in registry: ${senderXid.urString()}`);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef\n const { Function: FunctionClass } = require(\"@bcts/envelope\") as {\n Function: { from: (name: string) => unknown };\n };\n\n // Validate function\n const requestFunction = sealedRequest.function();\n const expectedFunction = FunctionClass.from(\"signInvite\");\n const functionMatches =\n requestFunction.equals !== undefined\n ? requestFunction.equals(expectedFunction)\n : String(requestFunction) === String(expectedFunction);\n\n if (!functionMatches) {\n throw new Error(`Unexpected request function: ${String(requestFunction)}`);\n }\n\n // Extract parameters\n const validUntil = sealedRequest.extractObjectForParameter<CborDate>(\"validUntil\");\n if (validUntil <= now) {\n throw new Error(\"signInvite request has expired\");\n }\n\n const groupId = sealedRequest.extractObjectForParameter<ARID>(\"group\");\n const sessionId = sealedRequest.extractObjectForParameter<ARID>(\"session\");\n const minSigners = Number(sealedRequest.extractObjectForParameter<bigint | number>(\"minSigners\"));\n\n // Extract participants and find our response ARID\n const participantEntries = sealedRequest.objectsForParameter(\"participant\");\n const participants: XID[] = [];\n let responseArid: ARID | undefined;\n\n for (const entry of participantEntries) {\n const xid: XID = entry.extractSubject();\n if (xid.urString() === owner.xid().urString()) {\n const encryptedArid = entry.objectForPredicate(\"response_arid\");\n const aridEnv = encryptedArid.decryptToRecipient(recipientKeys);\n responseArid = aridEnv.extractSubject();\n }\n participants.push(xid);\n }\n\n // Validations\n if (participants.length === 0) {\n throw new Error(\"signInvite request contains no participants\");\n }\n if (minSigners < 2) {\n throw new Error(\"minSigners must be at least 2\");\n }\n if (minSigners > participants.length) {\n throw new Error(\"minSigners exceeds participant count\");\n }\n\n const ownerInParticipants = participants.some((p) => p.urString() === owner.xid().urString());\n if (!ownerInParticipants) {\n throw new Error(\"signInvite request does not include this participant\");\n }\n\n if (responseArid === undefined) {\n throw new Error(\"signInvite request missing response ARID\");\n }\n\n // Sort participants by XID byte order — mirrors Rust `XID::cmp`.\n // The earlier port used `urString().localeCompare(...)`, which\n // diverges from byte order for bytes ≥ 0x80 and is locale-aware.\n participants.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n const targetEnvelope = sealedRequest.objectForParameter(\"target\");\n\n const coordinatorName = resolveSenderName(registry, senderXid) ?? senderXid.urString();\n const participantNames = formatParticipantNames(registry, participants, owner);\n\n // Output\n console.log(`Group: ${groupId.urString()}`);\n console.log(`Coordinator: ${coordinatorName}`);\n console.log(`Min signers: ${minSigners}`);\n console.log(`Participants: ${participantNames.join(\", \")}`);\n console.log(\"Target:\");\n console.log(targetEnvelope.format());\n\n // Primary output for scripting: session ID on its own line (no header)\n console.log(sessionId.urString());\n\n // Persist request details for follow-up commands\n const stateDir = signingStateDir(registryPath, groupId.hex(), sessionId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const root: Record<string, unknown> = {\n request_envelope: envelope.urString(),\n group: groupId.urString(),\n session: sessionId.urString(),\n coordinator: senderXid.urString(),\n min_signers: minSigners,\n response_arid: responseArid.urString(),\n participants: participants.map((xid) => xid.urString()),\n target: targetEnvelope.urString(),\n };\n\n fs.writeFileSync(path.join(stateDir, \"sign_receive.json\"), JSON.stringify(root, null, 2));\n\n return {\n sessionId: sessionId.urString(),\n groupId: groupId.urString(),\n targetUr: targetEnvelope.urString(),\n coordinatorName,\n minSigners,\n participantNames,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAS,uBAAuB,UAAoB,OAAmC;CACrF,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GACd,OAAM,IAAI,MAAM,qBAAqB;AAIvC,KAAI;EAEF,MAAM,EAAE,KAAK,aAAA,UAAqB,mBAAmB;EAErD,MAAM,MAAM,SAAS,aAAa,QAAQ;EAC1C,MAAM,SAAS,SAAS,YAAY,IAAI;AACxC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,mBAAmB,IAAI,UAAU,CAAC,YAAY;AAEhE,SAAO,OAAO,aAAa;SACrB;EAEN,MAAM,SAAS,SAAS,qBAAqB,QAAQ;AACrD,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,yBAAyB,QAAQ,aAAa;AAEhE,SAAO,OAAO,GAAG,aAAa;;;;;;;;AASlC,eAAe,0BACb,QACA,WACA,SACA,SACmB;AACnB,KAAI,cAAc,KAAA,KAAa,WAAW,KAAA,GAAW;AAEnD,MAAI;GAEF,MAAM,WAAW,MAAM,iBAAiB,QAD3B,YAAY,QAC2B,EAAE,eAAe,SAAS,MAAM;AACpF,OAAI,aAAa,KAAA,KAAa,aAAa,KACzC,OAAM,IAAI,MAAM,iDAAiD;AAEnE,UAAO;UACD;AAIR,MAAI,YAAY,KAAA,EACd,OAAM,IAAI,MAAM,+DAA+D;AAEjF,SAAO,gBAAgB,QAAQ;;AAIjC,KAAI;AACF,cAAY,QAAQ;AACpB,QAAM,IAAI,MAAM,sEAAsE;UAC/E,GAAG;AACV,MAAI,aAAa,SAAS,EAAE,QAAQ,SAAS,4BAA4B,CACvE,OAAM;;AAIV,QAAO,gBAAgB,QAAQ;;;;;;;AAQjC,SAAS,kBAAkB,UAAoB,WAAoC;CACjF,MAAM,QAAQ,SAAS,OAAO;AAG9B,KAAI,OAAO,KAAK,CAAC,UAAU,KAAK,UAAU,UAAU,CAElD,QAAO,0BADM,MAAM,SAAS,IAAI,UAAU,UAAU,EACb,KAAK;CAI9C,MAAM,SAAS,SAAS,YAAY,UAAU;AAC9C,KAAI,OAEF,QAAO,0BADM,OAAO,SAAS,IAAI,OAAO,KAAK,CAAC,UAAU,EACjB,MAAM;;;;;;;AAWjD,SAAS,uBACP,UACA,cACA,OACU;AACV,QAAO,aAAa,KAAK,QAAQ;EAC/B,MAAM,UAAU,IAAI,UAAU,KAAK,MAAM,KAAK,CAAC,UAAU;EACzD,IAAI;AAEJ,MAAI,QACF,QAAO,MAAM,SAAS,IAAI,IAAI,UAAU;MAGxC,QADe,SAAS,YAAY,IACvB,EAAE,SAAS,IAAI,IAAI,UAAU;AAG5C,SAAO,0BAA0B,MAAM,QAAQ;GAC/C;;;;;;;;;AAUJ,eAAsB,QACpB,QACA,WACA,SACA,KAC4B;AAE5B,KAAI,cAAc,KAAA,KAAa,QAAQ,mBAAmB,KAAA,EACxD,OAAM,IAAI,MAAM,+CAA+C;CAGjE,MAAM,eAAe,oBAAoB,QAAQ,cAAc,IAAI;CACnE,MAAM,WAAW,SAAS,KAAK,aAAa;CAC5C,MAAM,QAAQ,SAAS,OAAO;AAE9B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,+CAA+C;CAIjE,IAAI;AACJ,KAAI,QAAQ,WAAW,KAAA,KAAa,QAAQ,WAAW,GACrD,kBAAiB,uBAAuB,UAAU,QAAQ,OAAO;CAInE,MAAM,WAAW,MAAM,0BACrB,QACA,WACA,QAAQ,SACR,QAAQ,eACT;CAED,MAAM,MAAgB,SAAS,KAAK;CACpC,MAAM,gBAAgB,MAAM,aAAa,CAAC,sBAAsB;AAEhE,KAAI,kBAAkB,QAAQ,kBAAkB,KAAA,EAC9C,OAAM,IAAI,MAAM,mDAAmD;CAIrE,MAAM,EAAE,eAAe,uBAAA,UAA+B,aAAa;CA4BnE,MAAM,gBAAuC,mBAAmB,gBAC9D,UACA,KAAA,GACA,KACA,cACD;CAGD,MAAM,YAAY,cAAc,QAAQ,CAAC,KAAK;AAE9C,KAAI,mBAAmB,KAAA;MACjB,UAAU,UAAU,KAAK,eAAe,KAAK,CAAC,UAAU,CAC1D,OAAM,IAAI,MACR,sDAAsD,UAAU,UAAU,CAAC,aAAa,eAAe,KAAK,CAAC,UAAU,CAAC,GACzH;QAEE;EACL,MAAM,aAAa,MAAM,KAAK,CAAC,UAAU,KAAK,UAAU,UAAU;EAClE,MAAM,mBAAmB,SAAS,YAAY,UAAU,KAAK,KAAA;AAC7D,MAAI,CAAC,cAAc,CAAC,iBAClB,OAAM,IAAI,MAAM,yCAAyC,UAAU,UAAU,GAAG;;CAKpF,MAAM,EAAE,UAAU,kBAAA,UAA0B,iBAAiB;CAK7D,MAAM,kBAAkB,cAAc,UAAU;CAChD,MAAM,mBAAmB,cAAc,KAAK,aAAa;AAMzD,KAAI,EAJF,gBAAgB,WAAW,KAAA,IACvB,gBAAgB,OAAO,iBAAiB,GACxC,OAAO,gBAAgB,KAAK,OAAO,iBAAiB,EAGxD,OAAM,IAAI,MAAM,gCAAgC,OAAO,gBAAgB,GAAG;AAK5E,KADmB,cAAc,0BAAoC,aACvD,IAAI,IAChB,OAAM,IAAI,MAAM,iCAAiC;CAGnD,MAAM,UAAU,cAAc,0BAAgC,QAAQ;CACtE,MAAM,YAAY,cAAc,0BAAgC,UAAU;CAC1E,MAAM,aAAa,OAAO,cAAc,0BAA2C,aAAa,CAAC;CAGjG,MAAM,qBAAqB,cAAc,oBAAoB,cAAc;CAC3E,MAAM,eAAsB,EAAE;CAC9B,IAAI;AAEJ,MAAK,MAAM,SAAS,oBAAoB;EACtC,MAAM,MAAW,MAAM,gBAAgB;AACvC,MAAI,IAAI,UAAU,KAAK,MAAM,KAAK,CAAC,UAAU,CAG3C,gBAFsB,MAAM,mBAAmB,gBAClB,CAAC,mBAAmB,cAC3B,CAAC,gBAAgB;AAEzC,eAAa,KAAK,IAAI;;AAIxB,KAAI,aAAa,WAAW,EAC1B,OAAM,IAAI,MAAM,8CAA8C;AAEhE,KAAI,aAAa,EACf,OAAM,IAAI,MAAM,gCAAgC;AAElD,KAAI,aAAa,aAAa,OAC5B,OAAM,IAAI,MAAM,uCAAuC;AAIzD,KAAI,CADwB,aAAa,MAAM,MAAM,EAAE,UAAU,KAAK,MAAM,KAAK,CAAC,UAAU,CACpE,CACtB,OAAM,IAAI,MAAM,uDAAuD;AAGzE,KAAI,iBAAiB,KAAA,EACnB,OAAM,IAAI,MAAM,2CAA2C;AAM7D,cAAa,MAAM,GAAG,MAAM,gBAAgB,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC;CAEpE,MAAM,iBAAiB,cAAc,mBAAmB,SAAS;CAEjE,MAAM,kBAAkB,kBAAkB,UAAU,UAAU,IAAI,UAAU,UAAU;CACtF,MAAM,mBAAmB,uBAAuB,UAAU,cAAc,MAAM;AAG9E,SAAQ,IAAI,UAAU,QAAQ,UAAU,GAAG;AAC3C,SAAQ,IAAI,gBAAgB,kBAAkB;AAC9C,SAAQ,IAAI,gBAAgB,aAAa;AACzC,SAAQ,IAAI,iBAAiB,iBAAiB,KAAK,KAAK,GAAG;AAC3D,SAAQ,IAAI,UAAU;AACtB,SAAQ,IAAI,eAAe,QAAQ,CAAC;AAGpC,SAAQ,IAAI,UAAU,UAAU,CAAC;CAGjC,MAAM,WAAW,gBAAgB,cAAc,QAAQ,KAAK,EAAE,UAAU,KAAK,CAAC;AAC9E,IAAG,UAAU,UAAU,EAAE,WAAW,MAAM,CAAC;CAE3C,MAAM,OAAgC;EACpC,kBAAkB,SAAS,UAAU;EACrC,OAAO,QAAQ,UAAU;EACzB,SAAS,UAAU,UAAU;EAC7B,aAAa,UAAU,UAAU;EACjC,aAAa;EACb,eAAe,aAAa,UAAU;EACtC,cAAc,aAAa,KAAK,QAAQ,IAAI,UAAU,CAAC;EACvD,QAAQ,eAAe,UAAU;EAClC;AAED,IAAG,cAAc,KAAK,KAAK,UAAU,oBAAoB,EAAE,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;AAEzF,QAAO;EACL,WAAW,UAAU,UAAU;EAC/B,SAAS,QAAQ,UAAU;EAC3B,UAAU,eAAe,UAAU;EACnC;EACA;EACA;EACD"}
@@ -0,0 +1,190 @@
1
+ const require_chunk = require("./chunk-CZWwpsFl.cjs");
2
+ const require_proposed_participant = require("./proposed-participant-Dm1Eq6mX.cjs");
3
+ const require_registry_index = require("./registry/index.cjs");
4
+ const require_common = require("./common-lKP5EzHy.cjs");
5
+ const require_busy = require("./busy-EZU7EKr6.cjs");
6
+ const require_common$1 = require("./common-lThIvJmZ.cjs");
7
+ let _bcts_dcbor = require("@bcts/dcbor");
8
+ let node_fs = require("node:fs");
9
+ node_fs = require_chunk.__toESM(node_fs, 1);
10
+ let node_path = require("node:path");
11
+ node_path = require_chunk.__toESM(node_path, 1);
12
+ //#region src/cmd/sign/participant/receive.ts
13
+ /**
14
+ * Copyright © 2023-2026 Blockchain Commons, LLC
15
+ * Copyright © 2025-2026 Parity Technologies
16
+ *
17
+ *
18
+ * Sign participant receive command.
19
+ *
20
+ * Port of cmd/sign/participant/receive.rs from frost-hubert-rust.
21
+ *
22
+ * @module
23
+ */
24
+ var receive_exports = /* @__PURE__ */ require_chunk.__exportAll({ receive: () => receive });
25
+ /**
26
+ * Resolve sender from XID UR or pet name in registry.
27
+ *
28
+ * Port of `resolve_sender()` from cmd/dkg/common.rs lines 76-94.
29
+ */
30
+ function resolveSenderFromInput(registry, input) {
31
+ const trimmed = input.trim();
32
+ if (trimmed === "") throw new Error("Sender is required");
33
+ try {
34
+ const { XID: XIDClass } = require("@bcts/components");
35
+ const xid = XIDClass.fromURString(trimmed);
36
+ const record = registry.participant(xid);
37
+ if (!record) throw new Error(`Sender with XID ${xid.urString()} not found`);
38
+ return record.xidDocument();
39
+ } catch {
40
+ const result = registry.participantByPetName(trimmed);
41
+ if (!result) throw new Error(`Sender with pet name '${trimmed}' not found`);
42
+ return result[1].xidDocument();
43
+ }
44
+ }
45
+ /**
46
+ * Resolve sign invite from ARID or envelope UR.
47
+ *
48
+ * Port of `resolve_sign_request()` from cmd/sign/participant/receive.rs lines 250-284.
49
+ */
50
+ async function resolveSignInviteEnvelope(client, selection, request, timeout) {
51
+ if (selection !== void 0 && client !== void 0) {
52
+ try {
53
+ const envelope = await require_busy.getWithIndicator(client, require_common.parseAridUr(request), "Sign invite", timeout, false);
54
+ if (envelope === void 0 || envelope === null) throw new Error("signInvite request not found in Hubert storage");
55
+ return envelope;
56
+ } catch {}
57
+ if (timeout !== void 0) throw new Error("--timeout is only valid when retrieving requests from Hubert");
58
+ return require_common.parseEnvelopeUr(request);
59
+ }
60
+ try {
61
+ require_common.parseAridUr(request);
62
+ throw new Error("Hubert storage parameters are required to retrieve requests by ARID");
63
+ } catch (e) {
64
+ if (e instanceof Error && e.message.includes("Hubert storage parameters")) throw e;
65
+ }
66
+ return require_common.parseEnvelopeUr(request);
67
+ }
68
+ /**
69
+ * Get display name for sender from registry.
70
+ *
71
+ * Port of `resolve_sender_name()` from cmd/dkg/common.rs lines 96-116.
72
+ */
73
+ function resolveSenderName(registry, senderXid) {
74
+ const owner = registry.owner();
75
+ if (owner?.xid().urString() === senderXid.urString()) return require_common.formatNameWithOwnerMarker(owner.petName() ?? senderXid.urString(), true);
76
+ const record = registry.participant(senderXid);
77
+ if (record) return require_common.formatNameWithOwnerMarker(record.petName() ?? record.xid().urString(), false);
78
+ }
79
+ /**
80
+ * Format participant names with owner marker.
81
+ *
82
+ * Port of `format_participant_names()` from cmd/sign/participant/receive.rs lines 286-309.
83
+ */
84
+ function formatParticipantNames(registry, participants, owner) {
85
+ return participants.map((xid) => {
86
+ const isOwner = xid.urString() === owner.xid().urString();
87
+ let name;
88
+ if (isOwner) name = owner.petName() ?? xid.urString();
89
+ else name = registry.participant(xid)?.petName() ?? xid.urString();
90
+ return require_common.formatNameWithOwnerMarker(name, isOwner);
91
+ });
92
+ }
93
+ /**
94
+ * Execute the sign participant receive command.
95
+ *
96
+ * Fetches and validates a sign invite from the coordinator.
97
+ *
98
+ * Port of `CommandArgs::exec()` from cmd/sign/participant/receive.rs lines 56-247.
99
+ */
100
+ async function receive(client, selection, options, cwd) {
101
+ if (selection === void 0 && options.timeoutSeconds !== void 0) throw new Error("--timeout requires Hubert storage parameters");
102
+ const registryPath = require_registry_index.resolveRegistryPath(options.registryPath, cwd);
103
+ const registry = require_registry_index.Registry.load(registryPath);
104
+ const owner = registry.owner();
105
+ if (!owner) throw new Error("Registry owner with private keys is required");
106
+ let expectedSender;
107
+ if (options.sender !== void 0 && options.sender !== "") expectedSender = resolveSenderFromInput(registry, options.sender);
108
+ const envelope = await resolveSignInviteEnvelope(client, selection, options.request, options.timeoutSeconds);
109
+ const now = _bcts_dcbor.CborDate.now();
110
+ const recipientKeys = owner.xidDocument().inceptionPrivateKeys();
111
+ if (recipientKeys === null || recipientKeys === void 0) throw new Error("Owner XID document has no inception private keys");
112
+ const { SealedRequest: SealedRequestClass } = require("@bcts/gstp");
113
+ const sealedRequest = SealedRequestClass.tryFromEnvelope(envelope, void 0, now, recipientKeys);
114
+ const senderXid = sealedRequest.sender().xid();
115
+ if (expectedSender !== void 0) {
116
+ if (senderXid.urString() !== expectedSender.xid().urString()) throw new Error(`Request sender does not match expected sender (got ${senderXid.urString()}, expected ${expectedSender.xid().urString()})`);
117
+ } else {
118
+ const knownOwner = owner.xid().urString() === senderXid.urString();
119
+ const knownParticipant = registry.participant(senderXid) !== void 0;
120
+ if (!knownOwner && !knownParticipant) throw new Error(`Request sender not found in registry: ${senderXid.urString()}`);
121
+ }
122
+ const { Function: FunctionClass } = require("@bcts/envelope");
123
+ const requestFunction = sealedRequest.function();
124
+ const expectedFunction = FunctionClass.from("signInvite");
125
+ if (!(requestFunction.equals !== void 0 ? requestFunction.equals(expectedFunction) : String(requestFunction) === String(expectedFunction))) throw new Error(`Unexpected request function: ${String(requestFunction)}`);
126
+ if (sealedRequest.extractObjectForParameter("validUntil") <= now) throw new Error("signInvite request has expired");
127
+ const groupId = sealedRequest.extractObjectForParameter("group");
128
+ const sessionId = sealedRequest.extractObjectForParameter("session");
129
+ const minSigners = Number(sealedRequest.extractObjectForParameter("minSigners"));
130
+ const participantEntries = sealedRequest.objectsForParameter("participant");
131
+ const participants = [];
132
+ let responseArid;
133
+ for (const entry of participantEntries) {
134
+ const xid = entry.extractSubject();
135
+ if (xid.urString() === owner.xid().urString()) responseArid = entry.objectForPredicate("response_arid").decryptToRecipient(recipientKeys).extractSubject();
136
+ participants.push(xid);
137
+ }
138
+ if (participants.length === 0) throw new Error("signInvite request contains no participants");
139
+ if (minSigners < 2) throw new Error("minSigners must be at least 2");
140
+ if (minSigners > participants.length) throw new Error("minSigners exceeds participant count");
141
+ if (!participants.some((p) => p.urString() === owner.xid().urString())) throw new Error("signInvite request does not include this participant");
142
+ if (responseArid === void 0) throw new Error("signInvite request missing response ARID");
143
+ participants.sort((a, b) => require_proposed_participant.compareXidBytes(a.toData(), b.toData()));
144
+ const targetEnvelope = sealedRequest.objectForParameter("target");
145
+ const coordinatorName = resolveSenderName(registry, senderXid) ?? senderXid.urString();
146
+ const participantNames = formatParticipantNames(registry, participants, owner);
147
+ console.log(`Group: ${groupId.urString()}`);
148
+ console.log(`Coordinator: ${coordinatorName}`);
149
+ console.log(`Min signers: ${minSigners}`);
150
+ console.log(`Participants: ${participantNames.join(", ")}`);
151
+ console.log("Target:");
152
+ console.log(targetEnvelope.format());
153
+ console.log(sessionId.urString());
154
+ const stateDir = require_common$1.signingStateDir(registryPath, groupId.hex(), sessionId.hex());
155
+ node_fs.mkdirSync(stateDir, { recursive: true });
156
+ const root = {
157
+ request_envelope: envelope.urString(),
158
+ group: groupId.urString(),
159
+ session: sessionId.urString(),
160
+ coordinator: senderXid.urString(),
161
+ min_signers: minSigners,
162
+ response_arid: responseArid.urString(),
163
+ participants: participants.map((xid) => xid.urString()),
164
+ target: targetEnvelope.urString()
165
+ };
166
+ node_fs.writeFileSync(node_path.join(stateDir, "sign_receive.json"), JSON.stringify(root, null, 2));
167
+ return {
168
+ sessionId: sessionId.urString(),
169
+ groupId: groupId.urString(),
170
+ targetUr: targetEnvelope.urString(),
171
+ coordinatorName,
172
+ minSigners,
173
+ participantNames
174
+ };
175
+ }
176
+ //#endregion
177
+ Object.defineProperty(exports, "receive", {
178
+ enumerable: true,
179
+ get: function() {
180
+ return receive;
181
+ }
182
+ });
183
+ Object.defineProperty(exports, "receive_exports", {
184
+ enumerable: true,
185
+ get: function() {
186
+ return receive_exports;
187
+ }
188
+ });
189
+
190
+ //# sourceMappingURL=receive-kZMsXhbK.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receive-kZMsXhbK.cjs","names":["getWithIndicator","parseAridUr","parseEnvelopeUr","formatNameWithOwnerMarker","resolveRegistryPath","Registry","CborDate","compareXidBytes","signingStateDir","path"],"sources":["../src/cmd/sign/participant/receive.ts"],"sourcesContent":["/**\n * Copyright © 2023-2026 Blockchain Commons, LLC\n * Copyright © 2025-2026 Parity Technologies\n *\n *\n * Sign participant receive command.\n *\n * Port of cmd/sign/participant/receive.rs from frost-hubert-rust.\n *\n * @module\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { type ARID, type XID } from \"@bcts/components\";\nimport { compareXidBytes } from \"../../../dkg/proposed-participant.js\";\nimport { CborDate } from \"@bcts/dcbor\";\nimport type { Envelope } from \"@bcts/envelope\";\n\nimport { Registry, resolveRegistryPath, type OwnerRecord } from \"../../../registry/index.js\";\nimport { getWithIndicator } from \"../../busy.js\";\nimport { type StorageClient, type StorageSelection } from \"../../storage.js\";\nimport { parseAridUr, parseEnvelopeUr, formatNameWithOwnerMarker } from \"../../dkg/common.js\";\nimport { signingStateDir } from \"../common.js\";\n\n/**\n * Options for the sign receive command.\n */\nexport interface SignReceiveOptions {\n registryPath?: string;\n /** ARID or envelope UR string */\n request: string;\n timeoutSeconds?: number;\n /** Show request details only (info mode) */\n info?: boolean;\n /** Expected sender (XID UR or pet name) */\n sender?: string;\n}\n\n/**\n * Result of the sign receive command.\n */\nexport interface SignReceiveResult {\n sessionId: string;\n groupId: string;\n targetUr: string;\n coordinatorName: string;\n minSigners: number;\n participantNames: string[];\n}\n\n/**\n * Resolve sender from XID UR or pet name in registry.\n *\n * Port of `resolve_sender()` from cmd/dkg/common.rs lines 76-94.\n */\nfunction resolveSenderFromInput(registry: Registry, input: string): { xid: () => XID } {\n const trimmed = input.trim();\n if (trimmed === \"\") {\n throw new Error(\"Sender is required\");\n }\n\n // Try parsing as XID UR first\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-require-imports, no-undef\n const { XID: XIDClass } = require(\"@bcts/components\");\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n const xid = XIDClass.fromURString(trimmed) as XID;\n const record = registry.participant(xid);\n if (!record) {\n throw new Error(`Sender with XID ${xid.urString()} not found`);\n }\n return record.xidDocument();\n } catch {\n // Try looking up by pet name\n const result = registry.participantByPetName(trimmed);\n if (!result) {\n throw new Error(`Sender with pet name '${trimmed}' not found`);\n }\n return result[1].xidDocument();\n }\n}\n\n/**\n * Resolve sign invite from ARID or envelope UR.\n *\n * Port of `resolve_sign_request()` from cmd/sign/participant/receive.rs lines 250-284.\n */\nasync function resolveSignInviteEnvelope(\n client: StorageClient | undefined,\n selection: StorageSelection | undefined,\n request: string,\n timeout: number | undefined,\n): Promise<Envelope> {\n if (selection !== undefined && client !== undefined) {\n // Try to parse as ARID\n try {\n const arid = parseAridUr(request);\n const envelope = await getWithIndicator(client, arid, \"Sign invite\", timeout, false);\n if (envelope === undefined || envelope === null) {\n throw new Error(\"signInvite request not found in Hubert storage\");\n }\n return envelope;\n } catch {\n // Not an ARID, try as envelope\n }\n\n if (timeout !== undefined) {\n throw new Error(\"--timeout is only valid when retrieving requests from Hubert\");\n }\n return parseEnvelopeUr(request);\n }\n\n // No storage selection\n try {\n parseAridUr(request);\n throw new Error(\"Hubert storage parameters are required to retrieve requests by ARID\");\n } catch (e) {\n if (e instanceof Error && e.message.includes(\"Hubert storage parameters\")) {\n throw e;\n }\n // Not an ARID, parse as envelope\n }\n return parseEnvelopeUr(request);\n}\n\n/**\n * Get display name for sender from registry.\n *\n * Port of `resolve_sender_name()` from cmd/dkg/common.rs lines 96-116.\n */\nfunction resolveSenderName(registry: Registry, senderXid: XID): string | undefined {\n const owner = registry.owner();\n\n // Check if sender is the owner\n if (owner?.xid().urString() === senderXid.urString()) {\n const name = owner.petName() ?? senderXid.urString();\n return formatNameWithOwnerMarker(name, true);\n }\n\n // Look up in participants\n const record = registry.participant(senderXid);\n if (record) {\n const name = record.petName() ?? record.xid().urString();\n return formatNameWithOwnerMarker(name, false);\n }\n\n return undefined;\n}\n\n/**\n * Format participant names with owner marker.\n *\n * Port of `format_participant_names()` from cmd/sign/participant/receive.rs lines 286-309.\n */\nfunction formatParticipantNames(\n registry: Registry,\n participants: XID[],\n owner: OwnerRecord,\n): string[] {\n return participants.map((xid) => {\n const isOwner = xid.urString() === owner.xid().urString();\n let name: string;\n\n if (isOwner) {\n name = owner.petName() ?? xid.urString();\n } else {\n const record = registry.participant(xid);\n name = record?.petName() ?? xid.urString();\n }\n\n return formatNameWithOwnerMarker(name, isOwner);\n });\n}\n\n/**\n * Execute the sign participant receive command.\n *\n * Fetches and validates a sign invite from the coordinator.\n *\n * Port of `CommandArgs::exec()` from cmd/sign/participant/receive.rs lines 56-247.\n */\nexport async function receive(\n client: StorageClient | undefined,\n selection: StorageSelection | undefined,\n options: SignReceiveOptions,\n cwd: string,\n): Promise<SignReceiveResult> {\n // Validate timeout requires storage\n if (selection === undefined && options.timeoutSeconds !== undefined) {\n throw new Error(\"--timeout requires Hubert storage parameters\");\n }\n\n const registryPath = resolveRegistryPath(options.registryPath, cwd);\n const registry = Registry.load(registryPath);\n const owner = registry.owner();\n\n if (!owner) {\n throw new Error(\"Registry owner with private keys is required\");\n }\n\n // Resolve expected sender if provided\n let expectedSender: { xid: () => XID } | undefined;\n if (options.sender !== undefined && options.sender !== \"\") {\n expectedSender = resolveSenderFromInput(registry, options.sender);\n }\n\n // Resolve the invite envelope\n const envelope = await resolveSignInviteEnvelope(\n client,\n selection,\n options.request,\n options.timeoutSeconds,\n );\n\n const now: CborDate = CborDate.now();\n const recipientKeys = owner.xidDocument().inceptionPrivateKeys();\n\n if (recipientKeys === null || recipientKeys === undefined) {\n throw new Error(\"Owner XID document has no inception private keys\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef\n const { SealedRequest: SealedRequestClass } = require(\"@bcts/gstp\") as {\n SealedRequest: {\n tryFromEnvelope: (\n envelope: Envelope,\n expectedSender: XID | undefined,\n now: CborDate,\n recipientPrivateKeys: unknown,\n ) => SealedRequestInstance;\n };\n };\n\n interface SealedRequestInstance {\n sender: () => { xid: () => XID };\n function: () => { equals?: (other: unknown) => boolean; toString?: () => string };\n extractObjectForParameter: <T>(name: string) => T;\n objectForParameter: (name: string) => Envelope;\n objectsForParameter: (name: string) => ParticipantEntry[];\n }\n\n interface ParticipantEntry {\n extractSubject: () => XID;\n objectForPredicate: (name: string) => {\n decryptToRecipient: (keys: unknown) => {\n extractSubject: () => ARID;\n };\n };\n }\n\n const sealedRequest: SealedRequestInstance = SealedRequestClass.tryFromEnvelope(\n envelope,\n undefined,\n now,\n recipientKeys,\n );\n\n // Validate sender\n const senderXid = sealedRequest.sender().xid();\n\n if (expectedSender !== undefined) {\n if (senderXid.urString() !== expectedSender.xid().urString()) {\n throw new Error(\n `Request sender does not match expected sender (got ${senderXid.urString()}, expected ${expectedSender.xid().urString()})`,\n );\n }\n } else {\n const knownOwner = owner.xid().urString() === senderXid.urString();\n const knownParticipant = registry.participant(senderXid) !== undefined;\n if (!knownOwner && !knownParticipant) {\n throw new Error(`Request sender not found in registry: ${senderXid.urString()}`);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports, no-undef\n const { Function: FunctionClass } = require(\"@bcts/envelope\") as {\n Function: { from: (name: string) => unknown };\n };\n\n // Validate function\n const requestFunction = sealedRequest.function();\n const expectedFunction = FunctionClass.from(\"signInvite\");\n const functionMatches =\n requestFunction.equals !== undefined\n ? requestFunction.equals(expectedFunction)\n : String(requestFunction) === String(expectedFunction);\n\n if (!functionMatches) {\n throw new Error(`Unexpected request function: ${String(requestFunction)}`);\n }\n\n // Extract parameters\n const validUntil = sealedRequest.extractObjectForParameter<CborDate>(\"validUntil\");\n if (validUntil <= now) {\n throw new Error(\"signInvite request has expired\");\n }\n\n const groupId = sealedRequest.extractObjectForParameter<ARID>(\"group\");\n const sessionId = sealedRequest.extractObjectForParameter<ARID>(\"session\");\n const minSigners = Number(sealedRequest.extractObjectForParameter<bigint | number>(\"minSigners\"));\n\n // Extract participants and find our response ARID\n const participantEntries = sealedRequest.objectsForParameter(\"participant\");\n const participants: XID[] = [];\n let responseArid: ARID | undefined;\n\n for (const entry of participantEntries) {\n const xid: XID = entry.extractSubject();\n if (xid.urString() === owner.xid().urString()) {\n const encryptedArid = entry.objectForPredicate(\"response_arid\");\n const aridEnv = encryptedArid.decryptToRecipient(recipientKeys);\n responseArid = aridEnv.extractSubject();\n }\n participants.push(xid);\n }\n\n // Validations\n if (participants.length === 0) {\n throw new Error(\"signInvite request contains no participants\");\n }\n if (minSigners < 2) {\n throw new Error(\"minSigners must be at least 2\");\n }\n if (minSigners > participants.length) {\n throw new Error(\"minSigners exceeds participant count\");\n }\n\n const ownerInParticipants = participants.some((p) => p.urString() === owner.xid().urString());\n if (!ownerInParticipants) {\n throw new Error(\"signInvite request does not include this participant\");\n }\n\n if (responseArid === undefined) {\n throw new Error(\"signInvite request missing response ARID\");\n }\n\n // Sort participants by XID byte order — mirrors Rust `XID::cmp`.\n // The earlier port used `urString().localeCompare(...)`, which\n // diverges from byte order for bytes ≥ 0x80 and is locale-aware.\n participants.sort((a, b) => compareXidBytes(a.toData(), b.toData()));\n\n const targetEnvelope = sealedRequest.objectForParameter(\"target\");\n\n const coordinatorName = resolveSenderName(registry, senderXid) ?? senderXid.urString();\n const participantNames = formatParticipantNames(registry, participants, owner);\n\n // Output\n console.log(`Group: ${groupId.urString()}`);\n console.log(`Coordinator: ${coordinatorName}`);\n console.log(`Min signers: ${minSigners}`);\n console.log(`Participants: ${participantNames.join(\", \")}`);\n console.log(\"Target:\");\n console.log(targetEnvelope.format());\n\n // Primary output for scripting: session ID on its own line (no header)\n console.log(sessionId.urString());\n\n // Persist request details for follow-up commands\n const stateDir = signingStateDir(registryPath, groupId.hex(), sessionId.hex());\n fs.mkdirSync(stateDir, { recursive: true });\n\n const root: Record<string, unknown> = {\n request_envelope: envelope.urString(),\n group: groupId.urString(),\n session: sessionId.urString(),\n coordinator: senderXid.urString(),\n min_signers: minSigners,\n response_arid: responseArid.urString(),\n participants: participants.map((xid) => xid.urString()),\n target: targetEnvelope.urString(),\n };\n\n fs.writeFileSync(path.join(stateDir, \"sign_receive.json\"), JSON.stringify(root, null, 2));\n\n return {\n sessionId: sessionId.urString(),\n groupId: groupId.urString(),\n targetUr: targetEnvelope.urString(),\n coordinatorName,\n minSigners,\n participantNames,\n };\n}\n"],"mappings":"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"}
@@ -375,8 +375,16 @@ var OwnerRecord = class OwnerRecord {
375
375
  }
376
376
  /**
377
377
  * Deserialize from JSON object.
378
+ *
379
+ * Mirrors Rust's `#[serde(deny_unknown_fields)]` derive on
380
+ * `OwnerRecord` (`owner_record.rs:13-17`) — any field not in
381
+ * `{xid_document, pet_name}` causes Rust's `serde_json::from_str`
382
+ * to error with `unknown field`, and we mirror that here so a
383
+ * registry file produced by a future Rust version with extra
384
+ * fields is rejected explicitly rather than silently lossy.
378
385
  */
379
386
  static fromJSON(json) {
387
+ for (const key of Object.keys(json)) if (key !== "xid_document" && key !== "pet_name") throw new Error(`unknown field \`${key}\`, expected \`xid_document\` or \`pet_name\``);
380
388
  const xidDocumentUr = json["xid_document"];
381
389
  const petName = json["pet_name"];
382
390
  return OwnerRecord.fromSignedXidUr(xidDocumentUr, petName);
@@ -506,8 +514,17 @@ var ParticipantRecord = class ParticipantRecord {
506
514
  }
507
515
  /**
508
516
  * Deserialize from JSON object.
517
+ *
518
+ * Mirrors Rust's `#[serde(deny_unknown_fields)]` derive on
519
+ * `ParticipantRecord` (`participant_record.rs:12-17`) — Rust's
520
+ * `serde_json::from_str` errors with `unknown field` for any
521
+ * field outside `{xid_document, pet_name}`. We mirror that
522
+ * behaviour so a registry file produced by a future Rust
523
+ * version with extra fields is rejected explicitly rather than
524
+ * silently lossy.
509
525
  */
510
526
  static fromJSON(json) {
527
+ for (const key of Object.keys(json)) if (key !== "xid_document" && key !== "pet_name") throw new Error(`unknown field \`${key}\`, expected \`xid_document\` or \`pet_name\``);
511
528
  const xidDocumentUr = json["xid_document"];
512
529
  const petName = json["pet_name"];
513
530
  return ParticipantRecord.recreateFromSerialized(xidDocumentUr, petName);
@@ -527,9 +544,19 @@ function parseSignedXidDocument(xidDocumentUr) {
527
544
  try {
528
545
  envelope = _bcts_envelope.Envelope.fromTaggedCbor(envelopeCbor);
529
546
  } catch {
530
- envelope = _bcts_envelope.Envelope.fromUntaggedCbor(envelopeCbor);
547
+ try {
548
+ envelope = _bcts_envelope.Envelope.fromUntaggedCbor(envelopeCbor);
549
+ } catch (e) {
550
+ throw new Error(`Unable to decode XID document envelope: ${e.message ?? String(e)}`, { cause: e });
551
+ }
552
+ }
553
+ let document;
554
+ try {
555
+ document = _bcts_xid.XIDDocument.fromEnvelope(envelope, void 0, _bcts_xid.XIDVerifySignature.Inception);
556
+ } catch (e) {
557
+ throw new Error(`XID document must be signed by its inception key: ${e.message ?? String(e)}`, { cause: e });
531
558
  }
532
- return [sanitized, _bcts_xid.XIDDocument.fromEnvelope(envelope, void 0, _bcts_xid.XIDVerifySignature.Inception)];
559
+ return [sanitized, document];
533
560
  }
534
561
  /**
535
562
  * Sanitize XID UR input.
@@ -652,13 +679,43 @@ var Registry = class Registry {
652
679
  /**
653
680
  * Add a participant.
654
681
  *
655
- * Returns the outcome indicating whether the participant was already present or newly inserted.
682
+ * Mirrors Rust `Registry::add_participant`
683
+ * (`registry_impl.rs:83-124`):
684
+ *
685
+ * 1. If `record.pet_name()` is already used by some other XID,
686
+ * bail with `"Pet name '{name}' already used by another
687
+ * participant"`.
688
+ * 2. If `record.pet_name()` matches an existing record under the
689
+ * *same* XID and the public keys also match, return
690
+ * `AlreadyPresent`.
691
+ * 3. If the pet name matches the same XID but public keys don't,
692
+ * bail with `"Participant already exists with a different pet
693
+ * name"`.
694
+ * 4. Otherwise look up by XID. If present and public-keys + pet-name
695
+ * both match, return `AlreadyPresent`; if XID is present but
696
+ * anything differs, bail. If XID is new, insert and return
697
+ * `Inserted`.
698
+ *
699
+ * The earlier port short-circuited on `participants.has(xidUr)` and
700
+ * always returned `AlreadyPresent` — silently allowing re-adds with
701
+ * a different pet name or different public keys, which Rust
702
+ * correctly forbids.
656
703
  */
657
704
  addParticipant(xid, record) {
658
705
  const xidUr = xid.urString();
659
- if (this._participants.has(xidUr)) return "already_present";
660
706
  const petName = record.petName();
661
- if (petName !== void 0 && this.petNameExists(petName)) throw new Error(`Pet name "${petName}" is already used by another participant`);
707
+ if (petName !== void 0) {
708
+ for (const [existingXidUr, existingRecord] of this._participants) if (existingRecord.petName() === petName) {
709
+ if (existingXidUr !== xidUr) throw new Error(`Pet name '${petName}' already used by another participant`);
710
+ if (publicKeysEqual(existingRecord.publicKeys(), record.publicKeys())) return "already_present";
711
+ throw new Error("Participant already exists with a different pet name");
712
+ }
713
+ }
714
+ const existing = this._participants.get(xidUr);
715
+ if (existing !== void 0) {
716
+ if (publicKeysEqual(existing.publicKeys(), record.publicKeys()) && existing.petName() === record.petName()) return "already_present";
717
+ throw new Error("Participant already exists with a different pet name");
718
+ }
662
719
  this._participants.set(xidUr, record);
663
720
  return "inserted";
664
721
  }
@@ -748,17 +805,25 @@ var Registry = class Registry {
748
805
  }
749
806
  /**
750
807
  * Serialize to JSON object.
808
+ *
809
+ * Mirrors Rust `Registry`'s field declaration order
810
+ * (`registry_impl.rs:8-14` — `owner, participants, groups`). JSON
811
+ * object member order is not semantically significant, but
812
+ * `serde_json::to_string_pretty` emits keys in declaration order,
813
+ * so for byte-equal `registry.json` (used by the integration tests
814
+ * as a string assertion) the TS port must match Rust's order.
815
+ * Empty `owner` is omitted via `Option::None` skip in Rust; we
816
+ * reproduce that by only setting the key when the owner exists.
751
817
  */
752
818
  toJSON() {
819
+ const obj = {};
820
+ if (this._owner !== void 0) obj["owner"] = this._owner.toJSON();
753
821
  const participants = {};
754
822
  for (const [xidUr, record] of this._participants) participants[xidUr] = record.toJSON();
823
+ obj["participants"] = participants;
755
824
  const groups = {};
756
825
  for (const [aridHex, record] of this._groups) groups[aridHex] = record.toJSON();
757
- const obj = {
758
- groups,
759
- participants
760
- };
761
- if (this._owner !== void 0) obj["owner"] = this._owner.toJSON();
826
+ obj["groups"] = groups;
762
827
  return obj;
763
828
  }
764
829
  /**
@@ -791,6 +856,16 @@ function resolveRegistryPath(registryArg, cwd) {
791
856
  if (!registryArg.includes("/") && !registryArg.includes(node_path.sep)) return node_path.join(cwd, registryArg);
792
857
  return node_path.resolve(cwd, registryArg);
793
858
  }
859
+ /**
860
+ * Structural equality on `PublicKeys`, mirroring Rust's
861
+ * `PartialEq::eq` derive (per-component comparison of the signing
862
+ * and encapsulation public keys).
863
+ *
864
+ * @internal
865
+ */
866
+ function publicKeysEqual(a, b) {
867
+ return a.equals(b);
868
+ }
794
869
  //#endregion
795
870
  exports.AddOutcome = AddOutcome;
796
871
  exports.ContributionPaths = ContributionPaths;