wire-mesh-core 1.55.0 → 1.57.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.
- package/dist/adapters/frame-codec.d.cts +1 -1
- package/dist/adapters/frame-codec.d.mts +1 -1
- package/dist/adapters/node-identity.d.cts +2 -2
- package/dist/adapters/node-identity.d.mts +2 -2
- package/dist/adapters/tcp-transport.d.cts +1 -1
- package/dist/adapters/tcp-transport.d.mts +1 -1
- package/dist/adapters/threshold-identity.d.cts +2 -2
- package/dist/adapters/threshold-identity.d.mts +2 -2
- package/dist/adapters/threshold-wasm.d.cts +1 -1
- package/dist/adapters/threshold-wasm.d.mts +1 -1
- package/dist/adapters/tls-transport.d.cts +1 -1
- package/dist/adapters/tls-transport.d.mts +1 -1
- package/dist/domain/bulk.d.cts +2 -2
- package/dist/domain/bulk.d.mts +2 -2
- package/dist/domain/capability-grant.d.cts +3 -3
- package/dist/domain/capability-grant.d.mts +3 -3
- package/dist/domain/capability-request.d.cts +2 -2
- package/dist/domain/capability-request.d.mts +2 -2
- package/dist/domain/data-sync.d.cts +2 -2
- package/dist/domain/data-sync.d.mts +2 -2
- package/dist/domain/device-id.d.cts +1 -1
- package/dist/domain/device-id.d.mts +1 -1
- package/dist/domain/direct-manage-request.cjs +64 -0
- package/dist/domain/direct-manage-request.d.cts +16 -0
- package/dist/domain/direct-manage-request.d.mts +16 -0
- package/dist/domain/direct-manage-request.mjs +62 -0
- package/dist/domain/gossip-expansion.d.cts +1 -1
- package/dist/domain/gossip-expansion.d.mts +1 -1
- package/dist/domain/grant-candidates.d.cts +1 -1
- package/dist/domain/grant-candidates.d.mts +1 -1
- package/dist/domain/handshake.d.cts +1 -1
- package/dist/domain/handshake.d.mts +1 -1
- package/dist/domain/mesh-session.cjs +33 -65
- package/dist/domain/mesh-session.d.cts +3 -14
- package/dist/domain/mesh-session.d.mts +3 -14
- package/dist/domain/mesh-session.mjs +32 -64
- package/dist/domain/notice-board.d.cts +3 -3
- package/dist/domain/notice-board.d.mts +3 -3
- package/dist/domain/relay-hub.d.cts +2 -2
- package/dist/domain/relay-hub.d.mts +2 -2
- package/dist/domain/relay-use-gate.d.cts +2 -2
- package/dist/domain/relay-use-gate.d.mts +2 -2
- package/dist/domain/revocation-view.d.cts +2 -2
- package/dist/domain/revocation-view.d.mts +2 -2
- package/dist/domain/room-rekey.d.cts +3 -3
- package/dist/domain/room-rekey.d.mts +3 -3
- package/dist/domain/room-token-verification.d.cts +1 -1
- package/dist/domain/room-token-verification.d.mts +1 -1
- package/dist/domain/room.d.cts +4 -4
- package/dist/domain/room.d.mts +4 -4
- package/dist/domain/shard-manifest.d.cts +1 -1
- package/dist/domain/shard-manifest.d.mts +1 -1
- package/dist/domain/tokens.d.cts +1 -1
- package/dist/domain/tokens.d.mts +1 -1
- package/dist/domain/webrtc-signaling.d.cts +2 -2
- package/dist/domain/webrtc-signaling.d.mts +2 -2
- package/dist/generated/protocol.cjs +5 -1
- package/dist/generated/protocol.d.cts +2 -2
- package/dist/generated/protocol.d.mts +2 -2
- package/dist/generated/protocol.mjs +5 -2
- package/dist/{identity-BXrJ6_YM.d.mts → identity-Br6e0Rx5.d.mts} +1 -1
- package/dist/{identity-CmD4l_Mp.d.cts → identity-pOt5KxJB.d.cts} +1 -1
- package/dist/ports/identity.d.cts +1 -1
- package/dist/ports/identity.d.mts +1 -1
- package/dist/ports/transport.d.cts +1 -1
- package/dist/ports/transport.d.mts +1 -1
- package/dist/{protocol-PhUVprXA.d.cts → protocol-D2Ue2nd2.d.cts} +25 -11
- package/dist/{protocol-PhUVprXA.d.mts → protocol-D2Ue2nd2.d.mts} +25 -11
- package/dist/{room-token-verification-D8HndWYM.d.cts → room-token-verification-CNXMjIQd.d.cts} +2 -2
- package/dist/{room-token-verification-Dv1O6-FV.d.mts → room-token-verification-ZHgJhnsu.d.mts} +2 -2
- package/dist/{tokens-DIPzZ9JJ.d.cts → tokens-BtczHfrc.d.cts} +2 -2
- package/dist/{tokens-BVrgBeqF.d.mts → tokens-Hnq9oURj.d.mts} +2 -2
- package/dist/{transport-CbXX1Xd-.d.cts → transport-C5wU0UtA.d.mts} +1 -1
- package/dist/{transport-DG9ouGe-.d.mts → transport-fUY3alfO.d.cts} +1 -1
- package/package.json +5 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-
|
|
1
|
+
import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-D2Ue2nd2.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { B as IdentityKey, D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-pOt5KxJB.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { B as IdentityKey, D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-Br6e0Rx5.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-
|
|
1
|
+
import { r as Transport } from "../transport-fUY3alfO.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-
|
|
1
|
+
import { r as Transport } from "../transport-C5wU0UtA.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-pOt5KxJB.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-Br6e0Rx5.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-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.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>> {
|
package/dist/domain/bulk.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-
|
|
1
|
+
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
|
|
2
2
|
import { KeyValueStorage } from "../ports/storage.cjs";
|
|
3
|
-
import { t as Connection } from "../transport-
|
|
3
|
+
import { t as Connection } from "../transport-fUY3alfO.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. */
|
package/dist/domain/bulk.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-
|
|
1
|
+
import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
|
|
2
2
|
import { KeyValueStorage } from "../ports/storage.mjs";
|
|
3
|
-
import { t as Connection } from "../transport-
|
|
3
|
+
import { t as Connection } from "../transport-C5wU0UtA.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-pOt5KxJB.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-
|
|
5
|
+
import { a as RevocationCheck } from "../tokens-BtczHfrc.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-Br6e0Rx5.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-
|
|
5
|
+
import { a as RevocationCheck } from "../tokens-Hnq9oURj.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-D2Ue2nd2.cjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-pOt5KxJB.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-
|
|
2
|
-
import { t as IdentityPort } from "../identity-
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-D2Ue2nd2.mjs";
|
|
2
|
+
import { t as IdentityPort } from "../identity-Br6e0Rx5.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-
|
|
1
|
+
import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-D2Ue2nd2.cjs";
|
|
2
2
|
import { KeyValueStorage } from "../ports/storage.cjs";
|
|
3
|
-
import { t as IdentityPort } from "../identity-
|
|
3
|
+
import { t as IdentityPort } from "../identity-pOt5KxJB.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-
|
|
1
|
+
import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-D2Ue2nd2.mjs";
|
|
2
2
|
import { KeyValueStorage } from "../ports/storage.mjs";
|
|
3
|
-
import { t as IdentityPort } from "../identity-
|
|
3
|
+
import { t as IdentityPort } from "../identity-Br6e0Rx5.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-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { D as DeviceId } from "../protocol-D2Ue2nd2.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;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_domain_device_id = require("./device-id.cjs");
|
|
3
|
+
//#region src/domain/direct-manage-request.ts
|
|
4
|
+
/** 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. */
|
|
5
|
+
async function acceptDirectManageRequest(connection) {
|
|
6
|
+
const result = await connection.receive()[Symbol.asyncIterator]().next();
|
|
7
|
+
if (result.done === true || result.value.type !== "manage-request") return null;
|
|
8
|
+
const frame = result.value;
|
|
9
|
+
return {
|
|
10
|
+
requestId: frame["request-id"],
|
|
11
|
+
command: frame.command,
|
|
12
|
+
scope: frame.scope,
|
|
13
|
+
...frame.token !== void 0 ? { token: frame.token } : {},
|
|
14
|
+
respond: async (outcome) => {
|
|
15
|
+
const response = {
|
|
16
|
+
type: "manage-response",
|
|
17
|
+
"request-id": frame["request-id"],
|
|
18
|
+
outcome
|
|
19
|
+
};
|
|
20
|
+
await connection.send(response);
|
|
21
|
+
await connection.close();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function waitForDirectManageResponse(link, requestId) {
|
|
26
|
+
for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
|
|
27
|
+
throw new Error("connection closed before a response arrived");
|
|
28
|
+
}
|
|
29
|
+
/** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
|
|
30
|
+
async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
|
|
31
|
+
const link = await transport.connect(address);
|
|
32
|
+
if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && require_domain_device_id.deviceIdToHex(link.peerDeviceId) !== require_domain_device_id.deviceIdToHex(options.expectedPeerDeviceId)) {
|
|
33
|
+
await link.close();
|
|
34
|
+
throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
|
|
35
|
+
}
|
|
36
|
+
const requestId = 0;
|
|
37
|
+
const frame = {
|
|
38
|
+
type: "manage-request",
|
|
39
|
+
"request-id": requestId,
|
|
40
|
+
command,
|
|
41
|
+
scope,
|
|
42
|
+
...options.token !== void 0 ? { token: options.token } : {}
|
|
43
|
+
};
|
|
44
|
+
await link.send(frame);
|
|
45
|
+
const responsePromise = waitForDirectManageResponse(link, requestId);
|
|
46
|
+
let outcome;
|
|
47
|
+
if (options.timeoutMs === void 0) outcome = await responsePromise;
|
|
48
|
+
else {
|
|
49
|
+
outcome = await Promise.race([responsePromise, new Promise((resolve) => {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
resolve({
|
|
52
|
+
result: "error",
|
|
53
|
+
code: "timeout"
|
|
54
|
+
});
|
|
55
|
+
}, options.timeoutMs);
|
|
56
|
+
})]);
|
|
57
|
+
responsePromise.catch(() => void 0);
|
|
58
|
+
}
|
|
59
|
+
await link.close();
|
|
60
|
+
return outcome;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
exports.acceptDirectManageRequest = acceptDirectManageRequest;
|
|
64
|
+
exports.sendDirectManageRequest = sendDirectManageRequest;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
|
|
2
|
+
import { r as Transport, t as Connection } from "../transport-fUY3alfO.cjs";
|
|
3
|
+
import { IncomingManageRequest, ManageOutcome } from "./mesh-session.cjs";
|
|
4
|
+
//#region src/domain/direct-manage-request.d.ts
|
|
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. */
|
|
6
|
+
export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
|
|
7
|
+
export interface DirectManageRequestOptions {
|
|
8
|
+
/** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
|
|
9
|
+
expectedPeerDeviceId?: DeviceId;
|
|
10
|
+
token?: CapabilityToken;
|
|
11
|
+
/** Omit to wait indefinitely, matching sendManageRequest's own default. */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
|
|
15
|
+
export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
|
|
16
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
|
|
2
|
+
import { r as Transport, t as Connection } from "../transport-C5wU0UtA.mjs";
|
|
3
|
+
import { IncomingManageRequest, ManageOutcome } from "./mesh-session.mjs";
|
|
4
|
+
//#region src/domain/direct-manage-request.d.ts
|
|
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. */
|
|
6
|
+
export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
|
|
7
|
+
export interface DirectManageRequestOptions {
|
|
8
|
+
/** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
|
|
9
|
+
expectedPeerDeviceId?: DeviceId;
|
|
10
|
+
token?: CapabilityToken;
|
|
11
|
+
/** Omit to wait indefinitely, matching sendManageRequest's own default. */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
/** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
|
|
15
|
+
export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
|
|
16
|
+
//#endregion
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { deviceIdToHex } from "./device-id.mjs";
|
|
2
|
+
//#region src/domain/direct-manage-request.ts
|
|
3
|
+
/** 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. */
|
|
4
|
+
async function acceptDirectManageRequest(connection) {
|
|
5
|
+
const result = await connection.receive()[Symbol.asyncIterator]().next();
|
|
6
|
+
if (result.done === true || result.value.type !== "manage-request") return null;
|
|
7
|
+
const frame = result.value;
|
|
8
|
+
return {
|
|
9
|
+
requestId: frame["request-id"],
|
|
10
|
+
command: frame.command,
|
|
11
|
+
scope: frame.scope,
|
|
12
|
+
...frame.token !== void 0 ? { token: frame.token } : {},
|
|
13
|
+
respond: async (outcome) => {
|
|
14
|
+
const response = {
|
|
15
|
+
type: "manage-response",
|
|
16
|
+
"request-id": frame["request-id"],
|
|
17
|
+
outcome
|
|
18
|
+
};
|
|
19
|
+
await connection.send(response);
|
|
20
|
+
await connection.close();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function waitForDirectManageResponse(link, requestId) {
|
|
25
|
+
for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
|
|
26
|
+
throw new Error("connection closed before a response arrived");
|
|
27
|
+
}
|
|
28
|
+
/** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
|
|
29
|
+
async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
|
|
30
|
+
const link = await transport.connect(address);
|
|
31
|
+
if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && deviceIdToHex(link.peerDeviceId) !== deviceIdToHex(options.expectedPeerDeviceId)) {
|
|
32
|
+
await link.close();
|
|
33
|
+
throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
|
|
34
|
+
}
|
|
35
|
+
const requestId = 0;
|
|
36
|
+
const frame = {
|
|
37
|
+
type: "manage-request",
|
|
38
|
+
"request-id": requestId,
|
|
39
|
+
command,
|
|
40
|
+
scope,
|
|
41
|
+
...options.token !== void 0 ? { token: options.token } : {}
|
|
42
|
+
};
|
|
43
|
+
await link.send(frame);
|
|
44
|
+
const responsePromise = waitForDirectManageResponse(link, requestId);
|
|
45
|
+
let outcome;
|
|
46
|
+
if (options.timeoutMs === void 0) outcome = await responsePromise;
|
|
47
|
+
else {
|
|
48
|
+
outcome = await Promise.race([responsePromise, new Promise((resolve) => {
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
resolve({
|
|
51
|
+
result: "error",
|
|
52
|
+
code: "timeout"
|
|
53
|
+
});
|
|
54
|
+
}, options.timeoutMs);
|
|
55
|
+
})]);
|
|
56
|
+
responsePromise.catch(() => void 0);
|
|
57
|
+
}
|
|
58
|
+
await link.close();
|
|
59
|
+
return outcome;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { acceptDirectManageRequest, sendDirectManageRequest };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DeviceId, et as PeerAdvert } from "../protocol-
|
|
1
|
+
import { D as DeviceId, et as PeerAdvert } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { D as DeviceId, et as PeerAdvert } from "../protocol-D2Ue2nd2.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
|
-
import { D as DeviceId, f as CapabilityScope } from "../protocol-
|
|
1
|
+
import { D as DeviceId, f as CapabilityScope } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { D as DeviceId, f as CapabilityScope } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-D2Ue2nd2.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-
|
|
1
|
+
import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-D2Ue2nd2.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;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_rolldown_runtime = require("../rolldown-runtime-VH7oDXx4.cjs");
|
|
2
3
|
const require_domain_device_id = require("./device-id.cjs");
|
|
3
4
|
const require_domain_handshake = require("./handshake.cjs");
|
|
4
5
|
const require_adapters_frame_codec = require("../adapters/frame-codec.cjs");
|
|
6
|
+
let package_json = require("../../package.json");
|
|
7
|
+
package_json = require_rolldown_runtime.__toESM(package_json, 1);
|
|
5
8
|
//#region src/domain/relay-pairing.ts
|
|
6
9
|
function createRelayPairings() {
|
|
7
10
|
const established = /* @__PURE__ */ new Map();
|
|
@@ -49,6 +52,24 @@ function createPingRoundTrips() {
|
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
//#endregion
|
|
55
|
+
//#region src/domain/own-version.ts
|
|
56
|
+
/** This node's own actual, currently-running wire-mesh-core version -- read once from the installed package's own package.json, never from anything gossiped or cached, so buildSelfAdvert's own self-advert extension and version.get's manage-response both always answer with the truth for this exact running process. */
|
|
57
|
+
const OWN_VERSION = package_json.default.version;
|
|
58
|
+
/** The gossip extension key wire-mesh-core itself injects into every self-advert (mesh-session.ts's own buildSelfAdvert) to report OWN_VERSION -- reserved so a sendGossipUpdate caller can never also supply it, separately from peer-advert's own typed struct fields (RESERVED_PEER_ADVERT_KEYS in mesh-session.ts), since this is just an extension-tail key (transport.cddl), not a struct field. */
|
|
59
|
+
const CORE_VERSION_GOSSIP_KEY = "wire-mesh/version";
|
|
60
|
+
const VERSION_GET_VERB = "version.get";
|
|
61
|
+
/** True when params is the core/version domain's own version-get command (version.cddl) -- a receiver answers this unconditionally, bypassing incomingManageRequests entirely, since it is deliberately ungated: exactly as sensitive as what CORE_VERSION_GOSSIP_KEY already broadcasts unconditionally via gossip. */
|
|
62
|
+
function isVersionGetCommand(params) {
|
|
63
|
+
return "verb" in params && params.verb === VERSION_GET_VERB;
|
|
64
|
+
}
|
|
65
|
+
/** The manage-ok outcome mesh-session.ts answers a version.get request with -- OWN_VERSION on manage-ok's own open `* tstr => any` tail, the same pattern exec-list's own exec-session-info result already uses. */
|
|
66
|
+
function versionGetOutcome() {
|
|
67
|
+
return {
|
|
68
|
+
result: "ok",
|
|
69
|
+
version: OWN_VERSION
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
52
73
|
//#region src/domain/gossip-extensions.ts
|
|
53
74
|
/** peer-advert's own three typed fields -- reserved so a `sendGossipUpdate` caller can never override the session's own device-id, address list, or freshness timestamp by supplying an extension of the same name. */
|
|
54
75
|
const RESERVED_PEER_ADVERT_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -58,10 +79,11 @@ const RESERVED_PEER_ADVERT_KEYS = /* @__PURE__ */ new Set([
|
|
|
58
79
|
]);
|
|
59
80
|
/** A gossip extension key must be domain-qualified as `<domain>/<field>` (lowercase kebab-case each side), per `spec/CONVENTIONS.md`'s gossip-extension-namespacing convention -- this is what stops two independent applications sharing one gossip tail from silently colliding on a bare name like "status". */
|
|
60
81
|
const GOSSIP_EXTENSION_KEY_PATTERN = /^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/;
|
|
61
|
-
/** Rejects a `sendGossipUpdate` extension bag that would either shadow one of peer-advert's own mandatory fields or use a bare, non-domain-qualified key --
|
|
82
|
+
/** Rejects a `sendGossipUpdate` extension bag that would either shadow one of peer-advert's own mandatory fields, use the key wire-mesh-core itself reserves for its own self-reported version, or use a bare, non-domain-qualified key -- all are caller bugs that must fail loudly at the call site, not silently corrupt or ambiguously merge into the wire frame. */
|
|
62
83
|
function validateGossipExtensions(extensions) {
|
|
63
84
|
for (const key of Object.keys(extensions)) {
|
|
64
85
|
if (RESERVED_PEER_ADVERT_KEYS.has(key)) throw new Error(`sendGossipUpdate extension key "${key}" collides with a mandatory peer-advert field`);
|
|
86
|
+
if (key === "wire-mesh/version") throw new Error(`sendGossipUpdate extension key "${key}" is reserved for wire-mesh-core's own self-reported version`);
|
|
65
87
|
if (!GOSSIP_EXTENSION_KEY_PATTERN.test(key)) throw new Error(`sendGossipUpdate extension key "${key}" must be domain-qualified as "<domain>/<field>" (e.g. "presence/status")`);
|
|
66
88
|
}
|
|
67
89
|
}
|
|
@@ -162,12 +184,12 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
|
|
|
162
184
|
pending.resolve(frame.outcome);
|
|
163
185
|
}
|
|
164
186
|
}
|
|
165
|
-
/** relayFrame is present only for a manage-request that arrived wrapped in relay-data, and is that same outer relay-data-frame -- its own to-device/from-device fields carry whatever addressing it received. See IncomingManageRequest's own fromDevice/toDevice doc comments for what each means and why neither is ever guessed from pairing state. */
|
|
187
|
+
/** relayFrame is present only for a manage-request that arrived wrapped in relay-data, and is that same outer relay-data-frame -- its own to-device/from-device fields carry whatever addressing it received. See IncomingManageRequest's own fromDevice/toDevice doc comments for what each means and why neither is ever guessed from pairing state. A version.get request (VERSION_GET_VERB) is answered directly, right here, rather than ever reaching incomingManageRequests -- deliberately ungated (version.cddl), so no application needs to remember to register its own handler for it the way every other verb requires. */
|
|
166
188
|
function applyManageRequest(frame, relayFrame) {
|
|
167
189
|
const requestId = frame["request-id"];
|
|
168
190
|
const fromDevice = relayFrame?.["from-device"];
|
|
169
191
|
const toDevice = relayFrame?.["to-device"];
|
|
170
|
-
|
|
192
|
+
const incoming = {
|
|
171
193
|
requestId,
|
|
172
194
|
command: frame.command,
|
|
173
195
|
scope: frame.scope,
|
|
@@ -187,7 +209,12 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
|
|
|
187
209
|
await transmit(response, relayFrame !== void 0, fromDevice);
|
|
188
210
|
emit();
|
|
189
211
|
}
|
|
190
|
-
}
|
|
212
|
+
};
|
|
213
|
+
if (isVersionGetCommand(frame.command.params)) {
|
|
214
|
+
incoming.respond(versionGetOutcome());
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
emitIncomingManageRequest(incoming);
|
|
191
218
|
}
|
|
192
219
|
function applyFrame(frame) {
|
|
193
220
|
if (frame.type === "relay-data") {
|
|
@@ -301,13 +328,14 @@ function createSessionCore(identity, clock, reconnect, dial, onPeerAdvert, addre
|
|
|
301
328
|
onSessionEnd?.(link);
|
|
302
329
|
if (state.status === "connected") handleDisconnect("node closed the connection", address, localDomains);
|
|
303
330
|
}
|
|
304
|
-
/** 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 are spread before the
|
|
331
|
+
/** 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 and CORE_VERSION_GOSSIP_KEY are 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 both collisions loudly, but the field order is kept safe in its own right rather than relying solely on the guard staying in sync. */
|
|
305
332
|
function buildSelfAdvert(extensions) {
|
|
306
333
|
if (extensions !== void 0) validateGossipExtensions(extensions);
|
|
307
334
|
return {
|
|
308
335
|
type: "gossip",
|
|
309
336
|
peers: [{
|
|
310
337
|
...extensions,
|
|
338
|
+
[CORE_VERSION_GOSSIP_KEY]: OWN_VERSION,
|
|
311
339
|
device: identity.deviceId,
|
|
312
340
|
addresses: [...addresses],
|
|
313
341
|
"snapshot-seconds": Math.floor(clock.now() / MS_PER_SECOND)
|
|
@@ -555,67 +583,7 @@ async function acceptMeshSession(connection, identity, localDomains, options = {
|
|
|
555
583
|
peerDeviceId
|
|
556
584
|
};
|
|
557
585
|
}
|
|
558
|
-
/** 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 applyManageRequest/applyFrame above). 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. */
|
|
559
|
-
async function acceptDirectManageRequest(connection) {
|
|
560
|
-
const result = await connection.receive()[Symbol.asyncIterator]().next();
|
|
561
|
-
if (result.done === true || result.value.type !== "manage-request") return null;
|
|
562
|
-
const frame = result.value;
|
|
563
|
-
return {
|
|
564
|
-
requestId: frame["request-id"],
|
|
565
|
-
command: frame.command,
|
|
566
|
-
scope: frame.scope,
|
|
567
|
-
...frame.token !== void 0 ? { token: frame.token } : {},
|
|
568
|
-
respond: async (outcome) => {
|
|
569
|
-
const response = {
|
|
570
|
-
type: "manage-response",
|
|
571
|
-
"request-id": frame["request-id"],
|
|
572
|
-
outcome
|
|
573
|
-
};
|
|
574
|
-
await connection.send(response);
|
|
575
|
-
await connection.close();
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
async function waitForDirectManageResponse(link, requestId) {
|
|
580
|
-
for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
|
|
581
|
-
throw new Error("connection closed before a response arrived");
|
|
582
|
-
}
|
|
583
|
-
/** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
|
|
584
|
-
async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
|
|
585
|
-
const link = await transport.connect(address);
|
|
586
|
-
if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && require_domain_device_id.deviceIdToHex(link.peerDeviceId) !== require_domain_device_id.deviceIdToHex(options.expectedPeerDeviceId)) {
|
|
587
|
-
await link.close();
|
|
588
|
-
throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
|
|
589
|
-
}
|
|
590
|
-
const requestId = 0;
|
|
591
|
-
const frame = {
|
|
592
|
-
type: "manage-request",
|
|
593
|
-
"request-id": requestId,
|
|
594
|
-
command,
|
|
595
|
-
scope,
|
|
596
|
-
...options.token !== void 0 ? { token: options.token } : {}
|
|
597
|
-
};
|
|
598
|
-
await link.send(frame);
|
|
599
|
-
const responsePromise = waitForDirectManageResponse(link, requestId);
|
|
600
|
-
let outcome;
|
|
601
|
-
if (options.timeoutMs === void 0) outcome = await responsePromise;
|
|
602
|
-
else {
|
|
603
|
-
outcome = await Promise.race([responsePromise, new Promise((resolve) => {
|
|
604
|
-
setTimeout(() => {
|
|
605
|
-
resolve({
|
|
606
|
-
result: "error",
|
|
607
|
-
code: "timeout"
|
|
608
|
-
});
|
|
609
|
-
}, options.timeoutMs);
|
|
610
|
-
})]);
|
|
611
|
-
responsePromise.catch(() => void 0);
|
|
612
|
-
}
|
|
613
|
-
await link.close();
|
|
614
|
-
return outcome;
|
|
615
|
-
}
|
|
616
586
|
//#endregion
|
|
617
587
|
exports.HANDSHAKE_TIMEOUT_MS = HANDSHAKE_TIMEOUT_MS;
|
|
618
|
-
exports.acceptDirectManageRequest = acceptDirectManageRequest;
|
|
619
588
|
exports.acceptMeshSession = acceptMeshSession;
|
|
620
589
|
exports.createMeshSession = createMeshSession;
|
|
621
|
-
exports.sendDirectManageRequest = sendDirectManageRequest;
|