wire-mesh-core 1.59.0 → 2.0.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 (93) hide show
  1. package/README.md +1 -0
  2. package/dist/adapters/frame-codec.d.cts +1 -1
  3. package/dist/adapters/frame-codec.d.mts +1 -1
  4. package/dist/adapters/node-identity.d.cts +2 -2
  5. package/dist/adapters/node-identity.d.mts +2 -2
  6. package/dist/adapters/tcp-transport.d.cts +1 -1
  7. package/dist/adapters/tcp-transport.d.mts +1 -1
  8. package/dist/adapters/threshold-identity.d.cts +2 -2
  9. package/dist/adapters/threshold-identity.d.mts +2 -2
  10. package/dist/adapters/threshold-wasm.d.cts +1 -1
  11. package/dist/adapters/threshold-wasm.d.mts +1 -1
  12. package/dist/adapters/tls-transport.d.cts +1 -1
  13. package/dist/adapters/tls-transport.d.mts +1 -1
  14. package/dist/domain/bulk.d.cts +2 -2
  15. package/dist/domain/bulk.d.mts +2 -2
  16. package/dist/domain/capability-grant.d.cts +3 -3
  17. package/dist/domain/capability-grant.d.mts +3 -3
  18. package/dist/domain/capability-request.d.cts +2 -2
  19. package/dist/domain/capability-request.d.mts +2 -2
  20. package/dist/domain/data-sync.d.cts +2 -2
  21. package/dist/domain/data-sync.d.mts +2 -2
  22. package/dist/domain/device-id.d.cts +1 -1
  23. package/dist/domain/device-id.d.mts +1 -1
  24. package/dist/domain/direct-manage-request.d.cts +2 -2
  25. package/dist/domain/direct-manage-request.d.mts +2 -2
  26. package/dist/domain/gossip-expansion.d.cts +1 -1
  27. package/dist/domain/gossip-expansion.d.mts +1 -1
  28. package/dist/domain/gossip-extensions.cjs +1 -1
  29. package/dist/domain/gossip-extensions.mjs +1 -1
  30. package/dist/domain/grant-candidates.d.cts +1 -1
  31. package/dist/domain/grant-candidates.d.mts +1 -1
  32. package/dist/domain/handshake.d.cts +1 -1
  33. package/dist/domain/handshake.d.mts +1 -1
  34. package/dist/domain/mesh-session.cjs +13 -10
  35. package/dist/domain/mesh-session.d.cts +3 -3
  36. package/dist/domain/mesh-session.d.mts +3 -3
  37. package/dist/domain/mesh-session.mjs +13 -10
  38. package/dist/domain/notice-board.d.cts +3 -3
  39. package/dist/domain/notice-board.d.mts +3 -3
  40. package/dist/domain/path-trace.d.cts +1 -1
  41. package/dist/domain/path-trace.d.mts +1 -1
  42. package/dist/domain/peer-advert.cjs +61 -0
  43. package/dist/domain/peer-advert.d.cts +45 -0
  44. package/dist/domain/peer-advert.d.mts +45 -0
  45. package/dist/domain/peer-advert.mjs +55 -0
  46. package/dist/domain/relay-hub.cjs +22 -2
  47. package/dist/domain/relay-hub.d.cts +6 -3
  48. package/dist/domain/relay-hub.d.mts +6 -3
  49. package/dist/domain/relay-hub.mjs +22 -2
  50. package/dist/domain/relay-use-gate.d.cts +2 -2
  51. package/dist/domain/relay-use-gate.d.mts +2 -2
  52. package/dist/domain/revocation-view.d.cts +2 -2
  53. package/dist/domain/revocation-view.d.mts +2 -2
  54. package/dist/domain/room-rekey.d.cts +3 -3
  55. package/dist/domain/room-rekey.d.mts +3 -3
  56. package/dist/domain/room-token-verification.d.cts +1 -1
  57. package/dist/domain/room-token-verification.d.mts +1 -1
  58. package/dist/domain/room.d.cts +4 -4
  59. package/dist/domain/room.d.mts +4 -4
  60. package/dist/domain/shard-manifest.d.cts +1 -1
  61. package/dist/domain/shard-manifest.d.mts +1 -1
  62. package/dist/domain/tokens.d.cts +1 -1
  63. package/dist/domain/tokens.d.mts +1 -1
  64. package/dist/domain/topology-snapshot.d.cts +1 -1
  65. package/dist/domain/topology-snapshot.d.mts +1 -1
  66. package/dist/domain/topology.cjs +1 -1
  67. package/dist/domain/topology.d.cts +1 -1
  68. package/dist/domain/topology.d.mts +1 -1
  69. package/dist/domain/topology.mjs +1 -1
  70. package/dist/domain/webrtc-signaling.d.cts +2 -2
  71. package/dist/domain/webrtc-signaling.d.mts +2 -2
  72. package/dist/generated/protocol.cjs +3 -1
  73. package/dist/generated/protocol.d.cts +1 -1
  74. package/dist/generated/protocol.d.mts +1 -1
  75. package/dist/generated/protocol.mjs +3 -1
  76. package/dist/{gossip-extensions-Cdy5mQhY.mjs → gossip-extensions-BNiI44sU.mjs} +5 -2
  77. package/dist/{gossip-extensions-Dr_T7hk_.cjs → gossip-extensions-DRVTHKnR.cjs} +5 -2
  78. package/dist/{identity-BRlM-Krs.d.cts → identity-DImeXZcq.d.cts} +1 -1
  79. package/dist/{identity-DiuayfEd.d.mts → identity-Dc4xmwp3.d.mts} +1 -1
  80. package/dist/ports/identity.d.cts +1 -1
  81. package/dist/ports/identity.d.mts +1 -1
  82. package/dist/ports/transport.d.cts +1 -1
  83. package/dist/ports/transport.d.mts +1 -1
  84. package/dist/{protocol-BnbpCk0Z.d.cts → protocol-Bi2fzBCc.d.cts} +20 -0
  85. package/dist/{protocol-BnbpCk0Z.d.mts → protocol-Bi2fzBCc.d.mts} +20 -0
  86. package/dist/{room-token-verification-NjaqYY4k.d.mts → room-token-verification-CDXkm_Tg.d.cts} +2 -2
  87. package/dist/{room-token-verification-D9ogJTO3.d.cts → room-token-verification-CnHNaA8J.d.mts} +2 -2
  88. package/dist/{tokens-DtjdYS_a.d.mts → tokens-0r9ipK1r.d.mts} +2 -2
  89. package/dist/{tokens-BL2BzOmN.d.cts → tokens-BDVxEvl2.d.cts} +2 -2
  90. package/dist/{transport-BH8kGWXt.d.mts → transport-Cq_odHy3.d.cts} +1 -1
  91. package/dist/{transport-Ca_ZyyZe.d.cts → transport-GcJ4LYVE.d.mts} +1 -1
  92. package/package.json +5 -1
  93. package/wasm-dist/wire_mesh_threshold_wasm_bg.wasm +0 -0
@@ -0,0 +1,45 @@
1
+ import { B as IdentityKey, D as DeviceId, et as PeerAdvert } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
3
+ //#region src/domain/peer-advert.d.ts
4
+ /** peer-advert's own signature field, the one entry the signature does not cover, named once here so the signing input and the reserved-extension-key guard cannot drift apart. */
5
+ export declare const PEER_ADVERT_SIGNATURE_KEY = "signature";
6
+ /** peer-advert's own self-certifying key field. Covered by the signature like every other entry, so an advert cannot be re-keyed after signing. */
7
+ export declare const PEER_ADVERT_IDENTITY_KEY_KEY = "identity-key";
8
+ /**
9
+ * The domain-separation prefix every peer-advert signature is computed over, per spec/transport.cddl: the ASCII string "wire-mesh/peer-advert/v1" followed by one zero byte.
10
+ *
11
+ * Deliberately not tokens.cddl's COSE Sig_structure, which every other signature in this codebase reuses via sig1ToBeSigned. An advert's extension tail is an open `* tstr => any` map, so an attacker can craft an advert whose canonical encoding also satisfies token-claims; a Sig_structure over that encoding would then be replayable as a capability token's own signed payload. This prefix makes the two byte strings disjoint by construction, since a Sig_structure always begins with the CBOR head of a 4-element array (0x84) rather than the ASCII "w" this one starts with.
12
+ */
13
+ export declare const PEER_ADVERT_SIGNING_CONTEXT: Uint8Array<ArrayBuffer>;
14
+ /**
15
+ * Everything a peer-advert's signature covers: the whole advert apart from `signature` itself. Spelled out as its own interface rather than derived with `Omit<PeerAdvert, "signature">`, which collapses to the bare index signature because peer-advert's open extension tail makes `keyof PeerAdvert` a plain `string`.
16
+ *
17
+ * A full PeerAdvert satisfies this structurally, which is what lets peerAdvertSigningInput take either an advert being signed or one being verified without a second overload.
18
+ */
19
+ export interface UnsignedPeerAdvert {
20
+ readonly device: DeviceId;
21
+ readonly addresses: string[];
22
+ readonly "snapshot-seconds": number;
23
+ readonly "identity-key": IdentityKey;
24
+ /** peer-advert's own open extension tail (spec/CONVENTIONS.md's gossip-extension-namespacing convention). Every key here is signed over too, so nothing in the tail can be added, altered, or dropped without invalidating the advert. */
25
+ readonly [key: string]: unknown;
26
+ }
27
+ /** The crypto primitives verification needs, and nothing else: an advert is self-certifying, so a verifier never consults its own device-id or private key to check one. Narrowed from IdentityPort so a relay hub, which has no reason to hold a signing identity, can be given a verifier without one. */
28
+ export type PeerAdvertVerifier = Pick<IdentityPort, "verify" | "deriveDeviceId">;
29
+ /** The signing half's own dependency: a node only ever signs adverts naming itself, which is exactly why a gateway forwards its local peers' adverts verbatim instead of re-signing them. */
30
+ export type PeerAdvertSigner = Pick<IdentityPort, "sign">;
31
+ /**
32
+ * The exact bytes a peer-advert's signature is computed over: PEER_ADVERT_SIGNING_CONTEXT followed by the canonical CDE encoding of the advert with its `signature` entry removed and nothing else changed.
33
+ *
34
+ * Takes the advert rather than a pre-stripped copy so a verifier derives the input from precisely the bytes it received, leaving no room for the signed and checked views of an advert to disagree.
35
+ */
36
+ export declare function peerAdvertSigningInput(advert: Readonly<UnsignedPeerAdvert>): Uint8Array<ArrayBuffer>;
37
+ /** Signs an advert's content into the complete peer-advert that goes on the wire. The caller supplies `device` and `identity-key` itself rather than having them read off the signer, since the signer port carries only `sign`: a node building its own self-advert already holds both, and nothing else may legitimately sign an advert at all. */
38
+ export declare function signPeerAdvert(identity: Readonly<PeerAdvertSigner>, advert: Readonly<UnsignedPeerAdvert>): Promise<PeerAdvert>;
39
+ /**
40
+ * Both validity checks spec/transport.cddl states for a gossiped advert, in the order a verifier must apply them: the embedded identity-key self-certifies (sha256 of its public-key equals the advert's own `device`), and the signature verifies under that same key over peerAdvertSigningInput's bytes.
41
+ *
42
+ * Returns a verdict for every input, including a hostile one, and never throws. An advert's `alg` and `public-key` are attacker-chosen (identity-key constrains them to an int and a bstr, nothing more), so importing the key can legitimately fail for an algorithm no verifier implements or for key bytes of the wrong length. An unverifiable advert is a refused advert, exactly as if its signature had simply been wrong, which is why the import failure is turned into `false` here rather than propagated to a caller iterating a frame's other adverts.
43
+ */
44
+ export declare function verifyPeerAdvert(identity: Readonly<PeerAdvertVerifier>, advert: Readonly<PeerAdvert>): Promise<boolean>;
45
+ //#endregion
@@ -0,0 +1,45 @@
1
+ import { B as IdentityKey, D as DeviceId, et as PeerAdvert } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
3
+ //#region src/domain/peer-advert.d.ts
4
+ /** peer-advert's own signature field, the one entry the signature does not cover, named once here so the signing input and the reserved-extension-key guard cannot drift apart. */
5
+ export declare const PEER_ADVERT_SIGNATURE_KEY = "signature";
6
+ /** peer-advert's own self-certifying key field. Covered by the signature like every other entry, so an advert cannot be re-keyed after signing. */
7
+ export declare const PEER_ADVERT_IDENTITY_KEY_KEY = "identity-key";
8
+ /**
9
+ * The domain-separation prefix every peer-advert signature is computed over, per spec/transport.cddl: the ASCII string "wire-mesh/peer-advert/v1" followed by one zero byte.
10
+ *
11
+ * Deliberately not tokens.cddl's COSE Sig_structure, which every other signature in this codebase reuses via sig1ToBeSigned. An advert's extension tail is an open `* tstr => any` map, so an attacker can craft an advert whose canonical encoding also satisfies token-claims; a Sig_structure over that encoding would then be replayable as a capability token's own signed payload. This prefix makes the two byte strings disjoint by construction, since a Sig_structure always begins with the CBOR head of a 4-element array (0x84) rather than the ASCII "w" this one starts with.
12
+ */
13
+ export declare const PEER_ADVERT_SIGNING_CONTEXT: Uint8Array<ArrayBuffer>;
14
+ /**
15
+ * Everything a peer-advert's signature covers: the whole advert apart from `signature` itself. Spelled out as its own interface rather than derived with `Omit<PeerAdvert, "signature">`, which collapses to the bare index signature because peer-advert's open extension tail makes `keyof PeerAdvert` a plain `string`.
16
+ *
17
+ * A full PeerAdvert satisfies this structurally, which is what lets peerAdvertSigningInput take either an advert being signed or one being verified without a second overload.
18
+ */
19
+ export interface UnsignedPeerAdvert {
20
+ readonly device: DeviceId;
21
+ readonly addresses: string[];
22
+ readonly "snapshot-seconds": number;
23
+ readonly "identity-key": IdentityKey;
24
+ /** peer-advert's own open extension tail (spec/CONVENTIONS.md's gossip-extension-namespacing convention). Every key here is signed over too, so nothing in the tail can be added, altered, or dropped without invalidating the advert. */
25
+ readonly [key: string]: unknown;
26
+ }
27
+ /** The crypto primitives verification needs, and nothing else: an advert is self-certifying, so a verifier never consults its own device-id or private key to check one. Narrowed from IdentityPort so a relay hub, which has no reason to hold a signing identity, can be given a verifier without one. */
28
+ export type PeerAdvertVerifier = Pick<IdentityPort, "verify" | "deriveDeviceId">;
29
+ /** The signing half's own dependency: a node only ever signs adverts naming itself, which is exactly why a gateway forwards its local peers' adverts verbatim instead of re-signing them. */
30
+ export type PeerAdvertSigner = Pick<IdentityPort, "sign">;
31
+ /**
32
+ * The exact bytes a peer-advert's signature is computed over: PEER_ADVERT_SIGNING_CONTEXT followed by the canonical CDE encoding of the advert with its `signature` entry removed and nothing else changed.
33
+ *
34
+ * Takes the advert rather than a pre-stripped copy so a verifier derives the input from precisely the bytes it received, leaving no room for the signed and checked views of an advert to disagree.
35
+ */
36
+ export declare function peerAdvertSigningInput(advert: Readonly<UnsignedPeerAdvert>): Uint8Array<ArrayBuffer>;
37
+ /** Signs an advert's content into the complete peer-advert that goes on the wire. The caller supplies `device` and `identity-key` itself rather than having them read off the signer, since the signer port carries only `sign`: a node building its own self-advert already holds both, and nothing else may legitimately sign an advert at all. */
38
+ export declare function signPeerAdvert(identity: Readonly<PeerAdvertSigner>, advert: Readonly<UnsignedPeerAdvert>): Promise<PeerAdvert>;
39
+ /**
40
+ * Both validity checks spec/transport.cddl states for a gossiped advert, in the order a verifier must apply them: the embedded identity-key self-certifies (sha256 of its public-key equals the advert's own `device`), and the signature verifies under that same key over peerAdvertSigningInput's bytes.
41
+ *
42
+ * Returns a verdict for every input, including a hostile one, and never throws. An advert's `alg` and `public-key` are attacker-chosen (identity-key constrains them to an int and a bstr, nothing more), so importing the key can legitimately fail for an algorithm no verifier implements or for key bytes of the wrong length. An unverifiable advert is a refused advert, exactly as if its signature had simply been wrong, which is why the import failure is turned into `false` here rather than propagated to a caller iterating a frame's other adverts.
43
+ */
44
+ export declare function verifyPeerAdvert(identity: Readonly<PeerAdvertVerifier>, advert: Readonly<PeerAdvert>): Promise<boolean>;
45
+ //#endregion
@@ -0,0 +1,55 @@
1
+ import { t as bytesEqual } from "../token-scope-Z4bmci4M.mjs";
2
+ import { cdeEncodeOptions, encode } from "cbor2";
3
+ //#region src/domain/peer-advert.ts
4
+ /** peer-advert's own signature field, the one entry the signature does not cover, named once here so the signing input and the reserved-extension-key guard cannot drift apart. */
5
+ const PEER_ADVERT_SIGNATURE_KEY = "signature";
6
+ /** peer-advert's own self-certifying key field. Covered by the signature like every other entry, so an advert cannot be re-keyed after signing. */
7
+ const PEER_ADVERT_IDENTITY_KEY_KEY = "identity-key";
8
+ /**
9
+ * The domain-separation prefix every peer-advert signature is computed over, per spec/transport.cddl: the ASCII string "wire-mesh/peer-advert/v1" followed by one zero byte.
10
+ *
11
+ * Deliberately not tokens.cddl's COSE Sig_structure, which every other signature in this codebase reuses via sig1ToBeSigned. An advert's extension tail is an open `* tstr => any` map, so an attacker can craft an advert whose canonical encoding also satisfies token-claims; a Sig_structure over that encoding would then be replayable as a capability token's own signed payload. This prefix makes the two byte strings disjoint by construction, since a Sig_structure always begins with the CBOR head of a 4-element array (0x84) rather than the ASCII "w" this one starts with.
12
+ */
13
+ const PEER_ADVERT_SIGNING_CONTEXT = new TextEncoder().encode("wire-mesh/peer-advert/v1\0");
14
+ /**
15
+ * The exact bytes a peer-advert's signature is computed over: PEER_ADVERT_SIGNING_CONTEXT followed by the canonical CDE encoding of the advert with its `signature` entry removed and nothing else changed.
16
+ *
17
+ * Takes the advert rather than a pre-stripped copy so a verifier derives the input from precisely the bytes it received, leaving no room for the signed and checked views of an advert to disagree.
18
+ */
19
+ function peerAdvertSigningInput(advert) {
20
+ const content = {};
21
+ for (const [key, value] of Object.entries(advert)) {
22
+ if (key === "signature") continue;
23
+ content[key] = value;
24
+ }
25
+ const encoded = encode(content, cdeEncodeOptions);
26
+ const input = new Uint8Array(PEER_ADVERT_SIGNING_CONTEXT.length + encoded.length);
27
+ input.set(PEER_ADVERT_SIGNING_CONTEXT, 0);
28
+ input.set(encoded, PEER_ADVERT_SIGNING_CONTEXT.length);
29
+ return input;
30
+ }
31
+ /** Signs an advert's content into the complete peer-advert that goes on the wire. The caller supplies `device` and `identity-key` itself rather than having them read off the signer, since the signer port carries only `sign`: a node building its own self-advert already holds both, and nothing else may legitimately sign an advert at all. */
32
+ async function signPeerAdvert(identity, advert) {
33
+ const signature = await identity.sign(peerAdvertSigningInput(advert));
34
+ return {
35
+ ...advert,
36
+ [PEER_ADVERT_SIGNATURE_KEY]: signature
37
+ };
38
+ }
39
+ /**
40
+ * Both validity checks spec/transport.cddl states for a gossiped advert, in the order a verifier must apply them: the embedded identity-key self-certifies (sha256 of its public-key equals the advert's own `device`), and the signature verifies under that same key over peerAdvertSigningInput's bytes.
41
+ *
42
+ * Returns a verdict for every input, including a hostile one, and never throws. An advert's `alg` and `public-key` are attacker-chosen (identity-key constrains them to an int and a bstr, nothing more), so importing the key can legitimately fail for an algorithm no verifier implements or for key bytes of the wrong length. An unverifiable advert is a refused advert, exactly as if its signature had simply been wrong, which is why the import failure is turned into `false` here rather than propagated to a caller iterating a frame's other adverts.
43
+ */
44
+ async function verifyPeerAdvert(identity, advert) {
45
+ const key = advert[PEER_ADVERT_IDENTITY_KEY_KEY];
46
+ try {
47
+ const derived = await identity.deriveDeviceId(key["public-key"]);
48
+ if (!bytesEqual(derived, advert.device)) return false;
49
+ return await identity.verify(key, peerAdvertSigningInput(advert), advert[PEER_ADVERT_SIGNATURE_KEY]);
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
54
+ //#endregion
55
+ export { PEER_ADVERT_IDENTITY_KEY_KEY, PEER_ADVERT_SIGNATURE_KEY, PEER_ADVERT_SIGNING_CONTEXT, peerAdvertSigningInput, signPeerAdvert, verifyPeerAdvert };
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_domain_peer_advert = require("./peer-advert.cjs");
2
3
  //#region src/domain/relay-hub.ts
3
4
  const HEX_RADIX = 16;
4
5
  const HEX_DIGITS_PER_BYTE = 2;
@@ -13,7 +14,17 @@ function deviceFromKey(key) {
13
14
  for (let i = 0; i < device.length; i++) device[i] = Number.parseInt(key.slice(i * HEX_DIGITS_PER_BYTE, (i + 1) * HEX_DIGITS_PER_BYTE), HEX_RADIX);
14
15
  return device;
15
16
  }
16
- function createRelayHub(options = {}) {
17
+ /**
18
+ * Whether an already-verified advert may take over the registration a device currently holds (wire-mesh#225).
19
+ *
20
+ * Strictly newer when it arrives on a different connection from the one holding the registration, so replaying a victim's genuine advert, which carries the victim's own `snapshot-seconds` unchanged since altering it would break the signature, can never steal its route. Newer-or-equal from the connection that already holds it, so an unchanged heartbeat from the rightful owner still refreshes the addresses and extensions the hub replays in catch-up frames.
21
+ */
22
+ function supersedes(registered, advert, connection) {
23
+ const incoming = advert["snapshot-seconds"];
24
+ const held = registered.advert["snapshot-seconds"];
25
+ return registered.connection === connection ? incoming >= held : incoming > held;
26
+ }
27
+ function createRelayHub(options) {
17
28
  const connections = /* @__PURE__ */ new Set();
18
29
  const devices = /* @__PURE__ */ new Map();
19
30
  const connectionDevice = /* @__PURE__ */ new Map();
@@ -101,25 +112,34 @@ function createRelayHub(options = {}) {
101
112
  async function handleFrame(connection, frame) {
102
113
  if (frame.type === "gossip") {
103
114
  const supplanted = /* @__PURE__ */ new Set();
115
+ const accepted = [];
104
116
  for (const advert of frame.peers) {
117
+ if (!await require_domain_peer_advert.verifyPeerAdvert(options.identity, advert)) continue;
105
118
  const key = deviceKey(advert.device);
106
119
  const previous = devices.get(key);
120
+ if (previous !== void 0 && !supersedes(previous, advert, connection)) continue;
107
121
  if (previous !== void 0 && previous.connection !== connection) supplanted.add(previous.connection);
108
122
  devices.set(key, {
109
123
  connection,
110
124
  advert
111
125
  });
126
+ accepted.push(advert);
112
127
  }
128
+ if (accepted.length === 0) return;
113
129
  for (const registration of devices.values()) if (registration.connection === connection) {
114
130
  connectionDevice.set(connection, registration.advert.device);
115
131
  break;
116
132
  }
117
133
  stateChanged(connection);
118
134
  for (const other of supplanted) stateChanged(other);
135
+ const forwarded = {
136
+ type: "gossip",
137
+ peers: accepted
138
+ };
119
139
  for (const other of connections) {
120
140
  if (other === connection) continue;
121
141
  try {
122
- await other.send(frame);
142
+ await other.send(forwarded);
123
143
  } catch {}
124
144
  }
125
145
  const catchUp = [];
@@ -1,5 +1,6 @@
1
- import { D as DeviceId, N as Frame, et as PeerAdvert } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as Connection } from "../transport-Ca_ZyyZe.cjs";
1
+ import { D as DeviceId, N as Frame, et as PeerAdvert } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as Connection } from "../transport-Cq_odHy3.cjs";
3
+ import { PeerAdvertVerifier } from "./peer-advert.cjs";
3
4
  //#region src/domain/relay-hub.d.ts
4
5
  /**
5
6
  * One connection's relay state in serialisable form: everything a hub that never saw the connection register needs, alongside the still-open connection itself, to answer its next frame exactly as the hub that established the state would have. Only device-ids and adverts appear, never a Connection, so a host can persist this wherever it keeps per-connection state.
@@ -20,6 +21,8 @@ export interface RelayConnectionRestore {
20
21
  readonly state: RelayConnectionState;
21
22
  }
22
23
  export interface RelayHubOptions {
24
+ /** The crypto primitives this hub verifies every gossiped advert with (wire-mesh#225). Narrowed to verify/deriveDeviceId rather than a full IdentityPort because an advert is self-certifying: a hub checks one against the key the advert itself carries, and never needs a signing identity of its own to do it. */
25
+ readonly identity: Readonly<PeerAdvertVerifier>;
23
26
  /** Called whenever the value exportConnection returns for a connection changes: it gossiped an advert, gained or lost a pairing, or had one of its adverts taken over by another connection. A host whose own instance can be torn down while its connections stay open (the Cloudflare hub's hibernating Durable Object) reads the new value back with exportConnection, persists it against the connection, and hands it to a later instance through restoreConnections. */
24
27
  readonly onConnectionStateChanged?: (connection: Readonly<Connection>) => void;
25
28
  }
@@ -39,5 +42,5 @@ export interface RelayHub {
39
42
  /** Drops all registry and pairing state -- used by tests and by transport teardown. */
40
43
  stop: () => void;
41
44
  }
42
- export declare function createRelayHub(options?: RelayHubOptions): RelayHub;
45
+ export declare function createRelayHub(options: Readonly<RelayHubOptions>): RelayHub;
43
46
  //#endregion
@@ -1,5 +1,6 @@
1
- import { D as DeviceId, N as Frame, et as PeerAdvert } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as Connection } from "../transport-BH8kGWXt.mjs";
1
+ import { D as DeviceId, N as Frame, et as PeerAdvert } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as Connection } from "../transport-GcJ4LYVE.mjs";
3
+ import { PeerAdvertVerifier } from "./peer-advert.mjs";
3
4
  //#region src/domain/relay-hub.d.ts
4
5
  /**
5
6
  * One connection's relay state in serialisable form: everything a hub that never saw the connection register needs, alongside the still-open connection itself, to answer its next frame exactly as the hub that established the state would have. Only device-ids and adverts appear, never a Connection, so a host can persist this wherever it keeps per-connection state.
@@ -20,6 +21,8 @@ export interface RelayConnectionRestore {
20
21
  readonly state: RelayConnectionState;
21
22
  }
22
23
  export interface RelayHubOptions {
24
+ /** The crypto primitives this hub verifies every gossiped advert with (wire-mesh#225). Narrowed to verify/deriveDeviceId rather than a full IdentityPort because an advert is self-certifying: a hub checks one against the key the advert itself carries, and never needs a signing identity of its own to do it. */
25
+ readonly identity: Readonly<PeerAdvertVerifier>;
23
26
  /** Called whenever the value exportConnection returns for a connection changes: it gossiped an advert, gained or lost a pairing, or had one of its adverts taken over by another connection. A host whose own instance can be torn down while its connections stay open (the Cloudflare hub's hibernating Durable Object) reads the new value back with exportConnection, persists it against the connection, and hands it to a later instance through restoreConnections. */
24
27
  readonly onConnectionStateChanged?: (connection: Readonly<Connection>) => void;
25
28
  }
@@ -39,5 +42,5 @@ export interface RelayHub {
39
42
  /** Drops all registry and pairing state -- used by tests and by transport teardown. */
40
43
  stop: () => void;
41
44
  }
42
- export declare function createRelayHub(options?: RelayHubOptions): RelayHub;
45
+ export declare function createRelayHub(options: Readonly<RelayHubOptions>): RelayHub;
43
46
  //#endregion
@@ -1,3 +1,4 @@
1
+ import { verifyPeerAdvert } from "./peer-advert.mjs";
1
2
  //#region src/domain/relay-hub.ts
2
3
  const HEX_RADIX = 16;
3
4
  const HEX_DIGITS_PER_BYTE = 2;
@@ -12,7 +13,17 @@ function deviceFromKey(key) {
12
13
  for (let i = 0; i < device.length; i++) device[i] = Number.parseInt(key.slice(i * HEX_DIGITS_PER_BYTE, (i + 1) * HEX_DIGITS_PER_BYTE), HEX_RADIX);
13
14
  return device;
14
15
  }
15
- function createRelayHub(options = {}) {
16
+ /**
17
+ * Whether an already-verified advert may take over the registration a device currently holds (wire-mesh#225).
18
+ *
19
+ * Strictly newer when it arrives on a different connection from the one holding the registration, so replaying a victim's genuine advert, which carries the victim's own `snapshot-seconds` unchanged since altering it would break the signature, can never steal its route. Newer-or-equal from the connection that already holds it, so an unchanged heartbeat from the rightful owner still refreshes the addresses and extensions the hub replays in catch-up frames.
20
+ */
21
+ function supersedes(registered, advert, connection) {
22
+ const incoming = advert["snapshot-seconds"];
23
+ const held = registered.advert["snapshot-seconds"];
24
+ return registered.connection === connection ? incoming >= held : incoming > held;
25
+ }
26
+ function createRelayHub(options) {
16
27
  const connections = /* @__PURE__ */ new Set();
17
28
  const devices = /* @__PURE__ */ new Map();
18
29
  const connectionDevice = /* @__PURE__ */ new Map();
@@ -100,25 +111,34 @@ function createRelayHub(options = {}) {
100
111
  async function handleFrame(connection, frame) {
101
112
  if (frame.type === "gossip") {
102
113
  const supplanted = /* @__PURE__ */ new Set();
114
+ const accepted = [];
103
115
  for (const advert of frame.peers) {
116
+ if (!await verifyPeerAdvert(options.identity, advert)) continue;
104
117
  const key = deviceKey(advert.device);
105
118
  const previous = devices.get(key);
119
+ if (previous !== void 0 && !supersedes(previous, advert, connection)) continue;
106
120
  if (previous !== void 0 && previous.connection !== connection) supplanted.add(previous.connection);
107
121
  devices.set(key, {
108
122
  connection,
109
123
  advert
110
124
  });
125
+ accepted.push(advert);
111
126
  }
127
+ if (accepted.length === 0) return;
112
128
  for (const registration of devices.values()) if (registration.connection === connection) {
113
129
  connectionDevice.set(connection, registration.advert.device);
114
130
  break;
115
131
  }
116
132
  stateChanged(connection);
117
133
  for (const other of supplanted) stateChanged(other);
134
+ const forwarded = {
135
+ type: "gossip",
136
+ peers: accepted
137
+ };
118
138
  for (const other of connections) {
119
139
  if (other === connection) continue;
120
140
  try {
121
- await other.send(frame);
141
+ await other.send(forwarded);
122
142
  } catch {}
123
143
  }
124
144
  const catchUp = [];
@@ -1,5 +1,5 @@
1
- import { N as Frame } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as Connection } from "../transport-Ca_ZyyZe.cjs";
1
+ import { N as Frame } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as Connection } from "../transport-Cq_odHy3.cjs";
3
3
  //#region src/domain/relay-use-gate.d.ts
4
4
  export interface RelayUseAuthorizationTracker {
5
5
  /** True once authorize() has been called for this exact connection and revoke() has not been called since. */
@@ -1,5 +1,5 @@
1
- import { N as Frame } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as Connection } from "../transport-BH8kGWXt.mjs";
1
+ import { N as Frame } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as Connection } from "../transport-GcJ4LYVE.mjs";
3
3
  //#region src/domain/relay-use-gate.d.ts
4
4
  export interface RelayUseAuthorizationTracker {
5
5
  /** True once authorize() has been called for this exact connection and revoke() has not been called since. */
@@ -1,5 +1,5 @@
1
- import { bt as RevocationEntry } from "../protocol-BnbpCk0Z.cjs";
2
- import { a as RevocationCheck, d as VerifyRevocationEntryOptions, o as RevocationEntryVerdict } from "../tokens-BL2BzOmN.cjs";
1
+ import { bt as RevocationEntry } from "../protocol-Bi2fzBCc.cjs";
2
+ import { a as RevocationCheck, d as VerifyRevocationEntryOptions, o as RevocationEntryVerdict } from "../tokens-BDVxEvl2.cjs";
3
3
  //#region src/domain/revocation-view.d.ts
4
4
  /**
5
5
  * An in-memory RevocationCheck fed by ingested revocation-announce frames. Keyed by token-id alone, per management.cddl's contract: a token-id can legitimately carry multiple recorded entries from different issuers (the token's own issuer, and/or any number of parties holding a delegated manage:revoke authorization over it) -- entriesFor returns every one of them unfiltered, since checking which actually apply (issuer-match, or a verified authorization) is verifyTokenChain's own obligation, not this store's.
@@ -1,5 +1,5 @@
1
- import { bt as RevocationEntry } from "../protocol-BnbpCk0Z.mjs";
2
- import { a as RevocationCheck, d as VerifyRevocationEntryOptions, o as RevocationEntryVerdict } from "../tokens-DtjdYS_a.mjs";
1
+ import { bt as RevocationEntry } from "../protocol-Bi2fzBCc.mjs";
2
+ import { a as RevocationCheck, d as VerifyRevocationEntryOptions, o as RevocationEntryVerdict } from "../tokens-0r9ipK1r.mjs";
3
3
  //#region src/domain/revocation-view.d.ts
4
4
  /**
5
5
  * An in-memory RevocationCheck fed by ingested revocation-announce frames. Keyed by token-id alone, per management.cddl's contract: a token-id can legitimately carry multiple recorded entries from different issuers (the token's own issuer, and/or any number of parties holding a delegated manage:revoke authorization over it) -- entriesFor returns every one of them unfiltered, since checking which actually apply (issuer-match, or a verified authorization) is verifyTokenChain's own obligation, not this store's.
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, p as CapabilityToken } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { D as DeviceId, V as ManageCommand, p as CapabilityToken } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
4
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.cjs";
5
- import { a as RevocationCheck } from "../tokens-BL2BzOmN.cjs";
5
+ import { a as RevocationCheck } from "../tokens-BDVxEvl2.cjs";
6
6
  //#region src/domain/room-rekey.d.ts
7
7
  /**
8
8
  * Builds a room.rekey command per room.cddl. The outer verb is room:member, the same "one resource, several inner verbs" pattern room.send/room.leave/room.members already establish -- not a new capability of its own.
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { D as DeviceId, V as ManageCommand, p as CapabilityToken } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
4
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.mjs";
5
- import { a as RevocationCheck } from "../tokens-DtjdYS_a.mjs";
5
+ import { a as RevocationCheck } from "../tokens-0r9ipK1r.mjs";
6
6
  //#region src/domain/room-rekey.d.ts
7
7
  /**
8
8
  * Builds a room.rekey command per room.cddl. The outer verb is room:member, the same "one resource, several inner verbs" pattern room.send/room.leave/room.members already establish -- not a new capability of its own.
@@ -1,2 +1,2 @@
1
- import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-D9ogJTO3.cjs";
1
+ import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-CDXkm_Tg.cjs";
2
2
  export { ROOM_MEMBER_CAPABILITY, RoomTokenVerdict, RoomTokenVerdictReason, VerifyRoomTokenOptions, verifyRoomToken };
@@ -1,2 +1,2 @@
1
- import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-NjaqYY4k.mjs";
1
+ import { a as verifyRoomToken, i as VerifyRoomTokenOptions, n as RoomTokenVerdict, r as RoomTokenVerdictReason, t as ROOM_MEMBER_CAPABILITY } from "../room-token-verification-CnHNaA8J.mjs";
2
2
  export { ROOM_MEMBER_CAPABILITY, RoomTokenVerdict, RoomTokenVerdictReason, VerifyRoomTokenOptions, verifyRoomToken };
@@ -1,8 +1,8 @@
1
- import { At as RoomPath, Ot as RoomNotice, kt as RoomNoticeClaims, p as CapabilityToken, q as MessageRef } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { At as RoomPath, Ot as RoomNotice, kt as RoomNoticeClaims, p as CapabilityToken, q as MessageRef } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
- import { a as RevocationCheck } from "../tokens-BL2BzOmN.cjs";
5
- import { r as RoomTokenVerdictReason } from "../room-token-verification-D9ogJTO3.cjs";
4
+ import { a as RevocationCheck } from "../tokens-BDVxEvl2.cjs";
5
+ import { r as RoomTokenVerdictReason } from "../room-token-verification-CDXkm_Tg.cjs";
6
6
  //#region src/domain/room.d.ts
7
7
  export type RoomNoticeVerdictReason = "malformed" | "wrong_room" | "bad_signature" | "wrong_poster" | "content_expired" | "key_epoch_mismatch" | RoomTokenVerdictReason;
8
8
  export type RoomNoticeVerdict = {
@@ -1,8 +1,8 @@
1
- import { At as RoomPath, Ot as RoomNotice, kt as RoomNoticeClaims, p as CapabilityToken, q as MessageRef } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { At as RoomPath, Ot as RoomNotice, kt as RoomNoticeClaims, p as CapabilityToken, q as MessageRef } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
- import { a as RevocationCheck } from "../tokens-DtjdYS_a.mjs";
5
- import { r as RoomTokenVerdictReason } from "../room-token-verification-NjaqYY4k.mjs";
4
+ import { a as RevocationCheck } from "../tokens-0r9ipK1r.mjs";
5
+ import { r as RoomTokenVerdictReason } from "../room-token-verification-CnHNaA8J.mjs";
6
6
  //#region src/domain/room.d.ts
7
7
  export type RoomNoticeVerdictReason = "malformed" | "wrong_room" | "bad_signature" | "wrong_poster" | "content_expired" | "key_epoch_mismatch" | RoomTokenVerdictReason;
8
8
  export type RoomNoticeVerdict = {
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { PresentedShard, ShardConfig } from "./erasure-coding.cjs";
3
3
  //#region src/domain/shard-manifest.d.ts
4
4
  /** The content-type a core/data entry carrying a shard manifest declares. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { PresentedShard, ShardConfig } from "./erasure-coding.mjs";
3
3
  //#region src/domain/shard-manifest.d.ts
4
4
  /** The content-type a core/data entry carrying a shard manifest declares. */
@@ -1,2 +1,2 @@
1
- import { _ as verifyRevocationEntry, a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyCapabilityToken, h as sig1ToBeSigned, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-BL2BzOmN.cjs";
1
+ import { _ as verifyRevocationEntry, a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyCapabilityToken, h as sig1ToBeSigned, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-BDVxEvl2.cjs";
2
2
  export { MintCapabilityTokenOptions, MintRefusalReason, MintRevocationEntryOptions, MintVerdict, RevocationCheck, RevocationEntryVerdict, RevocationEntryVerdictReason, TokenVerdict, TokenVerdictReason, VerifyCapabilityTokenOptions, VerifyRevocationEntryOptions, canGrant, mintCapabilityToken, mintRevocationEntry, sig1ToBeSigned, verifyCapabilityToken, verifyRevocationEntry };
@@ -1,2 +1,2 @@
1
- import { _ as verifyRevocationEntry, a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyCapabilityToken, h as sig1ToBeSigned, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-DtjdYS_a.mjs";
1
+ import { _ as verifyRevocationEntry, a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyCapabilityToken, h as sig1ToBeSigned, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-0r9ipK1r.mjs";
2
2
  export { MintCapabilityTokenOptions, MintRefusalReason, MintRevocationEntryOptions, MintVerdict, RevocationCheck, RevocationEntryVerdict, RevocationEntryVerdictReason, TokenVerdict, TokenVerdictReason, VerifyCapabilityTokenOptions, VerifyRevocationEntryOptions, canGrant, mintCapabilityToken, mintRevocationEntry, sig1ToBeSigned, verifyCapabilityToken, verifyRevocationEntry };
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, en as TopologyPeers } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, en as TopologyPeers } from "../protocol-Bi2fzBCc.cjs";
2
2
  //#region src/domain/topology-snapshot.d.ts
3
3
  export interface TopologyPeersInputs {
4
4
  /** connection?.peerDeviceId -- the transport-authenticated direct peer, when the adapter gives one. Takes priority over firstAdvertisedPeer below. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, en as TopologyPeers } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, en as TopologyPeers } from "../protocol-Bi2fzBCc.mjs";
2
2
  //#region src/domain/topology-snapshot.d.ts
3
3
  export interface TopologyPeersInputs {
4
4
  /** connection?.peerDeviceId -- the transport-authenticated direct peer, when the adapter gives one. Takes priority over firstAdvertisedPeer below. */
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_generated_protocol = require("../generated/protocol.cjs");
3
- const require_gossip_extensions = require("../gossip-extensions-Dr_T7hk_.cjs");
3
+ const require_gossip_extensions = require("../gossip-extensions-DRVTHKnR.cjs");
4
4
  //#region src/domain/topology.ts
5
5
  /**
6
6
  * Topology introspection (wire-mesh#180): assembling a connection graph out of every device's own self-reported `topology/peers` gossip extension (mesh-session.ts's buildSelfAdvert computes and injects it automatically -- nothing here writes to it), and answering the on-demand `topology.get` manage-command for a live, cache-bust read instead of a possibly-stale gossiped one. `topology:get` is deliberately ungated, per management.cddl's own comment on `topology-get` and this registry entry -- no token/scope check anywhere in this module, mirroring how core/room's room.join/room.invite are ungated in room-client.ts.
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, V as ManageCommand, en as TopologyPeers, et as PeerAdvert } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, V as ManageCommand, en as TopologyPeers, et as PeerAdvert } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { DirectoryEntry, IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
3
3
  //#region src/domain/topology.d.ts
4
4
  export declare const TOPOLOGY_GET_CAPABILITY = "topology:get";
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, V as ManageCommand, en as TopologyPeers, et as PeerAdvert } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, V as ManageCommand, en as TopologyPeers, et as PeerAdvert } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { DirectoryEntry, IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
3
3
  //#region src/domain/topology.d.ts
4
4
  export declare const TOPOLOGY_GET_CAPABILITY = "topology:get";
@@ -1,5 +1,5 @@
1
1
  import { topologyGetSchema, topologyPeersSchema } from "../generated/protocol.mjs";
2
- import { t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-Cdy5mQhY.mjs";
2
+ import { t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-BNiI44sU.mjs";
3
3
  //#region src/domain/topology.ts
4
4
  /**
5
5
  * Topology introspection (wire-mesh#180): assembling a connection graph out of every device's own self-reported `topology/peers` gossip extension (mesh-session.ts's buildSelfAdvert computes and injects it automatically -- nothing here writes to it), and answering the on-demand `topology.get` manage-command for a live, cache-bust read instead of a possibly-stale gossiped one. `topology:get` is deliberately ungated, per management.cddl's own comment on `topology-get` and this registry entry -- no token/scope check anywhere in this module, mirroring how core/room's room.join/room.invite are ungated in room-client.ts.
@@ -1,6 +1,6 @@
1
- import { Ft as SfuTrackEntry, H as ManageCommandParams, It as SfuTrackMap, V as ManageCommand, an as WebrtcOffer, f as CapabilityScope, in as WebrtcIceCandidate, rn as WebrtcAnswer, z as IceCandidateInit } from "../protocol-BnbpCk0Z.cjs";
1
+ import { Ft as SfuTrackEntry, H as ManageCommandParams, It as SfuTrackMap, V as ManageCommand, an as WebrtcOffer, f as CapabilityScope, in as WebrtcIceCandidate, rn as WebrtcAnswer, z as IceCandidateInit } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { IncomingManageRequest } from "./mesh-session.cjs";
3
- import { u as VerifyCapabilityTokenOptions } from "../tokens-BL2BzOmN.cjs";
3
+ import { u as VerifyCapabilityTokenOptions } from "../tokens-BDVxEvl2.cjs";
4
4
  //#region src/domain/webrtc-signaling.d.ts
5
5
  /** The one capability verb gating every core/webrtc message shape: an authority over this node's own signaling as a whole, not three separate resources, mirroring how core/exec's exec:pty gates all of its own inner verbs. webrtc.sfu-track-map is gated by this same verb (see spec/webrtc.cddl's own comment): an SFU able to shape a negotiation can already misreport whose media is on which mid, so a separate capability over the track-map itself would guard nothing a client doesn't already have to trust the SFU for. */
6
6
  export declare const WEBRTC_SIGNAL_VERB = "webrtc:signal";
@@ -1,6 +1,6 @@
1
- import { Ft as SfuTrackEntry, H as ManageCommandParams, It as SfuTrackMap, V as ManageCommand, an as WebrtcOffer, f as CapabilityScope, in as WebrtcIceCandidate, rn as WebrtcAnswer, z as IceCandidateInit } from "../protocol-BnbpCk0Z.mjs";
1
+ import { Ft as SfuTrackEntry, H as ManageCommandParams, It as SfuTrackMap, V as ManageCommand, an as WebrtcOffer, f as CapabilityScope, in as WebrtcIceCandidate, rn as WebrtcAnswer, z as IceCandidateInit } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { IncomingManageRequest } from "./mesh-session.mjs";
3
- import { u as VerifyCapabilityTokenOptions } from "../tokens-DtjdYS_a.mjs";
3
+ import { u as VerifyCapabilityTokenOptions } from "../tokens-0r9ipK1r.mjs";
4
4
  //#region src/domain/webrtc-signaling.d.ts
5
5
  /** The one capability verb gating every core/webrtc message shape: an authority over this node's own signaling as a whole, not three separate resources, mirroring how core/exec's exec:pty gates all of its own inner verbs. webrtc.sfu-track-map is gated by this same verb (see spec/webrtc.cddl's own comment): an SFU able to shape a negotiation can already misreport whose media is on which mid, so a separate capability over the track-map itself would guard nothing a client doesn't already have to trust the SFU for. */
6
6
  export declare const WEBRTC_SIGNAL_VERB = "webrtc:signal";
@@ -467,7 +467,9 @@ const closeFrameSchema = zod.z.lazy(() => zod.z.object({
467
467
  const peerAdvertSchema = zod.z.lazy(() => zod.z.object({
468
468
  "device": zod.z.lazy(() => deviceIdSchema),
469
469
  "addresses": zod.z.array(zod.z.string()),
470
- "snapshot-seconds": zod.z.number().int()
470
+ "snapshot-seconds": zod.z.number().int(),
471
+ "identity-key": zod.z.lazy(() => identityKeySchema),
472
+ "signature": zod.z.instanceof(Uint8Array)
471
473
  }).catchall(zod.z.unknown()));
472
474
  const gossipFrameSchema = zod.z.lazy(() => zod.z.object({
473
475
  "type": zod.z.literal("gossip"),
@@ -1,2 +1,2 @@
1
- import { $ as PathTraceOk, $n as messageRefSchema, $r as thresholdKeygenRound2Schema, $t as TopologyGet, A as DomainId, An as dataEntriesFrameSchema, Ar as roomLeaveSchema, At as RoomPath, B as IdentityKey, Bn as frameVariantSchema, Br as sessionIdSchema, Bt as StreamSession, C as CoseTokenHeaders, Cn as coreCapabilitySchema, Cr as relayOfferFrameSchema, Ct as RoomJoinOk, D as DeviceId, Dn as coseHeaderLabelSchema, Dr as roomInviteSchema, Dt as RoomMembersOk, E as DataRequestFrame, En as coseHeaderKidSchema, Er as revocationEntrySchema, Et as RoomMembers, F as GossipFrame, Fn as dmRoomPathSchema, Fr as roomNoticeSchema, Ft as SfuTrackEntry, G as ManageRequestFrame, Gn as iceCandidateInitSchema, Gr as streamEndFrameSchema, Gt as ThresholdKeygenConfirm, H as ManageCommandParams, Hn as handleClaimsSchema, Hr as sfuTrackMapSchema, Ht as ThresholdAbort, I as HandleClaims, In as domainIdSchema, Ir as roomPathSchema, It as SfuTrackMap, J as NamespacedCapability, Jn as manageCommandSchema, Jr as thresholdAbortSchema, Jt as ThresholdShareClaims, K as ManageResponseFrame, Kn as identityKeySchema, Kr as streamSessionSchema, Kt as ThresholdKeygenRound1, L as HandleRecord, Ln as execListSchema, Lr as roomReadSchema, Lt as StreamAckFrame, M as ExecSessionInfo, Mn as dataRequestFrameSchema, Mr as roomMembersOkSchema, Mt as RoomRekey, N as Frame, Nn as deviceIdHexSchema, Nr as roomMembersSchema, Nt as RoomSend, O as DeviceIdHex, On as coseSign1Schema, Or as roomJoinOkSchema, Ot as RoomNotice, P as FrameVariant, Pn as deviceIdSchema, Pr as roomNoticeClaimsSchema, Pt as SessionId, Q as PathTrace, Qn as manageResponseFrameSchema, Qr as thresholdKeygenRound1Schema, Qt as TokenClaims, R as HandshakeFrame, Rn as execSessionInfoSchema, Rr as roomRekeySchema, Rt as StreamDataFrame, S as CoseSign1, Sn as coordinatorFrameSchema, Sr as relayInboundFrameSchema, St as RoomJoin, T as DataHaveFrame, Tn as coseHeaderAlgSchema, Tr as revocationClaimsSchema, Tt as RoomMember, U as ManageError, Un as handleRecordSchema, Ur as streamAckFrameSchema, Ut as ThresholdCommit, V as ManageCommand, Vn as gossipFrameSchema, Vr as sfuTrackEntrySchema, Vt as SyncPunchFrame, W as ManageOk, Wn as handshakeFrameSchema, Wr as streamDataFrameSchema, Wt as ThresholdCommitment, X as ObservedAddressFrame, Xn as manageOkSchema, Xr as thresholdCommitmentSchema, Xt as ThresholdSign, Y as NamespacedDomainId, Yn as manageErrorSchema, Yr as thresholdCommitSchema, Yt as ThresholdShareEnvelope, Z as OwnerNamedRoomPath, Zn as manageRequestFrameSchema, Zr as thresholdKeygenConfirmSchema, Zt as ThresholdSubject, _ as CoreCapability, _n as capabilityRequestSchema, _r as ptyResizeSchema, _t as RelayOfferFrame, a as BulkOpen, ai as topologyGetSchema, an as WebrtcOffer, ar as pathTraceSchema, at as PrivateUseDomainId, b as CoseHeaderKid, bn as capabilityVerbSchema, br as relayConnectFrameSchema, bt as RevocationEntry, c as CandidatesFrame, ci as versionGetSchema, cn as bulkCancelSchema, cr as pingFrameSchema, ct as ProcSpawn, d as CapabilityRequest, di as webrtcOfferSchema, dn as bulkOpenSchema, dr as privateUseDomainIdSchema, dt as PtyResize, ei as thresholdShareClaimsSchema, en as TopologyPeers, er as namespacedCapabilitySchema, et as PeerAdvert, f as CapabilityScope, fi as wireCandidateSchema, fn as bulkResumeSchema, fr as procKillSchema, ft as PtySpawn, g as CoordinatorFrame, gn as capabilityGrantSchema, gr as ptyKillSchema, gt as RelayInboundFrame, h as CloseFrame, hn as capabilityGrantOkSchema, hr as protocolVersionSchema, ht as RelayDataFrame, i as BulkEndFrame, ii as tokenClaimsSchema, in as WebrtcIceCandidate, ir as pathTraceOkSchema, it as PrivateUseCapability, j as ExecList, jn as dataHaveFrameSchema, jr as roomMemberSchema, jt as RoomRead, k as DmRoomPath, kn as coseTokenHeadersSchema, kr as roomJoinSchema, kt as RoomNoticeClaims, l as CapabilityGrant, li as webrtcAnswerSchema, ln as bulkDataFrameSchema, lr as pongFrameSchema, lt as ProtocolVersion, m as CapabilityVerb, mn as candidatesFrameSchema, mr as procSpawnSchema, mt as RelayConnectFrame, n as BulkCancel, ni as thresholdSignSchema, nn as VersionGet, nr as observedAddressFrameSchema, nt as PingFrame, o as BulkResume, oi as topologyPeersSchema, on as WireCandidate, or as peerAdvertSchema, ot as ProcKill, p as CapabilityToken, pn as candidateKindSchema, pr as procSignalSchema, pt as PtyWrite, q as MessageRef, qn as manageCommandParamsSchema, qr as syncPunchFrameSchema, qt as ThresholdKeygenRound2, r as BulkDataFrame, ri as thresholdSubjectSchema, rn as WebrtcAnswer, rr as ownerNamedRoomPathSchema, rt as PongFrame, s as CandidateKind, si as transferIdSchema, sn as bulkAckFrameSchema, sr as peerIdentitySchema, st as ProcSignal, t as BulkAckFrame, ti as thresholdShareEnvelopeSchema, tn as TransferId, tr as namespacedDomainIdSchema, tt as PeerIdentity, u as CapabilityGrantOk, ui as webrtcIceCandidateSchema, un as bulkEndFrameSchema, ur as privateUseCapabilitySchema, ut as PtyKill, v as CoreDomainName, vn as capabilityScopeSchema, vr as ptySpawnSchema, vt as RevocationAnnounceFrame, w as DataEntriesFrame, wn as coreDomainNameSchema, wr as revocationAnnounceFrameSchema, wt as RoomLeave, x as CoseHeaderLabel, xn as closeFrameSchema, xr as relayDataFrameSchema, xt as RoomInvite, y as CoseHeaderAlg, yn as capabilityTokenSchema, yr as ptyWriteSchema, yt as RevocationClaims, z as IceCandidateInit, zn as frameSchema, zr as roomSendSchema, zt as StreamEndFrame } from "../protocol-BnbpCk0Z.cjs";
1
+ import { $ as PathTraceOk, $n as messageRefSchema, $r as thresholdKeygenRound2Schema, $t as TopologyGet, A as DomainId, An as dataEntriesFrameSchema, Ar as roomLeaveSchema, At as RoomPath, B as IdentityKey, Bn as frameVariantSchema, Br as sessionIdSchema, Bt as StreamSession, C as CoseTokenHeaders, Cn as coreCapabilitySchema, Cr as relayOfferFrameSchema, Ct as RoomJoinOk, D as DeviceId, Dn as coseHeaderLabelSchema, Dr as roomInviteSchema, Dt as RoomMembersOk, E as DataRequestFrame, En as coseHeaderKidSchema, Er as revocationEntrySchema, Et as RoomMembers, F as GossipFrame, Fn as dmRoomPathSchema, Fr as roomNoticeSchema, Ft as SfuTrackEntry, G as ManageRequestFrame, Gn as iceCandidateInitSchema, Gr as streamEndFrameSchema, Gt as ThresholdKeygenConfirm, H as ManageCommandParams, Hn as handleClaimsSchema, Hr as sfuTrackMapSchema, Ht as ThresholdAbort, I as HandleClaims, In as domainIdSchema, Ir as roomPathSchema, It as SfuTrackMap, J as NamespacedCapability, Jn as manageCommandSchema, Jr as thresholdAbortSchema, Jt as ThresholdShareClaims, K as ManageResponseFrame, Kn as identityKeySchema, Kr as streamSessionSchema, Kt as ThresholdKeygenRound1, L as HandleRecord, Ln as execListSchema, Lr as roomReadSchema, Lt as StreamAckFrame, M as ExecSessionInfo, Mn as dataRequestFrameSchema, Mr as roomMembersOkSchema, Mt as RoomRekey, N as Frame, Nn as deviceIdHexSchema, Nr as roomMembersSchema, Nt as RoomSend, O as DeviceIdHex, On as coseSign1Schema, Or as roomJoinOkSchema, Ot as RoomNotice, P as FrameVariant, Pn as deviceIdSchema, Pr as roomNoticeClaimsSchema, Pt as SessionId, Q as PathTrace, Qn as manageResponseFrameSchema, Qr as thresholdKeygenRound1Schema, Qt as TokenClaims, R as HandshakeFrame, Rn as execSessionInfoSchema, Rr as roomRekeySchema, Rt as StreamDataFrame, S as CoseSign1, Sn as coordinatorFrameSchema, Sr as relayInboundFrameSchema, St as RoomJoin, T as DataHaveFrame, Tn as coseHeaderAlgSchema, Tr as revocationClaimsSchema, Tt as RoomMember, U as ManageError, Un as handleRecordSchema, Ur as streamAckFrameSchema, Ut as ThresholdCommit, V as ManageCommand, Vn as gossipFrameSchema, Vr as sfuTrackEntrySchema, Vt as SyncPunchFrame, W as ManageOk, Wn as handshakeFrameSchema, Wr as streamDataFrameSchema, Wt as ThresholdCommitment, X as ObservedAddressFrame, Xn as manageOkSchema, Xr as thresholdCommitmentSchema, Xt as ThresholdSign, Y as NamespacedDomainId, Yn as manageErrorSchema, Yr as thresholdCommitSchema, Yt as ThresholdShareEnvelope, Z as OwnerNamedRoomPath, Zn as manageRequestFrameSchema, Zr as thresholdKeygenConfirmSchema, Zt as ThresholdSubject, _ as CoreCapability, _n as capabilityRequestSchema, _r as ptyResizeSchema, _t as RelayOfferFrame, a as BulkOpen, ai as topologyGetSchema, an as WebrtcOffer, ar as pathTraceSchema, at as PrivateUseDomainId, b as CoseHeaderKid, bn as capabilityVerbSchema, br as relayConnectFrameSchema, bt as RevocationEntry, c as CandidatesFrame, ci as versionGetSchema, cn as bulkCancelSchema, cr as pingFrameSchema, ct as ProcSpawn, d as CapabilityRequest, di as webrtcOfferSchema, dn as bulkOpenSchema, dr as privateUseDomainIdSchema, dt as PtyResize, ei as thresholdShareClaimsSchema, en as TopologyPeers, er as namespacedCapabilitySchema, et as PeerAdvert, f as CapabilityScope, fi as wireCandidateSchema, fn as bulkResumeSchema, fr as procKillSchema, ft as PtySpawn, g as CoordinatorFrame, gn as capabilityGrantSchema, gr as ptyKillSchema, gt as RelayInboundFrame, h as CloseFrame, hn as capabilityGrantOkSchema, hr as protocolVersionSchema, ht as RelayDataFrame, i as BulkEndFrame, ii as tokenClaimsSchema, in as WebrtcIceCandidate, ir as pathTraceOkSchema, it as PrivateUseCapability, j as ExecList, jn as dataHaveFrameSchema, jr as roomMemberSchema, jt as RoomRead, k as DmRoomPath, kn as coseTokenHeadersSchema, kr as roomJoinSchema, kt as RoomNoticeClaims, l as CapabilityGrant, li as webrtcAnswerSchema, ln as bulkDataFrameSchema, lr as pongFrameSchema, lt as ProtocolVersion, m as CapabilityVerb, mn as candidatesFrameSchema, mr as procSpawnSchema, mt as RelayConnectFrame, n as BulkCancel, ni as thresholdSignSchema, nn as VersionGet, nr as observedAddressFrameSchema, nt as PingFrame, o as BulkResume, oi as topologyPeersSchema, on as WireCandidate, or as peerAdvertSchema, ot as ProcKill, p as CapabilityToken, pn as candidateKindSchema, pr as procSignalSchema, pt as PtyWrite, q as MessageRef, qn as manageCommandParamsSchema, qr as syncPunchFrameSchema, qt as ThresholdKeygenRound2, r as BulkDataFrame, ri as thresholdSubjectSchema, rn as WebrtcAnswer, rr as ownerNamedRoomPathSchema, rt as PongFrame, s as CandidateKind, si as transferIdSchema, sn as bulkAckFrameSchema, sr as peerIdentitySchema, st as ProcSignal, t as BulkAckFrame, ti as thresholdShareEnvelopeSchema, tn as TransferId, tr as namespacedDomainIdSchema, tt as PeerIdentity, u as CapabilityGrantOk, ui as webrtcIceCandidateSchema, un as bulkEndFrameSchema, ur as privateUseCapabilitySchema, ut as PtyKill, v as CoreDomainName, vn as capabilityScopeSchema, vr as ptySpawnSchema, vt as RevocationAnnounceFrame, w as DataEntriesFrame, wn as coreDomainNameSchema, wr as revocationAnnounceFrameSchema, wt as RoomLeave, x as CoseHeaderLabel, xn as closeFrameSchema, xr as relayDataFrameSchema, xt as RoomInvite, y as CoseHeaderAlg, yn as capabilityTokenSchema, yr as ptyWriteSchema, yt as RevocationClaims, z as IceCandidateInit, zn as frameSchema, zr as roomSendSchema, zt as StreamEndFrame } from "../protocol-Bi2fzBCc.cjs";
2
2
  export { BulkAckFrame, BulkCancel, BulkDataFrame, BulkEndFrame, BulkOpen, BulkResume, CandidateKind, CandidatesFrame, CapabilityGrant, CapabilityGrantOk, CapabilityRequest, CapabilityScope, CapabilityToken, CapabilityVerb, CloseFrame, CoordinatorFrame, CoreCapability, CoreDomainName, CoseHeaderAlg, CoseHeaderKid, CoseHeaderLabel, CoseSign1, CoseTokenHeaders, DataEntriesFrame, DataHaveFrame, DataRequestFrame, DeviceId, DeviceIdHex, DmRoomPath, DomainId, ExecList, ExecSessionInfo, Frame, FrameVariant, GossipFrame, HandleClaims, HandleRecord, HandshakeFrame, IceCandidateInit, IdentityKey, ManageCommand, ManageCommandParams, ManageError, ManageOk, ManageRequestFrame, ManageResponseFrame, MessageRef, NamespacedCapability, NamespacedDomainId, ObservedAddressFrame, OwnerNamedRoomPath, PathTrace, PathTraceOk, PeerAdvert, PeerIdentity, PingFrame, PongFrame, PrivateUseCapability, PrivateUseDomainId, ProcKill, ProcSignal, ProcSpawn, ProtocolVersion, PtyKill, PtyResize, PtySpawn, PtyWrite, RelayConnectFrame, RelayDataFrame, RelayInboundFrame, RelayOfferFrame, RevocationAnnounceFrame, RevocationClaims, RevocationEntry, RoomInvite, RoomJoin, RoomJoinOk, RoomLeave, RoomMember, RoomMembers, RoomMembersOk, RoomNotice, RoomNoticeClaims, RoomPath, RoomRead, RoomRekey, RoomSend, SessionId, SfuTrackEntry, SfuTrackMap, StreamAckFrame, StreamDataFrame, StreamEndFrame, StreamSession, SyncPunchFrame, ThresholdAbort, ThresholdCommit, ThresholdCommitment, ThresholdKeygenConfirm, ThresholdKeygenRound1, ThresholdKeygenRound2, ThresholdShareClaims, ThresholdShareEnvelope, ThresholdSign, ThresholdSubject, TokenClaims, TopologyGet, TopologyPeers, TransferId, VersionGet, WebrtcAnswer, WebrtcIceCandidate, WebrtcOffer, WireCandidate, bulkAckFrameSchema, bulkCancelSchema, bulkDataFrameSchema, bulkEndFrameSchema, bulkOpenSchema, bulkResumeSchema, candidateKindSchema, candidatesFrameSchema, capabilityGrantOkSchema, capabilityGrantSchema, capabilityRequestSchema, capabilityScopeSchema, capabilityTokenSchema, capabilityVerbSchema, closeFrameSchema, coordinatorFrameSchema, coreCapabilitySchema, coreDomainNameSchema, coseHeaderAlgSchema, coseHeaderKidSchema, coseHeaderLabelSchema, coseSign1Schema, coseTokenHeadersSchema, dataEntriesFrameSchema, dataHaveFrameSchema, dataRequestFrameSchema, deviceIdHexSchema, deviceIdSchema, dmRoomPathSchema, domainIdSchema, execListSchema, execSessionInfoSchema, frameSchema, frameVariantSchema, gossipFrameSchema, handleClaimsSchema, handleRecordSchema, handshakeFrameSchema, iceCandidateInitSchema, identityKeySchema, manageCommandParamsSchema, manageCommandSchema, manageErrorSchema, manageOkSchema, manageRequestFrameSchema, manageResponseFrameSchema, messageRefSchema, namespacedCapabilitySchema, namespacedDomainIdSchema, observedAddressFrameSchema, ownerNamedRoomPathSchema, pathTraceOkSchema, pathTraceSchema, peerAdvertSchema, peerIdentitySchema, pingFrameSchema, pongFrameSchema, privateUseCapabilitySchema, privateUseDomainIdSchema, procKillSchema, procSignalSchema, procSpawnSchema, protocolVersionSchema, ptyKillSchema, ptyResizeSchema, ptySpawnSchema, ptyWriteSchema, relayConnectFrameSchema, relayDataFrameSchema, relayInboundFrameSchema, relayOfferFrameSchema, revocationAnnounceFrameSchema, revocationClaimsSchema, revocationEntrySchema, roomInviteSchema, roomJoinOkSchema, roomJoinSchema, roomLeaveSchema, roomMemberSchema, roomMembersOkSchema, roomMembersSchema, roomNoticeClaimsSchema, roomNoticeSchema, roomPathSchema, roomReadSchema, roomRekeySchema, roomSendSchema, sessionIdSchema, sfuTrackEntrySchema, sfuTrackMapSchema, streamAckFrameSchema, streamDataFrameSchema, streamEndFrameSchema, streamSessionSchema, syncPunchFrameSchema, thresholdAbortSchema, thresholdCommitSchema, thresholdCommitmentSchema, thresholdKeygenConfirmSchema, thresholdKeygenRound1Schema, thresholdKeygenRound2Schema, thresholdShareClaimsSchema, thresholdShareEnvelopeSchema, thresholdSignSchema, thresholdSubjectSchema, tokenClaimsSchema, topologyGetSchema, topologyPeersSchema, transferIdSchema, versionGetSchema, webrtcAnswerSchema, webrtcIceCandidateSchema, webrtcOfferSchema, wireCandidateSchema };