agent-comms 8.4.1 → 8.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 (294) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +33 -3
  4. package/dist/bridges/cc-peer/front-runtime.d.ts.map +1 -1
  5. package/dist/bridges/cc-peer/front-runtime.js +4 -2
  6. package/dist/bridges/cc-peer/front-runtime.js.map +1 -1
  7. package/dist/bridges/user/controller.d.ts.map +1 -1
  8. package/dist/bridges/user/controller.js +4 -1
  9. package/dist/bridges/user/controller.js.map +1 -1
  10. package/dist/bridges/user/web/contract.d.ts +12 -0
  11. package/dist/bridges/user/web/contract.d.ts.map +1 -1
  12. package/dist/bridges/user/web/contract.js +9 -0
  13. package/dist/bridges/user/web/contract.js.map +1 -1
  14. package/dist/bridges/user/web/dist/bundle.js +1 -1
  15. package/dist/bridges/user/web/dist/mesh-worker.js +1 -1
  16. package/dist/bridges/user/web/dist/sw.js +1 -1
  17. package/dist/bridges/user/web/frontend/components/MeshGraphView.d.ts +3 -1
  18. package/dist/bridges/user/web/frontend/components/MeshGraphView.d.ts.map +1 -1
  19. package/dist/bridges/user/web/frontend/components/MeshGraphView.js +2 -4
  20. package/dist/bridges/user/web/frontend/components/MeshGraphView.js.map +1 -1
  21. package/dist/bridges/user/web/frontend/components/MeshPanel.d.ts +1 -1
  22. package/dist/bridges/user/web/frontend/components/MeshPanel.d.ts.map +1 -1
  23. package/dist/bridges/user/web/frontend/components/MeshPanel.js +6 -1
  24. package/dist/bridges/user/web/frontend/components/MeshPanel.js.map +1 -1
  25. package/dist/bridges/user/web/frontend/components/MeshTraceView.d.ts +3 -1
  26. package/dist/bridges/user/web/frontend/components/MeshTraceView.d.ts.map +1 -1
  27. package/dist/bridges/user/web/frontend/components/MeshTraceView.js +2 -4
  28. package/dist/bridges/user/web/frontend/components/MeshTraceView.js.map +1 -1
  29. package/dist/bridges/user/web/frontend/main.js +6 -13
  30. package/dist/bridges/user/web/frontend/main.js.map +1 -1
  31. package/dist/bridges/user/web/frontend/mesh-client.d.ts +2 -0
  32. package/dist/bridges/user/web/frontend/mesh-client.d.ts.map +1 -1
  33. package/dist/bridges/user/web/frontend/mesh-client.js +1 -0
  34. package/dist/bridges/user/web/frontend/mesh-client.js.map +1 -1
  35. package/dist/bridges/user/web/frontend/mesh-invalidation.d.ts +11 -0
  36. package/dist/bridges/user/web/frontend/mesh-invalidation.d.ts.map +1 -0
  37. package/dist/bridges/user/web/frontend/mesh-invalidation.js +26 -0
  38. package/dist/bridges/user/web/frontend/mesh-invalidation.js.map +1 -0
  39. package/dist/bridges/user/web/frontend/mesh-worker.d.ts.map +1 -1
  40. package/dist/bridges/user/web/frontend/mesh-worker.js +1 -0
  41. package/dist/bridges/user/web/frontend/mesh-worker.js.map +1 -1
  42. package/dist/bridges/user/web/frontend/tab-contract.d.ts +5 -0
  43. package/dist/bridges/user/web/frontend/tab-contract.d.ts.map +1 -1
  44. package/dist/bridges/user/web/reads.d.ts +3 -0
  45. package/dist/bridges/user/web/reads.d.ts.map +1 -1
  46. package/dist/bridges/user/web/reads.js +4 -0
  47. package/dist/bridges/user/web/reads.js.map +1 -1
  48. package/dist/bridges/user/web/router.d.ts +5 -0
  49. package/dist/bridges/user/web/router.d.ts.map +1 -1
  50. package/dist/bridges/user/web/router.js +2 -1
  51. package/dist/bridges/user/web/router.js.map +1 -1
  52. package/dist/core/agent-registry.d.ts.map +1 -1
  53. package/dist/core/agent-registry.js +20 -6
  54. package/dist/core/agent-registry.js.map +1 -1
  55. package/dist/core/agent-table.d.ts +18 -0
  56. package/dist/core/agent-table.d.ts.map +1 -0
  57. package/dist/core/agent-table.js +38 -0
  58. package/dist/core/agent-table.js.map +1 -0
  59. package/dist/core/atomic-file.d.ts +12 -0
  60. package/dist/core/atomic-file.d.ts.map +1 -0
  61. package/dist/core/atomic-file.js +37 -0
  62. package/dist/core/atomic-file.js.map +1 -0
  63. package/dist/core/bridge-mesh.d.ts.map +1 -1
  64. package/dist/core/bridge-mesh.js +7 -0
  65. package/dist/core/bridge-mesh.js.map +1 -1
  66. package/dist/core/bridge.d.ts +5 -0
  67. package/dist/core/bridge.d.ts.map +1 -1
  68. package/dist/core/bridge.js +26 -1
  69. package/dist/core/bridge.js.map +1 -1
  70. package/dist/core/delivery-engine.d.ts.map +1 -1
  71. package/dist/core/delivery-engine.js +4 -0
  72. package/dist/core/delivery-engine.js.map +1 -1
  73. package/dist/core/device-membership-verification.d.ts +23 -9
  74. package/dist/core/device-membership-verification.d.ts.map +1 -1
  75. package/dist/core/device-membership-verification.js +38 -11
  76. package/dist/core/device-membership-verification.js.map +1 -1
  77. package/dist/core/device-membership.d.ts +12 -3
  78. package/dist/core/device-membership.d.ts.map +1 -1
  79. package/dist/core/device-membership.js +10 -7
  80. package/dist/core/device-membership.js.map +1 -1
  81. package/dist/core/directory-admission.d.ts +5 -3
  82. package/dist/core/directory-admission.d.ts.map +1 -1
  83. package/dist/core/directory-admission.js +55 -32
  84. package/dist/core/directory-admission.js.map +1 -1
  85. package/dist/core/display-name.d.ts +29 -0
  86. package/dist/core/display-name.d.ts.map +1 -0
  87. package/dist/core/display-name.js +57 -0
  88. package/dist/core/display-name.js.map +1 -0
  89. package/dist/core/gateway-trust-actions.d.ts +7 -9
  90. package/dist/core/gateway-trust-actions.d.ts.map +1 -1
  91. package/dist/core/gateway-trust-actions.js +82 -16
  92. package/dist/core/gateway-trust-actions.js.map +1 -1
  93. package/dist/core/gateway-trust.d.ts +38 -17
  94. package/dist/core/gateway-trust.d.ts.map +1 -1
  95. package/dist/core/gateway-trust.js +86 -37
  96. package/dist/core/gateway-trust.js.map +1 -1
  97. package/dist/core/gossip-directory.d.ts +25 -3
  98. package/dist/core/gossip-directory.d.ts.map +1 -1
  99. package/dist/core/gossip-directory.js +53 -5
  100. package/dist/core/gossip-directory.js.map +1 -1
  101. package/dist/core/gossip-extensions.d.ts +4 -0
  102. package/dist/core/gossip-extensions.d.ts.map +1 -1
  103. package/dist/core/gossip-extensions.js +4 -0
  104. package/dist/core/gossip-extensions.js.map +1 -1
  105. package/dist/core/group-proofs.d.ts +49 -0
  106. package/dist/core/group-proofs.d.ts.map +1 -0
  107. package/dist/core/group-proofs.js +102 -0
  108. package/dist/core/group-proofs.js.map +1 -0
  109. package/dist/core/identity-store.d.ts +5 -4
  110. package/dist/core/identity-store.d.ts.map +1 -1
  111. package/dist/core/identity-store.js +13 -16
  112. package/dist/core/identity-store.js.map +1 -1
  113. package/dist/core/issuer-identity-file.d.ts +25 -0
  114. package/dist/core/issuer-identity-file.d.ts.map +1 -0
  115. package/dist/core/issuer-identity-file.js +124 -0
  116. package/dist/core/issuer-identity-file.js.map +1 -0
  117. package/dist/core/machine-grouping.d.ts +47 -0
  118. package/dist/core/machine-grouping.d.ts.map +1 -0
  119. package/dist/core/machine-grouping.js +102 -0
  120. package/dist/core/machine-grouping.js.map +1 -0
  121. package/dist/core/machine-identity.d.ts +19 -0
  122. package/dist/core/machine-identity.d.ts.map +1 -0
  123. package/dist/core/machine-identity.js +42 -0
  124. package/dist/core/machine-identity.js.map +1 -0
  125. package/dist/core/machine-list-groups.d.ts +13 -0
  126. package/dist/core/machine-list-groups.d.ts.map +1 -0
  127. package/dist/core/machine-list-groups.js +41 -0
  128. package/dist/core/machine-list-groups.js.map +1 -0
  129. package/dist/core/machine-name-claim.d.ts +22 -0
  130. package/dist/core/machine-name-claim.d.ts.map +1 -0
  131. package/dist/core/machine-name-claim.js +52 -0
  132. package/dist/core/machine-name-claim.js.map +1 -0
  133. package/dist/core/membership-proof.d.ts +20 -9
  134. package/dist/core/membership-proof.d.ts.map +1 -1
  135. package/dist/core/membership-proof.js +41 -15
  136. package/dist/core/membership-proof.js.map +1 -1
  137. package/dist/core/membership-proofs.d.ts +16 -8
  138. package/dist/core/membership-proofs.d.ts.map +1 -1
  139. package/dist/core/membership-proofs.js +24 -12
  140. package/dist/core/membership-proofs.js.map +1 -1
  141. package/dist/core/mesh-graph-trace-tool.d.ts +3 -2
  142. package/dist/core/mesh-graph-trace-tool.d.ts.map +1 -1
  143. package/dist/core/mesh-graph-trace-tool.js +8 -5
  144. package/dist/core/mesh-graph-trace-tool.js.map +1 -1
  145. package/dist/core/mesh-store-shared.d.ts +4 -1
  146. package/dist/core/mesh-store-shared.d.ts.map +1 -1
  147. package/dist/core/mesh-store-shared.js.map +1 -1
  148. package/dist/core/mesh-store.d.ts +20 -10
  149. package/dist/core/mesh-store.d.ts.map +1 -1
  150. package/dist/core/mesh-store.js +45 -38
  151. package/dist/core/mesh-store.js.map +1 -1
  152. package/dist/core/name-claim.d.ts +38 -0
  153. package/dist/core/name-claim.d.ts.map +1 -0
  154. package/dist/core/name-claim.js +93 -0
  155. package/dist/core/name-claim.js.map +1 -0
  156. package/dist/core/naming-actions.d.ts +21 -0
  157. package/dist/core/naming-actions.d.ts.map +1 -0
  158. package/dist/core/naming-actions.js +58 -0
  159. package/dist/core/naming-actions.js.map +1 -0
  160. package/dist/core/naming.d.ts +50 -0
  161. package/dist/core/naming.d.ts.map +1 -0
  162. package/dist/core/naming.js +81 -0
  163. package/dist/core/naming.js.map +1 -0
  164. package/dist/core/network-interfaces.d.ts +7 -0
  165. package/dist/core/network-interfaces.d.ts.map +1 -0
  166. package/dist/core/network-interfaces.js +23 -0
  167. package/dist/core/network-interfaces.js.map +1 -0
  168. package/dist/core/peer-versions.d.ts +6 -0
  169. package/dist/core/peer-versions.d.ts.map +1 -1
  170. package/dist/core/peer-versions.js +17 -0
  171. package/dist/core/peer-versions.js.map +1 -1
  172. package/dist/core/petnames.d.ts +22 -0
  173. package/dist/core/petnames.d.ts.map +1 -0
  174. package/dist/core/petnames.js +102 -0
  175. package/dist/core/petnames.js.map +1 -0
  176. package/dist/core/room-path.d.ts +2 -0
  177. package/dist/core/room-path.d.ts.map +1 -1
  178. package/dist/core/room-path.js +5 -1
  179. package/dist/core/room-path.js.map +1 -1
  180. package/dist/core/store.d.ts +4 -0
  181. package/dist/core/store.d.ts.map +1 -1
  182. package/dist/core/store.js +19 -2
  183. package/dist/core/store.js.map +1 -1
  184. package/dist/core/tool.d.ts +16 -5
  185. package/dist/core/tool.d.ts.map +1 -1
  186. package/dist/core/tool.js +59 -13
  187. package/dist/core/tool.js.map +1 -1
  188. package/dist/core/types.d.ts +28 -0
  189. package/dist/core/types.d.ts.map +1 -1
  190. package/dist/core/types.js +18 -0
  191. package/dist/core/types.js.map +1 -1
  192. package/dist/core/user-identity.d.ts +4 -6
  193. package/dist/core/user-identity.d.ts.map +1 -1
  194. package/dist/core/user-identity.js +9 -113
  195. package/dist/core/user-identity.js.map +1 -1
  196. package/dist/core/wire-mesh-transport-options.d.ts +2 -2
  197. package/dist/core/wire-mesh-transport-options.d.ts.map +1 -1
  198. package/dist/core/wire-mesh-transport.d.ts.map +1 -1
  199. package/dist/core/wire-mesh-transport.js +7 -7
  200. package/dist/core/wire-mesh-transport.js.map +1 -1
  201. package/dist/test/agent-table.unit.test.d.ts +5 -0
  202. package/dist/test/agent-table.unit.test.d.ts.map +1 -0
  203. package/dist/test/agent-table.unit.test.js +53 -0
  204. package/dist/test/agent-table.unit.test.js.map +1 -0
  205. package/dist/test/delivery-engine-delivery.unit.test.js +2 -0
  206. package/dist/test/delivery-engine-delivery.unit.test.js.map +1 -1
  207. package/dist/test/delivery-engine-directed-notify.unit.test.js +2 -0
  208. package/dist/test/delivery-engine-directed-notify.unit.test.js.map +1 -1
  209. package/dist/test/delivery-engine-own-agent.unit.test.js +2 -0
  210. package/dist/test/delivery-engine-own-agent.unit.test.js.map +1 -1
  211. package/dist/test/delivery-engine.unit.test.js +2 -0
  212. package/dist/test/delivery-engine.unit.test.js.map +1 -1
  213. package/dist/test/device-membership-verification.unit.test.js +25 -25
  214. package/dist/test/device-membership-verification.unit.test.js.map +1 -1
  215. package/dist/test/device-membership.unit.test.js +6 -6
  216. package/dist/test/device-membership.unit.test.js.map +1 -1
  217. package/dist/test/directory-admission.unit.test.js +103 -7
  218. package/dist/test/directory-admission.unit.test.js.map +1 -1
  219. package/dist/test/display-name.unit.test.d.ts +5 -0
  220. package/dist/test/display-name.unit.test.d.ts.map +1 -0
  221. package/dist/test/display-name.unit.test.js +65 -0
  222. package/dist/test/display-name.unit.test.js.map +1 -0
  223. package/dist/test/gateway-trust-tool.integration.test.js +117 -4
  224. package/dist/test/gateway-trust-tool.integration.test.js.map +1 -1
  225. package/dist/test/gateway-trust.unit.test.js +62 -13
  226. package/dist/test/gateway-trust.unit.test.js.map +1 -1
  227. package/dist/test/gossip-directory.unit.test.js +14 -1
  228. package/dist/test/gossip-directory.unit.test.js.map +1 -1
  229. package/dist/test/hub-exposure.integration.test.js +1 -1
  230. package/dist/test/hub-exposure.integration.test.js.map +1 -1
  231. package/dist/test/identity-restart.integration.test.js +3 -0
  232. package/dist/test/identity-restart.integration.test.js.map +1 -1
  233. package/dist/test/identity-store.integration.test.js +64 -14
  234. package/dist/test/identity-store.integration.test.js.map +1 -1
  235. package/dist/test/machine-grouping.unit.test.d.ts +2 -0
  236. package/dist/test/machine-grouping.unit.test.d.ts.map +1 -0
  237. package/dist/test/machine-grouping.unit.test.js +68 -0
  238. package/dist/test/machine-grouping.unit.test.js.map +1 -0
  239. package/dist/test/machine-identity.unit.test.d.ts +5 -0
  240. package/dist/test/machine-identity.unit.test.d.ts.map +1 -0
  241. package/dist/test/machine-identity.unit.test.js +80 -0
  242. package/dist/test/machine-identity.unit.test.js.map +1 -0
  243. package/dist/test/machine-list-groups.unit.test.d.ts +5 -0
  244. package/dist/test/machine-list-groups.unit.test.d.ts.map +1 -0
  245. package/dist/test/machine-list-groups.unit.test.js +61 -0
  246. package/dist/test/machine-list-groups.unit.test.js.map +1 -0
  247. package/dist/test/machine-membership-trust.integration.test.d.ts +5 -0
  248. package/dist/test/machine-membership-trust.integration.test.d.ts.map +1 -0
  249. package/dist/test/machine-membership-trust.integration.test.js +152 -0
  250. package/dist/test/machine-membership-trust.integration.test.js.map +1 -0
  251. package/dist/test/membership-proof.unit.test.js +105 -13
  252. package/dist/test/membership-proof.unit.test.js.map +1 -1
  253. package/dist/test/name-claim.unit.test.d.ts +5 -0
  254. package/dist/test/name-claim.unit.test.d.ts.map +1 -0
  255. package/dist/test/name-claim.unit.test.js +135 -0
  256. package/dist/test/name-claim.unit.test.js.map +1 -0
  257. package/dist/test/naming.integration.test.d.ts +5 -0
  258. package/dist/test/naming.integration.test.d.ts.map +1 -0
  259. package/dist/test/naming.integration.test.js +188 -0
  260. package/dist/test/naming.integration.test.js.map +1 -0
  261. package/dist/test/petnames.unit.test.d.ts +5 -0
  262. package/dist/test/petnames.unit.test.d.ts.map +1 -0
  263. package/dist/test/petnames.unit.test.js +94 -0
  264. package/dist/test/petnames.unit.test.js.map +1 -0
  265. package/dist/test/principal-membership-trust.integration.test.js +2 -1
  266. package/dist/test/principal-membership-trust.integration.test.js.map +1 -1
  267. package/dist/test/room-join-admission.integration.test.js +3 -0
  268. package/dist/test/room-join-admission.integration.test.js.map +1 -1
  269. package/dist/test/room-lifecycle-bare-name.unit.test.js +2 -0
  270. package/dist/test/room-lifecycle-bare-name.unit.test.js.map +1 -1
  271. package/dist/test/room-lifecycle-membership.unit.test.js +2 -0
  272. package/dist/test/room-lifecycle-membership.unit.test.js.map +1 -1
  273. package/dist/test/room-lifecycle-remote.unit.test.js +2 -0
  274. package/dist/test/room-lifecycle-remote.unit.test.js.map +1 -1
  275. package/dist/test/room-lifecycle.unit.test.js +2 -0
  276. package/dist/test/room-lifecycle.unit.test.js.map +1 -1
  277. package/dist/test/room-messaging-durable-send.unit.test.js +2 -0
  278. package/dist/test/room-messaging-durable-send.unit.test.js.map +1 -1
  279. package/dist/test/room-messaging.unit.test.js +2 -0
  280. package/dist/test/room-messaging.unit.test.js.map +1 -1
  281. package/dist/test/room-protocol-notify.unit.test.js +2 -0
  282. package/dist/test/room-protocol-notify.unit.test.js.map +1 -1
  283. package/dist/test/room-protocol.helper.d.ts.map +1 -1
  284. package/dist/test/room-protocol.helper.js +2 -0
  285. package/dist/test/room-protocol.helper.js.map +1 -1
  286. package/dist/test/room-send-retry.integration.test.js +3 -0
  287. package/dist/test/room-send-retry.integration.test.js.map +1 -1
  288. package/dist/test/test-transport.d.ts +3 -0
  289. package/dist/test/test-transport.d.ts.map +1 -1
  290. package/dist/test/test-transport.js +8 -1
  291. package/dist/test/test-transport.js.map +1 -1
  292. package/dist/test/user-identity.unit.test.js +40 -14
  293. package/dist/test/user-identity.unit.test.js.map +1 -1
  294. package/package.json +1 -1
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Which machine each listed agent runs on (agent-comms#343), for list_agents' machine-grouped output. This device's own machine is known directly; every other device's is read from the machine proof in its gossiped agent/self advert and identified cryptographically (membership-proof.ts's identifyMembershipProofIssuer), so a device is grouped under a machine only when that machine's key signed a current proof for it. A device with no such proof (a hidden agent, which sends no agent/self advert, or a bridge that predates machine proofs) has no machine here.
3
+ *
4
+ * The same pass reads each machine's self display name claim (agent-comms#345) from the adverts of the devices it vouches for, and keeps a name only when the claim is signed by that very machine's key: the issuer is authentic, the content is the machine's own choice.
5
+ *
6
+ * Grouping is display, not trust: a proof names who vouches for a device id, not that the device gossiping it is that device, so nothing here is used to authorise anything. Identifying a proof is cryptography, so verdicts are remembered per proof text until the proof lapses, and the cache is bounded. For the same reason a failure to identify one proof is reported and leaves that device unplaced rather than failing the listing it feeds.
7
+ */
8
+ import { readMachineNameClaim, readMembershipProof, } from "./gossip-directory.js";
9
+ /** The most proofs, and separately the most name claims, whose verdict is remembered. */
10
+ const VERDICT_CACHE_LIMIT = 1024;
11
+ export class MachineGrouping {
12
+ deps;
13
+ verdicts = new Map();
14
+ names = new Map();
15
+ constructor(deps) {
16
+ this.deps = deps;
17
+ }
18
+ /** The machine (hex) of each device this side can place on one, keyed by device id: this device's own, and every gossiped device with a current, valid machine proof. */
19
+ async machinesByDevice() {
20
+ const machines = new Map();
21
+ const own = this.deps.getMachineId();
22
+ // Without an identity attached there is no clock or revocation view to identify a proof against, and no own machine either.
23
+ if (own === undefined)
24
+ return machines;
25
+ machines.set(this.deps.getPeerId(), own);
26
+ for (const { deviceId, advert } of this.deps.listKnownDevices()) {
27
+ if (machines.has(deviceId))
28
+ continue;
29
+ const proof = readMembershipProof(advert, "machine");
30
+ if (proof === undefined)
31
+ continue;
32
+ const machine = await this.machineFor(proof, deviceId);
33
+ if (machine !== undefined)
34
+ machines.set(deviceId, machine);
35
+ }
36
+ return machines;
37
+ }
38
+ /** The self display name of each machine this side can place a device on, keyed by machine id: this host's own from its file, every other one from a claim its own key signed, gossiped by a device it vouches for. */
39
+ async machineNames() {
40
+ const names = new Map();
41
+ const machines = await this.machinesByDevice();
42
+ const own = this.deps.getMachineId();
43
+ const ownName = this.deps.getOwnMachineName();
44
+ if (own !== undefined && ownName !== undefined)
45
+ names.set(own, ownName);
46
+ for (const { deviceId, advert } of this.deps.listKnownDevices()) {
47
+ const machine = machines.get(deviceId);
48
+ if (machine === undefined || names.has(machine))
49
+ continue;
50
+ const claim = readMachineNameClaim(advert);
51
+ if (claim === undefined)
52
+ continue;
53
+ const name = await this.nameFor(claim, machine);
54
+ if (name !== undefined)
55
+ names.set(machine, name);
56
+ }
57
+ return names;
58
+ }
59
+ async nameFor(claim, subject) {
60
+ const cached = this.names.get(claim);
61
+ if (cached?.subject === subject && cached.until > Date.now()) {
62
+ return cached.name;
63
+ }
64
+ const verdict = await this.deps.verifyName({ claim, subject });
65
+ remember(this.names, claim, verdict.ok
66
+ ? { subject, name: verdict.name, until: verdict.expires }
67
+ : { subject, name: undefined, until: Number.POSITIVE_INFINITY });
68
+ return verdict.ok ? verdict.name : undefined;
69
+ }
70
+ async machineFor(proof, deviceHex) {
71
+ const cached = this.verdicts.get(proof);
72
+ if (cached?.deviceHex === deviceHex && cached.until > Date.now()) {
73
+ return cached.machine;
74
+ }
75
+ let verdict;
76
+ try {
77
+ verdict = await this.deps.identify({ proof, deviceHex });
78
+ }
79
+ catch (err) {
80
+ // Not remembered: the failure is in this side's own view (a revocation view that threw, say), not a verdict on the proof, so the next listing tries again.
81
+ this.deps.onError(new Error(`could not identify the machine proof of ${deviceHex}`, {
82
+ cause: err,
83
+ }));
84
+ return undefined;
85
+ }
86
+ remember(this.verdicts, proof, verdict.ok
87
+ ? { deviceHex, machine: verdict.issuerHex, until: verdict.expires }
88
+ : { deviceHex, machine: undefined, until: Number.POSITIVE_INFINITY });
89
+ return verdict.ok ? verdict.issuerHex : undefined;
90
+ }
91
+ }
92
+ /** Records verdict under key, evicting the oldest entry once the cache holds more than VERDICT_CACHE_LIMIT. */
93
+ function remember(cache, key, verdict) {
94
+ cache.delete(key);
95
+ cache.set(key, verdict);
96
+ if (cache.size > VERDICT_CACHE_LIMIT) {
97
+ const oldest = cache.keys().next();
98
+ if (oldest.done !== true)
99
+ cache.delete(oldest.value);
100
+ }
101
+ }
102
+ //# sourceMappingURL=machine-grouping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-grouping.js","sourceRoot":"","sources":["../../src/core/machine-grouping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Persistent machine identity (agent-comms#343): one keypair per OS account per host, the third identity scope beside a bridge slot's device identity (identity-store.ts) and the account's user principal (user-identity.ts). It is structurally another grouping issuer, like the principal: it vouches for the devices living on this host with the same group:member proofs (membership-proof.ts), so a peer can group devices by machine and trust or stop trusting a whole host in one act.
3
+ *
4
+ * Stored at ~/.agent-comms/machine-identity.json (mode 0600), so it belongs to the OS account whose home directory holds it: every bridge that account runs on the host reads the same file and is vouched for by the same machine key, while another account on the same host mints a machine key of its own and appears as a separate machine. Until the principal becomes portable across hosts (agent-comms#344) this is the same scope as the user principal; a key naming the host across every account needs a signer outside any one account and is agent-comms#367. The key is generated locally on first use and never derived from hardware (issuer-identity-file.ts says why); re-homing a rebuilt host is a deliberate copy of this file.
5
+ */
6
+ import type { PeerIdentity } from "./identity.js";
7
+ export interface MachineIdentityOptions {
8
+ /** Directory override for tests; defaults to ~/.agent-comms, beside the user identity and every bridge slot. */
9
+ dir?: string;
10
+ }
11
+ /** The machine identity file for options. */
12
+ export declare function machineIdentityFile(options?: Readonly<MachineIdentityOptions>): string;
13
+ /** Loads this account's machine identity for this host, creating it on first use (exclusive create, renewal in place, an unusable file refused rather than regenerated). */
14
+ export declare function loadOrCreateMachineIdentity(options?: Readonly<MachineIdentityOptions>): PeerIdentity;
15
+ /** The name this machine asserts for itself, or undefined when it has none or the machine identity has not been created yet. Read from disk on every call, so a name set by any bridge on the host is seen by all of them. */
16
+ export declare function loadMachineDisplayName(options?: Readonly<MachineIdentityOptions>): string | undefined;
17
+ /** Sets (or, given undefined, clears) the name this machine asserts for itself, keeping the key material and every other field. Throws if the machine identity has not been created yet. */
18
+ export declare function saveMachineDisplayName(options: Readonly<MachineIdentityOptions> | undefined, displayName: string | undefined): void;
19
+ //# sourceMappingURL=machine-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-identity.d.ts","sourceRoot":"","sources":["../../src/core/machine-identity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AASlD,MAAM,WAAW,sBAAsB;IACrC,gHAAgH;IAChH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GACzC,MAAM,CAGR;AAED,4KAA4K;AAC5K,wBAAgB,2BAA2B,CACzC,OAAO,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GACzC,YAAY,CAKd;AAcD,8NAA8N;AAC9N,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GACzC,MAAM,GAAG,SAAS,CAGpB;AAED,4LAA4L;AAC5L,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GAAG,SAAS,EACrD,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,IAAI,CAYN"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Persistent machine identity (agent-comms#343): one keypair per OS account per host, the third identity scope beside a bridge slot's device identity (identity-store.ts) and the account's user principal (user-identity.ts). It is structurally another grouping issuer, like the principal: it vouches for the devices living on this host with the same group:member proofs (membership-proof.ts), so a peer can group devices by machine and trust or stop trusting a whole host in one act.
3
+ *
4
+ * Stored at ~/.agent-comms/machine-identity.json (mode 0600), so it belongs to the OS account whose home directory holds it: every bridge that account runs on the host reads the same file and is vouched for by the same machine key, while another account on the same host mints a machine key of its own and appears as a separate machine. Until the principal becomes portable across hosts (agent-comms#344) this is the same scope as the user principal; a key naming the host across every account needs a signer outside any one account and is agent-comms#367. The key is generated locally on first use and never derived from hardware (issuer-identity-file.ts says why); re-homing a rebuilt host is a deliberate copy of this file.
5
+ */
6
+ import * as os from "node:os";
7
+ import * as path from "node:path";
8
+ import { isStoredIssuerKey, loadOrCreateIssuerIdentity, readIssuerRecord, writeIssuerRecord, } from "./issuer-identity-file.js";
9
+ /** The machine identity file for options. */
10
+ export function machineIdentityFile(options) {
11
+ const dir = options?.dir ?? path.join(os.homedir(), ".agent-comms");
12
+ return path.join(dir, "machine-identity.json");
13
+ }
14
+ /** Loads this account's machine identity for this host, creating it on first use (exclusive create, renewal in place, an unusable file refused rather than regenerated). */
15
+ export function loadOrCreateMachineIdentity(options) {
16
+ return loadOrCreateIssuerIdentity(machineIdentityFile(options), isStoredMachineIdentity);
17
+ }
18
+ function isStoredMachineIdentity(value) {
19
+ if (!isStoredIssuerKey(value))
20
+ return false;
21
+ return !("displayName" in value) || typeof value.displayName === "string";
22
+ }
23
+ /** The name this machine asserts for itself, or undefined when it has none or the machine identity has not been created yet. Read from disk on every call, so a name set by any bridge on the host is seen by all of them. */
24
+ export function loadMachineDisplayName(options) {
25
+ return readIssuerRecord(machineIdentityFile(options), isStoredMachineIdentity)
26
+ ?.displayName;
27
+ }
28
+ /** Sets (or, given undefined, clears) the name this machine asserts for itself, keeping the key material and every other field. Throws if the machine identity has not been created yet. */
29
+ export function saveMachineDisplayName(options, displayName) {
30
+ const file = machineIdentityFile(options);
31
+ const stored = readIssuerRecord(file, isStoredMachineIdentity);
32
+ if (stored === undefined) {
33
+ throw new Error(`no machine identity persisted yet; call loadOrCreateMachineIdentity first (${file})`);
34
+ }
35
+ const next = { ...stored };
36
+ if (displayName === undefined)
37
+ delete next.displayName;
38
+ else
39
+ next.displayName = displayName;
40
+ writeIssuerRecord(file, next);
41
+ }
42
+ //# sourceMappingURL=machine-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-identity.js","sourceRoot":"","sources":["../../src/core/machine-identity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,2BAA2B,CAAC;AAOnC,6CAA6C;AAC7C,MAAM,UAAU,mBAAmB,CACjC,OAA0C;IAE1C,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;AACjD,CAAC;AAED,4KAA4K;AAC5K,MAAM,UAAU,2BAA2B,CACzC,OAA0C;IAE1C,OAAO,0BAA0B,CAC/B,mBAAmB,CAAC,OAAO,CAAC,EAC5B,uBAAuB,CACxB,CAAC;AACJ,CAAC;AAOD,SAAS,uBAAuB,CAC9B,KAAc;IAEd,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,CAAC,CAAC,aAAa,IAAI,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC;AAC5E,CAAC;AAED,8NAA8N;AAC9N,MAAM,UAAU,sBAAsB,CACpC,OAA0C;IAE1C,OAAO,gBAAgB,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;QAC5E,EAAE,WAAW,CAAC;AAClB,CAAC;AAED,4LAA4L;AAC5L,MAAM,UAAU,sBAAsB,CACpC,OAAqD,EACrD,WAA+B;IAE/B,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;IAC/D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,8EAA8E,IAAI,GAAG,CACtF,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAA0B,EAAE,GAAG,MAAM,EAAE,CAAC;IAClD,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,WAAW,CAAC;;QAClD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACpC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * list_agents' machine grouping (agent-comms#343): the listed agents split into one group per machine, this host first, then every other machine by id, then the agents no machine proof places. Split out of tool.ts to keep that file under the repo's max-lines cap.
3
+ */
4
+ import type { AgentIdentity } from "./types.js";
5
+ import type { Namer } from "./naming.js";
6
+ /** One run of listed agents and the heading above it; no heading when nothing is grouped at all. */
7
+ export interface AgentGroup {
8
+ heading: string | undefined;
9
+ agents: AgentIdentity[];
10
+ }
11
+ /** agents grouped by machine, each group keeping the agents' listed order and each heading naming its machine by the display convention (namer). machines maps device id to machine id (MeshStore.listAgentMachines); undefined on a store with no machine identity, in which case, as when no agent is placed on a machine, the list is one ungrouped run. */
12
+ export declare function groupAgentsByMachine(agents: readonly AgentIdentity[], machines: ReadonlyMap<string, string> | undefined, ownMachine: string | undefined, namer: Namer): AgentGroup[];
13
+ //# sourceMappingURL=machine-list-groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-list-groups.d.ts","sourceRoot":"","sources":["../../src/core/machine-list-groups.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,oGAAoG;AACpG,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,+VAA+V;AAC/V,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACjD,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,EAAE,KAAK,GACX,UAAU,EAAE,CAiCd"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * list_agents' machine grouping (agent-comms#343): the listed agents split into one group per machine, this host first, then every other machine by id, then the agents no machine proof places. Split out of tool.ts to keep that file under the repo's max-lines cap.
3
+ */
4
+ /** agents grouped by machine, each group keeping the agents' listed order and each heading naming its machine by the display convention (namer). machines maps device id to machine id (MeshStore.listAgentMachines); undefined on a store with no machine identity, in which case, as when no agent is placed on a machine, the list is one ungrouped run. */
5
+ export function groupAgentsByMachine(agents, machines, ownMachine, namer) {
6
+ if (machines === undefined ||
7
+ !agents.some((agent) => machines.has(agent.id))) {
8
+ return [{ heading: undefined, agents: [...agents] }];
9
+ }
10
+ const byMachine = new Map();
11
+ const unplaced = [];
12
+ for (const agent of agents) {
13
+ const machine = machines.get(agent.id);
14
+ if (machine === undefined) {
15
+ unplaced.push(agent);
16
+ continue;
17
+ }
18
+ const group = byMachine.get(machine);
19
+ if (group === undefined)
20
+ byMachine.set(machine, [agent]);
21
+ else
22
+ group.push(agent);
23
+ }
24
+ const groups = [...byMachine]
25
+ .sort(([a], [b]) => {
26
+ if (a === ownMachine)
27
+ return -1;
28
+ if (b === ownMachine)
29
+ return 1;
30
+ return a.localeCompare(b);
31
+ })
32
+ .map(([machine, members]) => ({
33
+ heading: `Machine ${namer(machine)}${machine === ownMachine ? " (this machine)" : ""}:`,
34
+ agents: members,
35
+ }));
36
+ if (unplaced.length > 0) {
37
+ groups.push({ heading: "Machine not proven:", agents: unplaced });
38
+ }
39
+ return groups;
40
+ }
41
+ //# sourceMappingURL=machine-list-groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-list-groups.js","sourceRoot":"","sources":["../../src/core/machine-list-groups.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,+VAA+V;AAC/V,MAAM,UAAU,oBAAoB,CAClC,MAAgC,EAChC,QAAiD,EACjD,UAA8B,EAC9B,KAAY;IAEZ,IACE,QAAQ,KAAK,SAAS;QACtB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAC/C,CAAC;QACD,OAAO,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IACrD,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;;YACpD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,MAAM,GAAiB,CAAC,GAAG,SAAS,CAAC;SACxC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;QACjB,IAAI,CAAC,KAAK,UAAU;YAAE,OAAO,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,UAAU;YAAE,OAAO,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5B,OAAO,EAAE,WAAW,KAAK,CAAC,OAAO,CAAC,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG;QACvF,MAAM,EAAE,OAAO;KAChB,CAAC,CAAC,CAAC;IACN,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * This host's self display name claim (agent-comms#345), kept fresh beside the machine proof: re-minted on the proofs' own cadence from whatever name machine-identity.json currently holds, so a name set or cleared by any bridge on the host reaches every bridge's advert within one refresh, and the setting bridge's at once.
3
+ */
4
+ import type { MeshStoreIdentity } from "./mesh-store-shared.js";
5
+ export interface MachineNameClaimDeps {
6
+ /** Throws if the store has no identity yet. */
7
+ getIdentity: () => MeshStoreIdentity;
8
+ onError: (error: Error) => void;
9
+ }
10
+ export declare class MachineNameClaim {
11
+ private readonly deps;
12
+ private claim;
13
+ private timer;
14
+ constructor(deps: Readonly<MachineNameClaimDeps>);
15
+ /** The current claim, undefined while the machine has no name or before the first claim is minted. */
16
+ current(): string | undefined;
17
+ start(): void;
18
+ stop(): void;
19
+ /** Re-reads the machine's name and re-mints its claim now; drops the claim when the machine has no name. A failed mint is reported and the previous claim is kept until the next refresh. */
20
+ refresh(): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=machine-name-claim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-name-claim.d.ts","sourceRoot":"","sources":["../../src/core/machine-name-claim.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,iBAAiB,CAAC;IACrC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,gBAAgB;IAIf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAA6C;gBAE7B,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IAEjE,sGAAsG;IACtG,OAAO,IAAI,MAAM,GAAG,SAAS;IAI7B,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IAKZ,6LAA6L;IACvL,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAoB/B"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * This host's self display name claim (agent-comms#345), kept fresh beside the machine proof: re-minted on the proofs' own cadence from whatever name machine-identity.json currently holds, so a name set or cleared by any bridge on the host reaches every bridge's advert within one refresh, and the setting bridge's at once.
3
+ */
4
+ import { loadMachineDisplayName } from "./machine-identity.js";
5
+ import { MEMBERSHIP_PROOF_LIFETIME_MS } from "./membership-proof.js";
6
+ import { PROOF_REFRESH_INTERVAL_MS } from "./membership-proofs.js";
7
+ import { mintNameClaim } from "./name-claim.js";
8
+ export class MachineNameClaim {
9
+ deps;
10
+ claim;
11
+ timer;
12
+ constructor(deps) {
13
+ this.deps = deps;
14
+ }
15
+ /** The current claim, undefined while the machine has no name or before the first claim is minted. */
16
+ current() {
17
+ return this.claim;
18
+ }
19
+ start() {
20
+ this.stop();
21
+ void this.refresh();
22
+ this.timer = setInterval(() => {
23
+ void this.refresh();
24
+ }, PROOF_REFRESH_INTERVAL_MS);
25
+ this.timer.unref();
26
+ }
27
+ stop() {
28
+ clearInterval(this.timer);
29
+ this.timer = undefined;
30
+ }
31
+ /** Re-reads the machine's name and re-mints its claim now; drops the claim when the machine has no name. A failed mint is reported and the previous claim is kept until the next refresh. */
32
+ async refresh() {
33
+ const identity = this.deps.getIdentity();
34
+ const name = loadMachineDisplayName(identity.machineIdentityOptions);
35
+ if (name === undefined) {
36
+ this.claim = undefined;
37
+ return;
38
+ }
39
+ try {
40
+ this.claim = await mintNameClaim({
41
+ issuer: identity.machineIdentity,
42
+ clock: identity.clock,
43
+ name,
44
+ lifetimeMs: MEMBERSHIP_PROOF_LIFETIME_MS,
45
+ });
46
+ }
47
+ catch (error) {
48
+ this.deps.onError(error instanceof Error ? error : new Error(String(error)));
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=machine-name-claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-name-claim.js","sourceRoot":"","sources":["../../src/core/machine-name-claim.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQhD,MAAM,OAAO,gBAAgB;IAIE;IAHrB,KAAK,CAAqB;IAC1B,KAAK,CAA6C;IAE1D,YAA6B,IAAoC;QAApC,SAAI,GAAJ,IAAI,CAAgC;IAAG,CAAC;IAErE,sGAAsG;IACtG,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,EAAE,yBAAyB,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI;QACF,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAED,6LAA6L;IAC7L,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,sBAAsB,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC;QACrE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,GAAG,MAAM,aAAa,CAAC;gBAC/B,MAAM,EAAE,QAAQ,CAAC,eAAe;gBAChC,KAAK,EAAE,QAAQ,CAAC,KAAK;gBACrB,IAAI;gBACJ,UAAU,EAAE,4BAA4B;aACzC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,IAAI,CAAC,OAAO,CACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * The proof a device gossips to show that its user principal vouches for it (agent-comms#266): a short-lived group:member token, minted by that principal for that one device, carried as one line of text. A receiver that trusts the principal verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal is for.
2
+ * The proof a device gossips to show that a grouping issuer vouches for it (agent-comms#266 for the user principal, agent-comms#343 for the machine): a short-lived group:member token, minted by that issuer for that one device, carried as one line of text. A receiver that trusts the issuer verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal or a machine is for. A receiver that only wants to know which machine a device runs on reads the issuer out of the proof (identifyMembershipProofIssuer).
3
3
  *
4
- * A proof shows that the principal vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the principal, so anyone connected to the hub can see which devices belong to which principal.
4
+ * A proof shows that the issuer vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the issuer, so anyone connected to the hub can see which devices belong to which principal and which run on the same machine. That is why a proof is only ever carried in the agent/self advert, which a hidden or ghost agent never sends.
5
5
  *
6
- * Every bridge of one account shares that account's user identity, so any of them can mint a proof for any device id, which is the same trust the principal is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the account's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared user-identity.json.
6
+ * Every bridge of one account shares that account's user identity, and every bridge one account runs on a host shares that account's machine identity for the host (machine-identity.ts), so any of them can mint a proof for any device id, which is the same trust the issuer is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the issuer's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared identity file.
7
7
  */
8
8
  import type { Clock } from "wire-mesh-core/ports/clock";
9
9
  import type { IdentityPort } from "wire-mesh-core/ports/identity";
@@ -14,20 +14,20 @@ export declare const MAX_MEMBERSHIP_PROOF_LENGTH = 4096;
14
14
  /** How long a minted proof stays valid. Long enough to ride out a missed refresh or a gossip gap, short enough that a device an account stops running stops being trusted soon after. */
15
15
  export declare const MEMBERSHIP_PROOF_LIFETIME_MS: number;
16
16
  export interface MintMembershipProofOptions {
17
- /** The user principal vouching for the device. */
18
- userIdentity: IdentityPort;
17
+ /** The grouping issuer vouching for the device: the user principal or the machine. */
18
+ issuer: IdentityPort;
19
19
  clock: Clock;
20
20
  /** The device the proof is for, and the only device it will verify for. */
21
21
  deviceId: DeviceId;
22
22
  }
23
- /** A fresh proof that options.userIdentity vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
23
+ /** A fresh proof that options.issuer vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
24
24
  export declare function mintMembershipProof(options: Readonly<MintMembershipProofOptions>): Promise<string>;
25
25
  export interface VerifyMembershipProofOptions {
26
26
  proof: string;
27
27
  /** The device the proof is claimed for: the gossiping device the directory entry names. */
28
28
  deviceId: DeviceId;
29
- /** The principal the proof must have been minted by. */
30
- principalId: DeviceId;
29
+ /** The issuer (principal or machine) the proof must have been minted by. */
30
+ issuerId: DeviceId;
31
31
  /** The verifying node's own identity, clock and revocation view. */
32
32
  identity: IdentityPort;
33
33
  clock: Clock;
@@ -40,6 +40,17 @@ export type MembershipProofVerdict = {
40
40
  ok: false;
41
41
  reason: string;
42
42
  };
43
- /** Whether options.proof shows that options.principalId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or principal, an expired or revoked one, all come back as a refusal. */
43
+ /** Whether options.proof shows that options.issuerId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or issuer, an expired or revoked one, all come back as a refusal. */
44
44
  export declare function verifyMembershipProof(options: Readonly<VerifyMembershipProofOptions>): Promise<MembershipProofVerdict>;
45
+ export type IdentifyMembershipProofOptions = Omit<VerifyMembershipProofOptions, "issuerId">;
46
+ export type IdentifiedMembershipProof = {
47
+ ok: true;
48
+ issuerHex: string;
49
+ expires: number;
50
+ } | {
51
+ ok: false;
52
+ reason: string;
53
+ };
54
+ /** Which issuer options.proof shows vouching for options.deviceId right now, learned from the proof itself rather than checked against one already known: the same refusals as verifyMembershipProof, plus a proof whose named group is not the key that signed it. */
55
+ export declare function identifyMembershipProofIssuer(options: Readonly<IdentifyMembershipProofOptions>): Promise<IdentifiedMembershipProof>;
45
56
  //# sourceMappingURL=membership-proof.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"membership-proof.d.ts","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAUlE,gPAAgP;AAChP,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD,yLAAyL;AACzL,eAAO,MAAM,4BAA4B,QACU,CAAC;AAEpD,MAAM,WAAW,0BAA0B;IACzC,kDAAkD;IAClD,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,mJAAmJ;AACnJ,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,QAAQ,CAAC,0BAA0B,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,EAAE,QAAQ,CAAC;IACnB,wDAAwD;IACxD,WAAW,EAAE,QAAQ,CAAC;IACtB,oEAAoE;IACpE,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,2PAA2P;AAC3P,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAC9C,OAAO,CAAC,sBAAsB,CAAC,CAqBjC"}
1
+ {"version":3,"file":"membership-proof.d.ts","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAEV,QAAQ,EACT,MAAM,mCAAmC,CAAC;AAa3C,gPAAgP;AAChP,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD,yLAAyL;AACzL,eAAO,MAAM,4BAA4B,QACU,CAAC;AAEpD,MAAM,WAAW,0BAA0B;IACzC,sFAAsF;IACtF,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,6IAA6I;AAC7I,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,QAAQ,CAAC,0BAA0B,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,EAAE,QAAQ,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,EAAE,QAAQ,CAAC;IACnB,oEAAoE;IACpE,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAgBhE,qPAAqP;AACrP,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAC9C,OAAO,CAAC,sBAAsB,CAAC,CAcjC;AAED,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,4BAA4B,EAC5B,UAAU,CACX,CAAC;AAEF,MAAM,MAAM,yBAAyB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,uQAAuQ;AACvQ,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,QAAQ,CAAC,8BAA8B,CAAC,GAChD,OAAO,CAAC,yBAAyB,CAAC,CAgBpC"}
@@ -1,12 +1,12 @@
1
1
  /**
2
- * The proof a device gossips to show that its user principal vouches for it (agent-comms#266): a short-lived group:member token, minted by that principal for that one device, carried as one line of text. A receiver that trusts the principal verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal is for.
2
+ * The proof a device gossips to show that a grouping issuer vouches for it (agent-comms#266 for the user principal, agent-comms#343 for the machine): a short-lived group:member token, minted by that issuer for that one device, carried as one line of text. A receiver that trusts the issuer verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal or a machine is for. A receiver that only wants to know which machine a device runs on reads the issuer out of the proof (identifyMembershipProofIssuer).
3
3
  *
4
- * A proof shows that the principal vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the principal, so anyone connected to the hub can see which devices belong to which principal.
4
+ * A proof shows that the issuer vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the issuer, so anyone connected to the hub can see which devices belong to which principal and which run on the same machine. That is why a proof is only ever carried in the agent/self advert, which a hidden or ghost agent never sends.
5
5
  *
6
- * Every bridge of one account shares that account's user identity, so any of them can mint a proof for any device id, which is the same trust the principal is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the account's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared user-identity.json.
6
+ * Every bridge of one account shares that account's user identity, and every bridge one account runs on a host shares that account's machine identity for the host (machine-identity.ts), so any of them can mint a proof for any device id, which is the same trust the issuer is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the issuer's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared identity file.
7
7
  */
8
- import { mintDeviceMembership } from "./device-membership.js";
9
- import { verifyDeviceMembership } from "./device-membership-verification.js";
8
+ import { mintGroupMembership } from "./device-membership.js";
9
+ import { identifyGroupIssuer, verifyDeviceMembership, } from "./device-membership-verification.js";
10
10
  import { randomId } from "./random-id.js";
11
11
  import { CommsError } from "./store.js";
12
12
  import { decodeTokenText, encodeTokenText } from "./token-text.js";
@@ -16,10 +16,10 @@ const MEMBERSHIP_PROOF_LIFETIME_MINUTES = 15;
16
16
  export const MAX_MEMBERSHIP_PROOF_LENGTH = 4096;
17
17
  /** How long a minted proof stays valid. Long enough to ride out a missed refresh or a gossip gap, short enough that a device an account stops running stops being trusted soon after. */
18
18
  export const MEMBERSHIP_PROOF_LIFETIME_MS = MEMBERSHIP_PROOF_LIFETIME_MINUTES * MS_PER_MINUTE;
19
- /** A fresh proof that options.userIdentity vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
19
+ /** A fresh proof that options.issuer vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
20
20
  export async function mintMembershipProof(options) {
21
- const verdict = await mintDeviceMembership({
22
- userIdentity: options.userIdentity,
21
+ const verdict = await mintGroupMembership({
22
+ issuer: options.issuer,
23
23
  clock: options.clock,
24
24
  tokenId: randomId(),
25
25
  deviceId: options.deviceId,
@@ -30,24 +30,29 @@ export async function mintMembershipProof(options) {
30
30
  }
31
31
  return encodeTokenText(verdict.token);
32
32
  }
33
- /** Whether options.proof shows that options.principalId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or principal, an expired or revoked one, all come back as a refusal. */
34
- export async function verifyMembershipProof(options) {
35
- if (options.proof.length > MAX_MEMBERSHIP_PROOF_LENGTH) {
33
+ /** The proof text decoded to a token, or the refusal for text that cannot be one. */
34
+ function decodeProof(proof) {
35
+ if (proof.length > MAX_MEMBERSHIP_PROOF_LENGTH) {
36
36
  return { ok: false, reason: "too_long" };
37
37
  }
38
- let token;
39
38
  try {
40
- token = decodeTokenText(options.proof);
39
+ return { ok: true, token: decodeTokenText(proof) };
41
40
  }
42
41
  catch {
43
42
  return { ok: false, reason: "not_a_token" };
44
43
  }
45
- const verdict = await verifyDeviceMembership(token, {
44
+ }
45
+ /** Whether options.proof shows that options.issuerId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or issuer, an expired or revoked one, all come back as a refusal. */
46
+ export async function verifyMembershipProof(options) {
47
+ const decoded = decodeProof(options.proof);
48
+ if (!decoded.ok)
49
+ return decoded;
50
+ const verdict = await verifyDeviceMembership(decoded.token, {
46
51
  identity: options.identity,
47
52
  clock: options.clock,
48
53
  revocation: options.revocation,
49
54
  expectedBearer: options.deviceId,
50
- userDeviceId: options.principalId,
55
+ groupIssuerId: options.issuerId,
51
56
  });
52
57
  if (!verdict.ok)
53
58
  return { ok: false, reason: verdict.reason };
@@ -56,4 +61,25 @@ export async function verifyMembershipProof(options) {
56
61
  return { ok: false, reason: "not_root_level" };
57
62
  return { ok: true, expires: verdict.claims.expires };
58
63
  }
64
+ /** Which issuer options.proof shows vouching for options.deviceId right now, learned from the proof itself rather than checked against one already known: the same refusals as verifyMembershipProof, plus a proof whose named group is not the key that signed it. */
65
+ export async function identifyMembershipProofIssuer(options) {
66
+ const decoded = decodeProof(options.proof);
67
+ if (!decoded.ok)
68
+ return decoded;
69
+ const verdict = await identifyGroupIssuer(decoded.token, {
70
+ identity: options.identity,
71
+ clock: options.clock,
72
+ revocation: options.revocation,
73
+ expectedBearer: options.deviceId,
74
+ });
75
+ if (!verdict.ok)
76
+ return { ok: false, reason: verdict.reason };
77
+ if (verdict.depth !== 0)
78
+ return { ok: false, reason: "not_root_level" };
79
+ return {
80
+ ok: true,
81
+ issuerHex: verdict.issuerHex,
82
+ expires: verdict.claims.expires,
83
+ };
84
+ }
59
85
  //# sourceMappingURL=membership-proof.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"membership-proof.js","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAE7C,gPAAgP;AAChP,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,yLAAyL;AACzL,MAAM,CAAC,MAAM,4BAA4B,GACvC,iCAAiC,GAAG,aAAa,CAAC;AAUpD,mJAAmJ;AACnJ,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAA6C;IAE7C,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC;QACzC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,QAAQ,EAAE;QACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,4BAA4B;KAC5D,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,UAAU,CAClB,sCAAsC,OAAO,CAAC,MAAM,EAAE,EACtD,aAAa,CACd,CAAC;IACJ,CAAC;IACD,OAAO,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAiBD,2PAA2P;AAC3P,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA+C;IAE/C,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,2BAA2B,EAAE,CAAC;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAC9C,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,KAAK,EAAE;QAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,QAAQ;QAChC,YAAY,EAAE,OAAO,CAAC,WAAW;KAClC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9D,gHAAgH;IAChH,IAAI,OAAO,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACxE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"membership-proof.js","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,16 @@
1
1
  /**
2
- * A device's own membership proof, kept fresh, and the check for other devices' proofs (agent-comms#266). Split out of MeshStore, which hands it its identity, peer id and error sink.
2
+ * A device's own membership proof under one grouping issuer, kept fresh, and the checks for other devices' proofs (agent-comms#266, agent-comms#343). MeshStore holds one instance per issuer, its user principal and its machine, and hands each its identity, peer id and error sink. The checks do not depend on which issuer an instance mints under, so either instance can verify or identify any proof.
3
3
  */
4
+ import type { IdentityPort } from "wire-mesh-core/ports/identity";
4
5
  import type { MeshStoreIdentity } from "./mesh-store-shared.js";
5
- import { type MembershipProofVerdict } from "./membership-proof.js";
6
+ import { type IdentifiedMembershipProof, type MembershipProofVerdict } from "./membership-proof.js";
7
+ /** How often an advertised proof, or a name claim riding beside it, is re-minted. */
8
+ export declare const PROOF_REFRESH_INTERVAL_MS: number;
6
9
  export interface MembershipProofsDeps {
7
10
  /** Throws if the store has no identity yet. */
8
11
  getIdentity: () => MeshStoreIdentity;
12
+ /** The issuer this instance mints under, picked out of the store's identity: its user principal or its machine. */
13
+ issuerOf: (identity: Readonly<MeshStoreIdentity>) => IdentityPort;
9
14
  /** This device's own id (hex). */
10
15
  getPeerId: () => string;
11
16
  onError: (error: Error) => void;
@@ -16,19 +21,22 @@ export declare class MembershipProofs {
16
21
  private minting;
17
22
  private timer;
18
23
  constructor(deps: Readonly<MembershipProofsDeps>);
19
- /** The agent/self advert field carrying this device's current proof, empty until the first has been minted. */
20
- advertField(): {
21
- membership?: string;
22
- };
24
+ /** This device's current proof, undefined until the first has been minted. */
25
+ current(): string | undefined;
23
26
  /** Mints a proof now and again on an interval. A failed mint is reported and retried on the next tick; the previous proof stays until it lapses. */
24
27
  start(): void;
25
28
  stop(): void;
26
- /** Whether a gossiped proof shows that principalHex vouches for deviceHex, as this node sees it: its own identity, clock and revocation view. */
29
+ /** Whether a gossiped proof shows that issuerHex vouches for deviceHex, as this node sees it: its own identity, clock and revocation view. */
27
30
  verify(claim: Readonly<{
28
31
  proof: string;
29
32
  deviceHex: string;
30
- principalHex: string;
33
+ issuerHex: string;
31
34
  }>): Promise<MembershipProofVerdict>;
35
+ /** Which issuer a gossiped proof shows vouching for deviceHex, as this node sees it. */
36
+ identify(claim: Readonly<{
37
+ proof: string;
38
+ deviceHex: string;
39
+ }>): Promise<IdentifiedMembershipProof>;
32
40
  private refresh;
33
41
  }
34
42
  //# sourceMappingURL=membership-proofs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"membership-proofs.d.ts","sourceRoot":"","sources":["../../src/core/membership-proofs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,iBAAiB,CAAC;IACrC,kCAAkC;IAClC,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAA6C;gBAE7B,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IAEjE,+GAA+G;IAC/G,WAAW,IAAI;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAItC,oJAAoJ;IACpJ,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IAKZ,iJAAiJ;IAC3I,MAAM,CACV,KAAK,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,GAC1E,OAAO,CAAC,sBAAsB,CAAC;IAYlC,OAAO,CAAC,OAAO;CAuBhB"}
1
+ {"version":3,"file":"membership-proofs.d.ts","sourceRoot":"","sources":["../../src/core/membership-proofs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAKL,KAAK,yBAAyB,EAC9B,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAI/B,qFAAqF;AACrF,eAAO,MAAM,yBAAyB,QACiB,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,iBAAiB,CAAC;IACrC,mHAAmH;IACnH,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,KAAK,YAAY,CAAC;IAClE,kCAAkC;IAClC,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAA6C;gBAE7B,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IAEjE,8EAA8E;IAC9E,OAAO,IAAI,MAAM,GAAG,SAAS;IAI7B,oJAAoJ;IACpJ,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IAKZ,8IAA8I;IACxI,MAAM,CACV,KAAK,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GACvE,OAAO,CAAC,sBAAsB,CAAC;IAYlC,wFAAwF;IAClF,QAAQ,CACZ,KAAK,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,GACpD,OAAO,CAAC,yBAAyB,CAAC;IAWrC,OAAO,CAAC,OAAO;CAuBhB"}