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
package/README.md CHANGED
@@ -13,6 +13,7 @@ The TypeScript implementation of wire-mesh's protocol, built ports/adapters: dom
13
13
 
14
14
  - **Handshake negotiation** (`src/domain/handshake.ts`) -- protocol-version and capability-domain negotiation between two peers, the mechanism agent-comms issue #31 is fixed by.
15
15
  - **Capability-token verification** (`src/domain/tokens.ts`) -- the full chain tokens.cddl documents: COSE_Sign1 signature verification, the self-certifying issuer-key check (`sha256(issuer-key.public-key) == issuer`), expiry/not-before, issuer-matched revocation (only a token's own issuer's signed revocation-entry counts, checked across every ancestor in the delegation chain, not just the leaf), and recursive delegation-chain narrowing across all three axes of authority: a delegated token's issuer must be its parent's bearer (the chain is unbroken), its expiry must not exceed its parent's, and its scope must narrow its parent's (identical kind, equal-or-descendant path when the parent carries one) with an identical capability verb (the verb grammar has no sub-verb relation, so a different verb is different authority, not narrower). Also exports `verifyRevocationEntry` for ingesting gossiped revocation-announce frames: each entry is itself a signed, self-certifying COSE_Sign1 over revocation-claims, verified before it may enter the revocation view.
16
+ - **Peer-advert verification** (`src/domain/peer-advert.ts`): the two checks transport.cddl states for a gossiped advert, applied by the relay hub before it registers or forwards one and by every session before one reaches its directory: the embedded identity-key self-certifies (`sha256(identity-key.public-key) == device`), and the signature verifies under that same key over the domain-separated canonical encoding of the advert with its signature removed, which covers the open extension tail as well. Deliberately not tokens.cddl's COSE Sig_structure: an advert's open tail lets an attacker craft one whose encoding also satisfies token-claims, so the prefixed construction keeps the two byte strings disjoint. Reaches a verdict for every input rather than throwing, since an advert's algorithm and key bytes are attacker-chosen.
16
17
 
17
18
  ## Deliberately deferred
18
19
 
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-Bi2fzBCc.cjs";
2
2
  //#region src/adapters/frame-codec.d.ts
3
3
  export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
4
  /** Wraps frame as a relay-data-frame's own opaque payload, stamping to-device when the caller knows which established pairing to address it to (wire-mesh#30) -- the outbound counterpart to tryDecodeFrame's own doc comment below, which describes the inbound side of the same relay-data envelope. Omitting toDevice leaves the frame unaddressed, which the receiving hub then routes via its own most-recently-established-pairing fallback. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-Bi2fzBCc.mjs";
2
2
  //#region src/adapters/frame-codec.d.ts
3
3
  export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
4
  /** Wraps frame as a relay-data-frame's own opaque payload, stamping to-device when the caller knows which established pairing to address it to (wire-mesh#30) -- the outbound counterpart to tryDecodeFrame's own doc comment below, which describes the inbound side of the same relay-data envelope. Omitting toDevice leaves the frame unaddressed, which the receiving hub then routes via its own most-recently-established-pairing fallback. */
@@ -1,5 +1,5 @@
1
- import { B as IdentityKey, D as DeviceId } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { B as IdentityKey, D as DeviceId } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
3
3
  import { webcrypto } from "node:crypto";
4
4
  //#region src/adapters/node-identity.d.ts
5
5
  export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
@@ -1,5 +1,5 @@
1
- import { B as IdentityKey, D as DeviceId } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { B as IdentityKey, D as DeviceId } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
3
3
  import { webcrypto } from "node:crypto";
4
4
  //#region src/adapters/node-identity.d.ts
5
5
  export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-Ca_ZyyZe.cjs";
1
+ import { r as Transport } from "../transport-Cq_odHy3.cjs";
2
2
  //#region src/adapters/tcp-transport.d.ts
3
3
  /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
4
  export declare function createTcpTransport(): Transport;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-BH8kGWXt.mjs";
1
+ import { r as Transport } from "../transport-GcJ4LYVE.mjs";
2
2
  //#region src/adapters/tcp-transport.d.ts
3
3
  /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
4
  export declare function createTcpTransport(): Transport;
@@ -1,5 +1,5 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.cjs";
2
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
3
3
  import { DeviceKeyed } from "./threshold-wasm.cjs";
4
4
  import { ThresholdSubject } from "../domain/threshold-subject.cjs";
5
5
  //#region src/adapters/threshold-identity.d.ts
@@ -1,5 +1,5 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.mjs";
2
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
3
3
  import { DeviceKeyed } from "./threshold-wasm.mjs";
4
4
  import { ThresholdSubject } from "../domain/threshold-subject.mjs";
5
5
  //#region src/adapters/threshold-identity.d.ts
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.cjs";
2
2
  //#region src/adapters/threshold-wasm.d.ts
3
3
  /** One `(deviceId, bytes)` pair -- the wasm boundary's own encoding of a map keyed by participant, since wasm-bindgen has no built-in `Map<Uint8Array, Uint8Array>` marshalling. */
4
4
  export interface DeviceKeyed<T extends Uint8Array = Uint8Array<ArrayBuffer>> {
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.mjs";
2
2
  //#region src/adapters/threshold-wasm.d.ts
3
3
  /** One `(deviceId, bytes)` pair -- the wasm boundary's own encoding of a map keyed by participant, since wasm-bindgen has no built-in `Map<Uint8Array, Uint8Array>` marshalling. */
4
4
  export interface DeviceKeyed<T extends Uint8Array = Uint8Array<ArrayBuffer>> {
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-Ca_ZyyZe.cjs";
1
+ import { r as Transport } from "../transport-Cq_odHy3.cjs";
2
2
  //#region src/adapters/tls-transport.d.ts
3
3
  export interface TlsIdentity {
4
4
  certificatePem: string;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-BH8kGWXt.mjs";
1
+ import { r as Transport } from "../transport-GcJ4LYVE.mjs";
2
2
  //#region src/adapters/tls-transport.d.ts
3
3
  export interface TlsIdentity {
4
4
  certificatePem: string;
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { KeyValueStorage } from "../ports/storage.cjs";
3
- import { t as Connection } from "../transport-Ca_ZyyZe.cjs";
3
+ import { t as Connection } from "../transport-Cq_odHy3.cjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
5
5
  //#region src/domain/bulk.d.ts
6
6
  /** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { KeyValueStorage } from "../ports/storage.mjs";
3
- import { t as Connection } from "../transport-BH8kGWXt.mjs";
3
+ import { t as Connection } from "../transport-GcJ4LYVE.mjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
5
5
  //#region src/domain/bulk.d.ts
6
6
  /** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, 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, f as CapabilityScope, 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/capability-grant.d.ts
7
7
  /**
8
8
  * Builds a capability-grant command per management.cddl. The outer `manage-command.verb` is the capability string itself, the identical convention capability-request.ts's own buildCapabilityRequestCommand already establishes -- a receiver's per-capability handler is how it knows which grant this push is even for. `params.verb` is the fixed "capability.grant" marker. No scope or invitee field: manage-request-frame's own top-level scope already carries the former, and the request's own destination already carries the latter -- naming either a second time inside params could only ever disagree with the fact it duplicates.
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, 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/capability-grant.d.ts
7
7
  /**
8
8
  * Builds a capability-grant command per management.cddl. The outer `manage-command.verb` is the capability string itself, the identical convention capability-request.ts's own buildCapabilityRequestCommand already establishes -- a receiver's per-capability handler is how it knows which grant this push is even for. `params.verb` is the fixed "capability.grant" marker. No scope or invitee field: manage-request-frame's own top-level scope already carries the former, and the request's own destination already carries the latter -- naming either a second time inside params could only ever disagree with the fact it duplicates.
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } 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, MeshSession } from "./mesh-session.cjs";
5
5
  //#region src/domain/capability-request.d.ts
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } 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, MeshSession } from "./mesh-session.mjs";
5
5
  //#region src/domain/capability-request.d.ts
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { KeyValueStorage } from "../ports/storage.cjs";
3
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
3
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
4
4
  //#region src/domain/data-sync.d.ts
5
5
  export interface DataSyncOptions {
6
6
  identity: IdentityPort;
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { KeyValueStorage } from "../ports/storage.mjs";
3
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
3
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
4
4
  //#region src/domain/data-sync.d.ts
5
5
  export interface DataSyncOptions {
6
6
  identity: IdentityPort;
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.cjs";
2
2
  //#region src/domain/device-id.d.ts
3
3
  /** Lowercase, byte-exact hex for an arbitrary-length byte string -- the same encoding convention deviceIdToHex uses for the fixed-length device-id case, extracted so any other byte string needing a stable, displayable, map-keyable text form (e.g. a token-id, which tokens.cddl defines as an arbitrary-length bstr rather than a 32-byte device-id) can use the identical convention without going through a device-id-shaped function. */
4
4
  export declare function bytesToHex(bytes: Uint8Array): string;
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId } from "../protocol-Bi2fzBCc.mjs";
2
2
  //#region src/domain/device-id.d.ts
3
3
  /** Lowercase, byte-exact hex for an arbitrary-length byte string -- the same encoding convention deviceIdToHex uses for the fixed-length device-id case, extracted so any other byte string needing a stable, displayable, map-keyable text form (e.g. a token-id, which tokens.cddl defines as an arbitrary-length bstr rather than a 32-byte device-id) can use the identical convention without going through a device-id-shaped function. */
4
4
  export declare function bytesToHex(bytes: Uint8Array): string;
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.cjs";
2
- import { r as Transport, t as Connection } from "../transport-Ca_ZyyZe.cjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.cjs";
2
+ import { r as Transport, t as Connection } from "../transport-Cq_odHy3.cjs";
3
3
  import { IncomingManageRequest, ManageOutcome } from "./mesh-session.cjs";
4
4
  //#region src/domain/direct-manage-request.d.ts
5
5
  /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
2
- import { r as Transport, t as Connection } from "../transport-BH8kGWXt.mjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.mjs";
2
+ import { r as Transport, t as Connection } from "../transport-GcJ4LYVE.mjs";
3
3
  import { IncomingManageRequest, ManageOutcome } from "./mesh-session.mjs";
4
4
  //#region src/domain/direct-manage-request.d.ts
5
5
  /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, et as PeerAdvert } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, et as PeerAdvert } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { MeshSession } from "./mesh-session.cjs";
3
3
  //#region src/domain/gossip-expansion.d.ts
4
4
  /** One not-yet-considered device this session's gossip directory has surfaced, carrying only what a dial/trust decision needs: the device-id to decide identity, and the "host:port" addresses its own peer-advert claims are directly reachable at, in the order it advertised them. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, et as PeerAdvert } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, et as PeerAdvert } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { MeshSession } from "./mesh-session.mjs";
3
3
  //#region src/domain/gossip-expansion.d.ts
4
4
  /** One not-yet-considered device this session's gossip directory has surfaced, carrying only what a dial/trust decision needs: the device-id to decide identity, and the "host:port" addresses its own peer-advert claims are directly reachable at, in the order it advertised them. */
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_gossip_extensions = require("../gossip-extensions-Dr_T7hk_.cjs");
2
+ const require_gossip_extensions = require("../gossip-extensions-DRVTHKnR.cjs");
3
3
  exports.TOPOLOGY_PEERS_GOSSIP_KEY = require_gossip_extensions.TOPOLOGY_PEERS_GOSSIP_KEY;
4
4
  exports.validateGossipExtensions = require_gossip_extensions.validateGossipExtensions;
@@ -1,2 +1,2 @@
1
- import { n as validateGossipExtensions, t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-Cdy5mQhY.mjs";
1
+ import { n as validateGossipExtensions, t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-BNiI44sU.mjs";
2
2
  export { TOPOLOGY_PEERS_GOSSIP_KEY, validateGossipExtensions };
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, f as CapabilityScope } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, f as CapabilityScope } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { DirectoryEntry, MeshSession } from "./mesh-session.cjs";
3
3
  import { z } from "zod";
4
4
  //#region src/domain/grant-candidates.d.ts
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, f as CapabilityScope } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, f as CapabilityScope } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { DirectoryEntry, MeshSession } from "./mesh-session.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/domain/grant-candidates.d.ts
@@ -1,4 +1,4 @@
1
- import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-BnbpCk0Z.cjs";
1
+ import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-Bi2fzBCc.cjs";
2
2
  //#region src/domain/handshake.d.ts
3
3
  /** The highest protocol version this build of core understands. */
4
4
  export declare const SUPPORTED_PROTOCOL_VERSION: ProtocolVersion;
@@ -1,4 +1,4 @@
1
- import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-BnbpCk0Z.mjs";
1
+ import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-Bi2fzBCc.mjs";
2
2
  //#region src/domain/handshake.d.ts
3
3
  /** The highest protocol version this build of core understands. */
4
4
  export declare const SUPPORTED_PROTOCOL_VERSION: ProtocolVersion;
@@ -4,7 +4,8 @@ const require_domain_handshake = require("./handshake.cjs");
4
4
  const require_domain_async_queue = require("./async-queue.cjs");
5
5
  const require_domain_ping_round_trips = require("./ping-round-trips.cjs");
6
6
  const require_domain_topology_snapshot = require("./topology-snapshot.cjs");
7
- const require_gossip_extensions = require("../gossip-extensions-Dr_T7hk_.cjs");
7
+ const require_gossip_extensions = require("../gossip-extensions-DRVTHKnR.cjs");
8
+ const require_domain_peer_advert = require("./peer-advert.cjs");
8
9
  const require_adapters_frame_codec = require("../adapters/frame-codec.cjs");
9
10
  //#region src/domain/relay-pairing.ts
10
11
  function createRelayPairings() {
@@ -135,7 +136,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
135
136
  }
136
137
  incomingQueue.push(incoming);
137
138
  }
138
- function applyFrame(frame) {
139
+ async function applyFrame(frame) {
139
140
  if (frame.type === "relay-data") {
140
141
  const inner = require_adapters_frame_codec.tryDecodeFrame(frame.payload);
141
142
  if (inner !== null && (inner.type === "manage-request" || inner.type === "manage-response")) {
@@ -159,6 +160,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
159
160
  });
160
161
  if (frame.type === "handshake") applyRemoteHandshake(frame);
161
162
  else if (frame.type === "gossip") for (const advert of frame.peers) {
163
+ if (!await require_domain_peer_advert.verifyPeerAdvert(identity, advert)) continue;
162
164
  directory.set(require_domain_device_id.deviceIdToHex(advert.device), {
163
165
  device: advert.device,
164
166
  advert
@@ -241,26 +243,27 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
241
243
  async function consume(link, address, localDomains) {
242
244
  for await (const frame of link.receive()) {
243
245
  if (feedCancelled) return;
244
- applyFrame(frame);
246
+ await applyFrame(frame);
245
247
  await onFrame?.(link, frame);
246
248
  emit();
247
249
  }
248
250
  onSessionEnd?.(link);
249
251
  if (state.status === "connected") handleDisconnect("node closed the connection", address, localDomains);
250
252
  }
251
- /** Builds this side's own self-advert: this node's own directly-reachable addresses (wire-mesh#38), or none for a caller with nothing to offer (a browser client, which cannot accept inbound connections) -- either is an honest advert, not a stopgap. extensions merge onto peer-advert's own open `* tstr => any` tail -- the mechanism sendGossipUpdate uses to keep a gossiped fact (presence status, an accept/refuse policy, or any future domain's own) live over the connection's lifetime. Extensions, CORE_VERSION_GOSSIP_KEY, and topology/peers are all spread before the mandatory fields (never after) so a caller-supplied key of the same name can never shadow them on the wire -- validateGossipExtensions already rejects every such collision loudly, but the field order is kept safe in its own right rather than relying solely on the guard staying in sync. topology/peers (wire-mesh#180) is recomputed fresh on every call, the same "always current, never cached" treatment snapshot-seconds already gets, since a session's own connection identity and relay pairings can change between one self-advert and the next. */
252
- function buildSelfAdvert(extensions) {
253
+ /** Builds this side's own self-advert and signs it with this node's own key (wire-mesh#225), which is what makes it usable by a receiver that learned of it through a hub or a gateway rather than directly from here. this node's own directly-reachable addresses (wire-mesh#38) are advertised, or none for a caller with nothing to offer (a browser client, which cannot accept inbound connections) -- either is an honest advert, not a stopgap. extensions merge onto peer-advert's own open `* tstr => any` tail -- the mechanism sendGossipUpdate uses to keep a gossiped fact (presence status, an accept/refuse policy, or any future domain's own) live over the connection's lifetime, and every such key is covered by the signature. Extensions, CORE_VERSION_GOSSIP_KEY, and topology/peers are all spread before the mandatory fields (never after) so a caller-supplied key of the same name can never shadow them on the wire -- validateGossipExtensions already rejects every such collision loudly, but the field order is kept safe in its own right rather than relying solely on the guard staying in sync. topology/peers (wire-mesh#180) is recomputed fresh on every call, the same "always current, never cached" treatment snapshot-seconds already gets, since a session's own connection identity and relay pairings can change between one self-advert and the next. */
254
+ async function buildSelfAdvert(extensions) {
253
255
  if (extensions !== void 0) require_gossip_extensions.validateGossipExtensions(extensions);
254
256
  return {
255
257
  type: "gossip",
256
- peers: [{
258
+ peers: [await require_domain_peer_advert.signPeerAdvert(identity, {
257
259
  ...extensions,
258
260
  [require_gossip_extensions.CORE_VERSION_GOSSIP_KEY]: require_gossip_extensions.OWN_VERSION,
259
261
  [require_gossip_extensions.TOPOLOGY_PEERS_GOSSIP_KEY]: topology.compute(),
260
262
  device: identity.deviceId,
261
263
  addresses: [...addresses],
262
- "snapshot-seconds": Math.floor(clock.now() / MS_PER_SECOND)
263
- }]
264
+ "snapshot-seconds": Math.floor(clock.now() / MS_PER_SECOND),
265
+ "identity-key": identity.identityKey
266
+ })]
264
267
  };
265
268
  }
266
269
  /** Everything a connection needs once it exists, regardless of whether it was dialled (createMeshSession's own doConnect, below) or handed over already established (acceptMeshSession): send this side's handshake and self-advert, arm the handshake timeout, and start consuming frames. The two entry points differ only in how link itself came to exist and what address means for it -- a real dial target for one, a caller-chosen label for the other, since the Connection/Transport ports expose no remote-address concept of their own for an accepted connection. */
@@ -279,7 +282,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
279
282
  });
280
283
  await connection.send(localHandshakeSent);
281
284
  emit();
282
- const selfAdvert = buildSelfAdvert();
285
+ const selfAdvert = await buildSelfAdvert();
283
286
  frameLog.push({
284
287
  direction: "sent",
285
288
  frame: selfAdvert
@@ -397,7 +400,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
397
400
  },
398
401
  async sendGossipUpdate(extensions) {
399
402
  requireConnectedLink();
400
- const frame = buildSelfAdvert(extensions);
403
+ const frame = await buildSelfAdvert(extensions);
401
404
  frameLog.push({
402
405
  direction: "sent",
403
406
  frame
@@ -1,7 +1,7 @@
1
- import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, U as ManageError, V as ManageCommand, W as ManageOk, bt as RevocationEntry, en as TopologyPeers, et as PeerAdvert, f as CapabilityScope, lt as ProtocolVersion, p as CapabilityToken, w as DataEntriesFrame } from "../protocol-BnbpCk0Z.cjs";
2
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
1
+ import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, U as ManageError, V as ManageCommand, W as ManageOk, bt as RevocationEntry, en as TopologyPeers, et as PeerAdvert, f as CapabilityScope, lt as ProtocolVersion, p as CapabilityToken, w as DataEntriesFrame } 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 { r as Transport, t as Connection } from "../transport-Ca_ZyyZe.cjs";
4
+ import { r as Transport, t as Connection } from "../transport-Cq_odHy3.cjs";
5
5
  //#region src/domain/mesh-session.d.ts
6
6
  /** How long to wait for the node's handshake before calling it unanswered. A relay-only node never sends one; that is a state to display, not an error. */
7
7
  export declare const HANDSHAKE_TIMEOUT_MS = 3000;
@@ -1,7 +1,7 @@
1
- import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, U as ManageError, V as ManageCommand, W as ManageOk, bt as RevocationEntry, en as TopologyPeers, et as PeerAdvert, f as CapabilityScope, lt as ProtocolVersion, p as CapabilityToken, w as DataEntriesFrame } from "../protocol-BnbpCk0Z.mjs";
2
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
1
+ import { D as DeviceId, E as DataRequestFrame, N as Frame, T as DataHaveFrame, U as ManageError, V as ManageCommand, W as ManageOk, bt as RevocationEntry, en as TopologyPeers, et as PeerAdvert, f as CapabilityScope, lt as ProtocolVersion, p as CapabilityToken, w as DataEntriesFrame } 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 { r as Transport, t as Connection } from "../transport-BH8kGWXt.mjs";
4
+ import { r as Transport, t as Connection } from "../transport-GcJ4LYVE.mjs";
5
5
  //#region src/domain/mesh-session.d.ts
6
6
  /** How long to wait for the node's handshake before calling it unanswered. A relay-only node never sends one; that is a state to display, not an error. */
7
7
  export declare const HANDSHAKE_TIMEOUT_MS = 3000;
@@ -3,7 +3,8 @@ import { negotiate } from "./handshake.mjs";
3
3
  import { createAsyncQueue } from "./async-queue.mjs";
4
4
  import { createPingRoundTrips } from "./ping-round-trips.mjs";
5
5
  import { createTopologySnapshotTracker } from "./topology-snapshot.mjs";
6
- import { a as isVersionGetCommand, i as OWN_VERSION, n as validateGossipExtensions, o as versionGetOutcome, r as CORE_VERSION_GOSSIP_KEY, t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-Cdy5mQhY.mjs";
6
+ import { a as isVersionGetCommand, i as OWN_VERSION, n as validateGossipExtensions, o as versionGetOutcome, r as CORE_VERSION_GOSSIP_KEY, t as TOPOLOGY_PEERS_GOSSIP_KEY } from "../gossip-extensions-BNiI44sU.mjs";
7
+ import { signPeerAdvert, verifyPeerAdvert } from "./peer-advert.mjs";
7
8
  import { tryDecodeFrame, wrapRelayData } from "../adapters/frame-codec.mjs";
8
9
  //#region src/domain/relay-pairing.ts
9
10
  function createRelayPairings() {
@@ -134,7 +135,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
134
135
  }
135
136
  incomingQueue.push(incoming);
136
137
  }
137
- function applyFrame(frame) {
138
+ async function applyFrame(frame) {
138
139
  if (frame.type === "relay-data") {
139
140
  const inner = tryDecodeFrame(frame.payload);
140
141
  if (inner !== null && (inner.type === "manage-request" || inner.type === "manage-response")) {
@@ -158,6 +159,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
158
159
  });
159
160
  if (frame.type === "handshake") applyRemoteHandshake(frame);
160
161
  else if (frame.type === "gossip") for (const advert of frame.peers) {
162
+ if (!await verifyPeerAdvert(identity, advert)) continue;
161
163
  directory.set(deviceIdToHex(advert.device), {
162
164
  device: advert.device,
163
165
  advert
@@ -240,26 +242,27 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
240
242
  async function consume(link, address, localDomains) {
241
243
  for await (const frame of link.receive()) {
242
244
  if (feedCancelled) return;
243
- applyFrame(frame);
245
+ await applyFrame(frame);
244
246
  await onFrame?.(link, frame);
245
247
  emit();
246
248
  }
247
249
  onSessionEnd?.(link);
248
250
  if (state.status === "connected") handleDisconnect("node closed the connection", address, localDomains);
249
251
  }
250
- /** Builds this side's own self-advert: this node's own directly-reachable addresses (wire-mesh#38), or none for a caller with nothing to offer (a browser client, which cannot accept inbound connections) -- either is an honest advert, not a stopgap. extensions merge onto peer-advert's own open `* tstr => any` tail -- the mechanism sendGossipUpdate uses to keep a gossiped fact (presence status, an accept/refuse policy, or any future domain's own) live over the connection's lifetime. Extensions, CORE_VERSION_GOSSIP_KEY, and topology/peers are all spread before the mandatory fields (never after) so a caller-supplied key of the same name can never shadow them on the wire -- validateGossipExtensions already rejects every such collision loudly, but the field order is kept safe in its own right rather than relying solely on the guard staying in sync. topology/peers (wire-mesh#180) is recomputed fresh on every call, the same "always current, never cached" treatment snapshot-seconds already gets, since a session's own connection identity and relay pairings can change between one self-advert and the next. */
251
- function buildSelfAdvert(extensions) {
252
+ /** Builds this side's own self-advert and signs it with this node's own key (wire-mesh#225), which is what makes it usable by a receiver that learned of it through a hub or a gateway rather than directly from here. this node's own directly-reachable addresses (wire-mesh#38) are advertised, or none for a caller with nothing to offer (a browser client, which cannot accept inbound connections) -- either is an honest advert, not a stopgap. extensions merge onto peer-advert's own open `* tstr => any` tail -- the mechanism sendGossipUpdate uses to keep a gossiped fact (presence status, an accept/refuse policy, or any future domain's own) live over the connection's lifetime, and every such key is covered by the signature. Extensions, CORE_VERSION_GOSSIP_KEY, and topology/peers are all spread before the mandatory fields (never after) so a caller-supplied key of the same name can never shadow them on the wire -- validateGossipExtensions already rejects every such collision loudly, but the field order is kept safe in its own right rather than relying solely on the guard staying in sync. topology/peers (wire-mesh#180) is recomputed fresh on every call, the same "always current, never cached" treatment snapshot-seconds already gets, since a session's own connection identity and relay pairings can change between one self-advert and the next. */
253
+ async function buildSelfAdvert(extensions) {
252
254
  if (extensions !== void 0) validateGossipExtensions(extensions);
253
255
  return {
254
256
  type: "gossip",
255
- peers: [{
257
+ peers: [await signPeerAdvert(identity, {
256
258
  ...extensions,
257
259
  [CORE_VERSION_GOSSIP_KEY]: OWN_VERSION,
258
260
  [TOPOLOGY_PEERS_GOSSIP_KEY]: topology.compute(),
259
261
  device: identity.deviceId,
260
262
  addresses: [...addresses],
261
- "snapshot-seconds": Math.floor(clock.now() / MS_PER_SECOND)
262
- }]
263
+ "snapshot-seconds": Math.floor(clock.now() / MS_PER_SECOND),
264
+ "identity-key": identity.identityKey
265
+ })]
263
266
  };
264
267
  }
265
268
  /** Everything a connection needs once it exists, regardless of whether it was dialled (createMeshSession's own doConnect, below) or handed over already established (acceptMeshSession): send this side's handshake and self-advert, arm the handshake timeout, and start consuming frames. The two entry points differ only in how link itself came to exist and what address means for it -- a real dial target for one, a caller-chosen label for the other, since the Connection/Transport ports expose no remote-address concept of their own for an accepted connection. */
@@ -278,7 +281,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
278
281
  });
279
282
  await connection.send(localHandshakeSent);
280
283
  emit();
281
- const selfAdvert = buildSelfAdvert();
284
+ const selfAdvert = await buildSelfAdvert();
282
285
  frameLog.push({
283
286
  direction: "sent",
284
287
  frame: selfAdvert
@@ -396,7 +399,7 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
396
399
  },
397
400
  async sendGossipUpdate(extensions) {
398
401
  requireConnectedLink();
399
- const frame = buildSelfAdvert(extensions);
402
+ const frame = await buildSelfAdvert(extensions);
400
403
  frameLog.push({
401
404
  direction: "sent",
402
405
  frame
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, Ot as RoomNotice, T as DataHaveFrame, kt as RoomNoticeClaims, p as CapabilityToken } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, Ot as RoomNotice, T as DataHaveFrame, kt as RoomNoticeClaims, p as CapabilityToken } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { KeyValueStorage } from "../ports/storage.cjs";
3
- import { t as IdentityPort } from "../identity-BRlM-Krs.cjs";
3
+ import { t as IdentityPort } from "../identity-DImeXZcq.cjs";
4
4
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
5
- import { a as RevocationCheck } from "../tokens-BL2BzOmN.cjs";
5
+ import { a as RevocationCheck } from "../tokens-BDVxEvl2.cjs";
6
6
  import { RoomNoticeVerdictReason } from "./room.cjs";
7
7
  //#region src/domain/notice-board.d.ts
8
8
  /**
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, Ot as RoomNotice, T as DataHaveFrame, kt as RoomNoticeClaims, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, Ot as RoomNotice, T as DataHaveFrame, kt as RoomNoticeClaims, p as CapabilityToken } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { KeyValueStorage } from "../ports/storage.mjs";
3
- import { t as IdentityPort } from "../identity-DiuayfEd.mjs";
3
+ import { t as IdentityPort } from "../identity-Dc4xmwp3.mjs";
4
4
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
5
- import { a as RevocationCheck } from "../tokens-DtjdYS_a.mjs";
5
+ import { a as RevocationCheck } from "../tokens-0r9ipK1r.mjs";
6
6
  import { RoomNoticeVerdictReason } from "./room.mjs";
7
7
  //#region src/domain/notice-board.d.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, H as ManageCommandParams, Q as PathTrace, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.cjs";
1
+ import { D as DeviceId, H as ManageCommandParams, Q as PathTrace, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.cjs";
2
2
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
3
3
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.cjs";
4
4
  //#region src/domain/path-trace.d.ts
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, H as ManageCommandParams, Q as PathTrace, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-BnbpCk0Z.mjs";
1
+ import { D as DeviceId, H as ManageCommandParams, Q as PathTrace, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-Bi2fzBCc.mjs";
2
2
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
3
3
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.mjs";
4
4
  //#region src/domain/path-trace.d.ts
@@ -0,0 +1,61 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_token_scope = require("../token-scope-CxHXTT3u.cjs");
3
+ let cbor2 = require("cbor2");
4
+ //#region src/domain/peer-advert.ts
5
+ /** 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. */
6
+ const PEER_ADVERT_SIGNATURE_KEY = "signature";
7
+ /** 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. */
8
+ const PEER_ADVERT_IDENTITY_KEY_KEY = "identity-key";
9
+ /**
10
+ * 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.
11
+ *
12
+ * 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.
13
+ */
14
+ const PEER_ADVERT_SIGNING_CONTEXT = new TextEncoder().encode("wire-mesh/peer-advert/v1\0");
15
+ /**
16
+ * 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.
17
+ *
18
+ * 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.
19
+ */
20
+ function peerAdvertSigningInput(advert) {
21
+ const content = {};
22
+ for (const [key, value] of Object.entries(advert)) {
23
+ if (key === "signature") continue;
24
+ content[key] = value;
25
+ }
26
+ const encoded = (0, cbor2.encode)(content, cbor2.cdeEncodeOptions);
27
+ const input = new Uint8Array(PEER_ADVERT_SIGNING_CONTEXT.length + encoded.length);
28
+ input.set(PEER_ADVERT_SIGNING_CONTEXT, 0);
29
+ input.set(encoded, PEER_ADVERT_SIGNING_CONTEXT.length);
30
+ return input;
31
+ }
32
+ /** 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. */
33
+ async function signPeerAdvert(identity, advert) {
34
+ const signature = await identity.sign(peerAdvertSigningInput(advert));
35
+ return {
36
+ ...advert,
37
+ [PEER_ADVERT_SIGNATURE_KEY]: signature
38
+ };
39
+ }
40
+ /**
41
+ * 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.
42
+ *
43
+ * 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.
44
+ */
45
+ async function verifyPeerAdvert(identity, advert) {
46
+ const key = advert[PEER_ADVERT_IDENTITY_KEY_KEY];
47
+ try {
48
+ const derived = await identity.deriveDeviceId(key["public-key"]);
49
+ if (!require_token_scope.bytesEqual(derived, advert.device)) return false;
50
+ return await identity.verify(key, peerAdvertSigningInput(advert), advert[PEER_ADVERT_SIGNATURE_KEY]);
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ //#endregion
56
+ exports.PEER_ADVERT_IDENTITY_KEY_KEY = PEER_ADVERT_IDENTITY_KEY_KEY;
57
+ exports.PEER_ADVERT_SIGNATURE_KEY = PEER_ADVERT_SIGNATURE_KEY;
58
+ exports.PEER_ADVERT_SIGNING_CONTEXT = PEER_ADVERT_SIGNING_CONTEXT;
59
+ exports.peerAdvertSigningInput = peerAdvertSigningInput;
60
+ exports.signPeerAdvert = signPeerAdvert;
61
+ exports.verifyPeerAdvert = verifyPeerAdvert;