agent-comms 4.4.0 → 4.5.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -1
- package/dist/core/agent-registry.d.ts.map +1 -1
- package/dist/core/agent-registry.js +2 -0
- package/dist/core/agent-registry.js.map +1 -1
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +1 -0
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/device-membership-verification.d.ts +2 -1
- package/dist/core/device-membership-verification.d.ts.map +1 -1
- package/dist/core/device-membership-verification.js +1 -1
- package/dist/core/device-membership-verification.js.map +1 -1
- package/dist/core/device-membership.d.ts +2 -0
- package/dist/core/device-membership.d.ts.map +1 -1
- package/dist/core/device-membership.js +9 -5
- package/dist/core/device-membership.js.map +1 -1
- package/dist/core/directory-admission.d.ts +20 -0
- package/dist/core/directory-admission.d.ts.map +1 -0
- package/dist/core/directory-admission.js +88 -0
- package/dist/core/directory-admission.js.map +1 -0
- package/dist/core/gateway-trust-actions.d.ts +1 -1
- package/dist/core/gateway-trust-actions.d.ts.map +1 -1
- package/dist/core/gateway-trust-actions.js +5 -1
- package/dist/core/gateway-trust-actions.js.map +1 -1
- package/dist/core/gateway-trust.d.ts +14 -2
- package/dist/core/gateway-trust.d.ts.map +1 -1
- package/dist/core/gateway-trust.js +40 -2
- package/dist/core/gateway-trust.js.map +1 -1
- package/dist/core/gossip-directory.d.ts +2 -0
- package/dist/core/gossip-directory.d.ts.map +1 -1
- package/dist/core/gossip-directory.js +9 -0
- package/dist/core/gossip-directory.js.map +1 -1
- package/dist/core/gossip-extensions.d.ts +2 -0
- package/dist/core/gossip-extensions.d.ts.map +1 -1
- package/dist/core/hub-session.d.ts +6 -9
- package/dist/core/hub-session.d.ts.map +1 -1
- package/dist/core/hub-session.js +18 -41
- package/dist/core/hub-session.js.map +1 -1
- package/dist/core/membership-proof.d.ts +45 -0
- package/dist/core/membership-proof.d.ts.map +1 -0
- package/dist/core/membership-proof.js +59 -0
- package/dist/core/membership-proof.js.map +1 -0
- package/dist/core/membership-proofs.d.ts +34 -0
- package/dist/core/membership-proofs.d.ts.map +1 -0
- package/dist/core/membership-proofs.js +66 -0
- package/dist/core/membership-proofs.js.map +1 -0
- package/dist/core/mesh-store.d.ts +8 -0
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +18 -3
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/tool.d.ts +5 -0
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js.map +1 -1
- package/dist/core/wire-mesh-transport-options.d.ts +12 -0
- package/dist/core/wire-mesh-transport-options.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.d.ts +1 -1
- package/dist/core/wire-mesh-transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.js +5 -5
- package/dist/core/wire-mesh-transport.js.map +1 -1
- package/dist/test/directory-admission.unit.test.d.ts +2 -0
- package/dist/test/directory-admission.unit.test.d.ts.map +1 -0
- package/dist/test/directory-admission.unit.test.js +182 -0
- package/dist/test/directory-admission.unit.test.js.map +1 -0
- package/dist/test/dm-grant-tool.integration.test.js +2 -1
- package/dist/test/dm-grant-tool.integration.test.js.map +1 -1
- package/dist/test/gateway-trust-tool.integration.test.js +22 -0
- package/dist/test/gateway-trust-tool.integration.test.js.map +1 -1
- package/dist/test/gateway-trust.unit.test.js +82 -1
- package/dist/test/gateway-trust.unit.test.js.map +1 -1
- package/dist/test/hub-mode-session.integration.test.js +10 -84
- package/dist/test/hub-mode-session.integration.test.js.map +1 -1
- package/dist/test/hub-session-dispatch.unit.test.js +14 -68
- package/dist/test/hub-session-dispatch.unit.test.js.map +1 -1
- package/dist/test/membership-proof.unit.test.d.ts +2 -0
- package/dist/test/membership-proof.unit.test.d.ts.map +1 -0
- package/dist/test/membership-proof.unit.test.js +148 -0
- package/dist/test/membership-proof.unit.test.js.map +1 -0
- package/dist/test/principal-membership-trust.integration.test.d.ts +5 -0
- package/dist/test/principal-membership-trust.integration.test.d.ts.map +1 -0
- package/dist/test/principal-membership-trust.integration.test.js +169 -0
- package/dist/test/principal-membership-trust.integration.test.js.map +1 -0
- package/dist/test/test-transport.d.ts.map +1 -1
- package/dist/test/test-transport.js +1 -0
- package/dist/test/test-transport.js.map +1 -1
- package/package.json +1 -1
|
@@ -32,6 +32,15 @@ export function findPresenceAdvert(deviceIdHex, directory) {
|
|
|
32
32
|
const status = entry.advert[PRESENCE_GOSSIP_KEY];
|
|
33
33
|
return AgentStatus.is(status) ? status : undefined;
|
|
34
34
|
}
|
|
35
|
+
/** The membership proof a peer gossiped in its agent/self advert, or undefined when it carries none. The advert is self-asserted by whichever peer sent it, so this only reads it; nothing about the proof is believed until it is verified. */
|
|
36
|
+
export function readMembershipProof(advert) {
|
|
37
|
+
const self = advert[AGENT_SELF_GOSSIP_KEY];
|
|
38
|
+
if (typeof self !== "object" || self === null)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!("membership" in self))
|
|
41
|
+
return undefined;
|
|
42
|
+
return typeof self.membership === "string" ? self.membership : undefined;
|
|
43
|
+
}
|
|
35
44
|
/** Re-sends this side's own current presence status, currently-hosted rooms, self-agent identity, and package versions, together, onto every live session's gossip self-advert -- one gossip frame per tick carrying whichever facts are actually known, rather than a separate frame per fact. Split out of wire-mesh-transport.ts's own WireMeshTransport class purely to keep that file under the repo's max-lines cap, the same reason mergeKnownDevices/findPresenceAdvert above already live here rather than there. A session that fails to send (mid-disconnect, most likely -- watchForDisconnect will independently notice and clean it up) is reported via onError and skipped, not allowed to stop the tick from reaching the rest of allSessions: a periodic broadcast to N peers is N independent operations, not one atomic unit. Unlike presence/hostedRooms/selfAgentAdvert, the versions extension is never actually absent (this side's own agent-comms package version is always known), so every tick this function is called for sends at least that one fact -- there is no "nothing to report" case left to short-circuit on. The hub's own session (agent-comms#156) is gated separately from every ordinary local-peer session in allSessions: local mesh trust is a different layer (connect_request/introduce approval already gated it before it ever joined allSessions), but the hub session is a broadcast to every connected hub peer, trusted or not, and would otherwise leak this side's own presence/hosted-rooms/self-agent/versions advert onto the hub regardless of GatewayTrust -- forwardAdvertsToHub/pushHubCatchUp's own hasAny gate exists to prevent exactly this for OTHER local peers' adverts, and this side's own self-advert deserves the identical gate, not a bypass. */
|
|
36
45
|
export function readvertiseGossip(options) {
|
|
37
46
|
const { allSessions, hub, hasAnyTrustedGateway, onError, getCurrentPresence, getHostedRooms, getSelfAgentAdvert, getCcPeerVersion, } = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gossip-directory.js","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gossip-directory.js","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,5 +23,7 @@ export interface AgentSelfAdvert {
|
|
|
23
23
|
startedAt: string;
|
|
24
24
|
tags: string[];
|
|
25
25
|
subscribedRooms: string[];
|
|
26
|
+
/** A proof that this device's user principal vouches for it (membership-proof.ts), so a peer that trusts that principal trusts this device without it being listed individually. Absent until the first proof has been minted. */
|
|
27
|
+
membership?: string;
|
|
26
28
|
}
|
|
27
29
|
//# sourceMappingURL=gossip-extensions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gossip-extensions.d.ts","sourceRoot":"","sources":["../../src/core/gossip-extensions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8QAA8Q;AAC9Q,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,mSAAmS;AACnS,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,+cAA+c;AAC/c,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6RAA6R;AAC7R,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,gkBAAgkB;AAChkB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"gossip-extensions.d.ts","sourceRoot":"","sources":["../../src/core/gossip-extensions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8QAA8Q;AAC9Q,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,mSAAmS;AACnS,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,+cAA+c;AAC/c,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6RAA6R;AAC7R,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAElD,gkBAAgkB;AAChkB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,kOAAkO;IAClO,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
|
|
@@ -6,7 +6,6 @@ import type { CapabilityScope, CapabilityToken, Frame, ManageCommand } from "wir
|
|
|
6
6
|
import type { IdentityPort } from "wire-mesh-core/ports/identity";
|
|
7
7
|
import type { Connection } from "wire-mesh-core/ports/transport";
|
|
8
8
|
import type { ConnectionHandle, MeshTraceResult, TransportEvents } from "./transport.js";
|
|
9
|
-
import type { MeshMessage } from "./wire-protocol.js";
|
|
10
9
|
import type { RoomRequestOrigin } from "./room-router.js";
|
|
11
10
|
/** How long a room-domain request routed through the hub's relay-connect/relay-data pairing waits for a response before giving up. Unlike an ordinary local peer session, a relay-connect naming an unknown target-device is silently dropped by the hub (spec/relay-hub's own documented behaviour -- no error frame exists for "no such device"), so a request to a device that turns out not to be reachable via any gateway would otherwise hang forever rather than surfacing as a normal "not reachable" outcome. */
|
|
12
11
|
export interface HubSessionDeps {
|
|
@@ -27,10 +26,10 @@ export interface HubSessionDeps {
|
|
|
27
26
|
onDirectory: (entries: readonly DirectoryEntry[]) => void;
|
|
28
27
|
/** Dispatches an already-received, non-legacy-frame request (a real core/room verb: room.send, room.notify, room.join, ...) to WireMeshTransport's own roomRouter, the identical dispatch a local peer session's own drainSession already uses -- the outbound half of agent-comms#155's "remote to local" routing leg. Deferred the same lazy-`this`-capture way DeliveryEngine's own sendRoomRequestToMember closure is, since roomRouter is constructed after this class's own instance in WireMeshTransport's constructor. Declared with the identical (request, handle, origin?) shape RoomRouter.handleRequest itself has (agent-comms#216) so WireMeshTransport can keep wiring this dep as a direct method reference rather than a wrapper -- dispatchHubRequest below is what actually supplies a non-empty origin, carrying this side's own dialled hub address. */
|
|
29
28
|
handleRoomRequest: (request: IncomingManageRequest, handle: Readonly<ConnectionHandle>, origin?: Readonly<RoomRequestOrigin>) => Promise<void>;
|
|
30
|
-
/** The gateway trust boundary (agent-comms#156): whether the given device-id (hex) is currently trusted as a bare device.
|
|
29
|
+
/** The gateway trust boundary (agent-comms#156): whether the given device-id (hex) is currently trusted as a bare device. It only decides which room-domain senders dispatchHubRequest records as known hub peers; a real room-domain verb's own dispatch is never gated on it at all, see dispatchHubRequest's own doc for why that is sound. connect()'s own directory-merge filter uses admitEntries below instead, not this. */
|
|
31
30
|
isTrusted: (deviceHex: string) => boolean;
|
|
32
|
-
/** Whether
|
|
33
|
-
|
|
31
|
+
/** Whether a gossiped directory entry may be merged (agent-comms#156, widened by #187 and #266): its device is trusted by id, or is itself a trusted principal's own device (agent-comms#192), or carries a membership proof a trusted principal vouches for. A gossiped advert is self-asserted, so the proof is what makes trusting a principal cover its devices; verifying one is cryptography, hence asynchronous. */
|
|
32
|
+
admitEntries: (entries: readonly DirectoryEntry[]) => Promise<DirectoryEntry[]>;
|
|
34
33
|
/** Forwards a room-domain manage-request on to a specific LOCAL peer session (one this gateway is directly connected to over the ordinary local mesh, keyed by device-id hex) rather than dispatching it against this gateway's own local state -- consume()'s own toDevice disambiguation (agent-comms#184, wire-mesh-core 1.48.1's IncomingManageRequest.toDevice). Returns undefined when no local session exists for that device-id, in which case consume() falls back to handleRoomRequest exactly as it always has. */
|
|
35
34
|
forwardToLocalPeer: (deviceHex: string, command: ManageCommand, scope: Readonly<CapabilityScope>, token?: CapabilityToken) => Promise<ManageOutcome> | undefined;
|
|
36
35
|
}
|
|
@@ -58,8 +57,6 @@ export declare class HubSession {
|
|
|
58
57
|
/** Consumes one hub session's inbound relayed manage-requests until it ends, dispatching each in arrival order to dispatchHubRequest -- see that function's own doc for the actual per-request trust decision. Split out purely so the decision itself is directly unit-testable against fake requests/deps (hub-session-dispatch.test.ts), the same reason hub-forwarding.ts's own forwardAdvertsToHub/pushHubCatchUp are standalone functions rather than private methods. Passes this.url through as dispatchHubRequest's own hubAddress -- this side's own dialled address for the hub every request on this specific session arrived over (agent-comms#216), the answering-side equivalent of tracePath's own local.hubAddress above. */
|
|
59
58
|
private consume;
|
|
60
59
|
private watchDisconnect;
|
|
61
|
-
/** Sends one message to a hub-discovered peer through the hub's relay pairing. */
|
|
62
|
-
sendToPeer(peerDeviceHex: string, message: MeshMessage): Promise<void>;
|
|
63
60
|
/** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
|
|
64
61
|
advertiseDevices(entries: readonly DirectoryEntry[]): Promise<void>;
|
|
65
62
|
/** Sends a real core/room manage-request to a specific hub-reachable peer, through the hub's relay-connect/relay-data pairing -- the local-to-remote leg of agent-comms#155's routing, mirroring MeshTransport.sendRoomRequest's own not_connected/outcome contract so WireMeshTransport can fall back to this uniformly when memberId isn't a local peer session. Bounded by HUB_ROOM_REQUEST_TIMEOUT_MS (see its own doc) since an unreachable target-device is silently dropped by the hub with no error frame, unlike a local session's own connection-level failure. */
|
|
@@ -68,13 +65,13 @@ export declare class HubSession {
|
|
|
68
65
|
tracePath(peerDeviceHex: string, timeoutMs?: number): Promise<MeshTraceResult>;
|
|
69
66
|
}
|
|
70
67
|
/** The slice of HubSessionDeps dispatchHubRequest actually needs. Named so its own tests, and consume()'s call site, don't repeat the same Pick inline. */
|
|
71
|
-
export type HubRequestDispatchDeps = Pick<HubSessionDeps, "isTrusted" | "
|
|
68
|
+
export type HubRequestDispatchDeps = Pick<HubSessionDeps, "isTrusted" | "handleRoomRequest" | "forwardToLocalPeer">;
|
|
72
69
|
/**
|
|
73
70
|
* Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by this gateway, exactly as it already did for the legacy opaque-frame path and now also for a real room-domain verb (agent-comms#155's "remote to local" leg).
|
|
74
71
|
*
|
|
75
|
-
* A request with no fromDevice at all is refused before
|
|
72
|
+
* A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (room-router.ts's resolveHandle, then a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
|
|
76
73
|
*
|
|
77
|
-
* The legacy FRAME_VERB
|
|
74
|
+
* Only room-domain verbs are accepted over a relay at all. The legacy FRAME_VERB carriage (an opaque MeshMessage, which local peer sessions still use for mesh-state gossip) is refused here with unsupported_verb whoever sent it and whether or not that sender is trusted: a relayed sender has passed neither connect_request approval nor the coordinator's own membership, so it has no business feeding state_sync or state_update into this side's mesh state, and nothing on the relay path has any other use for a bare frame. Refusing the verb wholesale is the allow-list; there is no per-message-type list to keep in step with new MeshMessage variants (agent-comms#169, agent-comms#268).
|
|
78
75
|
*
|
|
79
76
|
* A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest/forwardToLocalPeer), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
|
|
80
77
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EACnB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,KAAK,EACL,aAAa,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"hub-session.d.ts","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
|
package/dist/core/hub-session.js
CHANGED
|
@@ -5,9 +5,8 @@ import { acceptMeshSession, } from "wire-mesh-core/domain/mesh-session";
|
|
|
5
5
|
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
6
6
|
import { tracePath as coreTracePath } from "wire-mesh-core/domain/path-trace";
|
|
7
7
|
import { ROOM_REQUEST_TIMEOUT_MS, manageRequestTimeoutMs, } from "./request-timeouts.js";
|
|
8
|
-
import { extractMessage } from "./room-router.js";
|
|
9
8
|
import { connectWsUrl } from "./ws-dial.js";
|
|
10
|
-
import {
|
|
9
|
+
import { DOMAIN, FRAME_VERB } from "./wire-mesh-transport.js";
|
|
11
10
|
export class HubSession {
|
|
12
11
|
deps;
|
|
13
12
|
session;
|
|
@@ -66,14 +65,13 @@ export class HubSession {
|
|
|
66
65
|
this.connection = connection;
|
|
67
66
|
this.url = url;
|
|
68
67
|
this.deps.trackForShutdown(session);
|
|
69
|
-
// Merge the hub's directory (its catch-up arrives as the first session events) and keep refreshing it on every subsequent one. Every trusted remote entry is also surfaced via onDirectory, so the transport can merge it into its own mesh-wide knownDevices (agent-comms#155's remote-directory-merge leg). Filtered to
|
|
68
|
+
// Merge the hub's directory (its catch-up arrives as the first session events) and keep refreshing it on every subsequent one. Every trusted remote entry is also surfaced via onDirectory, so the transport can merge it into its own mesh-wide knownDevices (agent-comms#155's remote-directory-merge leg). Filtered to admitEntries (agent-comms#156, widened by agent-comms#187's principal allowlist per agent-comms#192 and by #266's membership proofs) before either hubPeersKnown tracking or onDirectory sees it: an untrusted device's gossiped presence is not merely withheld from listAgents, it is never even recorded as "known" here, so nothing downstream can act on it via any path this class exposes.
|
|
70
69
|
void (async () => {
|
|
71
70
|
const ownHex = deviceIdToHex(identity.deviceId);
|
|
72
71
|
for await (const event of session.events) {
|
|
73
72
|
if (this.deps.isShuttingDown())
|
|
74
73
|
break;
|
|
75
|
-
const remoteEntries = event.directory.filter((entry) => deviceIdToHex(entry.device) !== ownHex
|
|
76
|
-
this.deps.isTrustedForDirectory(deviceIdToHex(entry.device)));
|
|
74
|
+
const remoteEntries = await this.deps.admitEntries(event.directory.filter((entry) => deviceIdToHex(entry.device) !== ownHex));
|
|
77
75
|
for (const entry of remoteEntries) {
|
|
78
76
|
this.hubPeersKnown.add(deviceIdToHex(entry.device));
|
|
79
77
|
}
|
|
@@ -82,7 +80,10 @@ export class HubSession {
|
|
|
82
80
|
if (event.state.status === "closed")
|
|
83
81
|
break;
|
|
84
82
|
}
|
|
85
|
-
})()
|
|
83
|
+
})().catch((error) => {
|
|
84
|
+
// The merge loop must not die silently: a failure here would stop this side learning of every remote device until the hub is redialled.
|
|
85
|
+
this.deps.events.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
86
|
+
});
|
|
86
87
|
this.consume(session, deviceIdToHex(identity.deviceId));
|
|
87
88
|
void (async () => {
|
|
88
89
|
await this.watchDisconnect(session);
|
|
@@ -117,14 +118,6 @@ export class HubSession {
|
|
|
117
118
|
this.url = undefined;
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
|
-
/** Sends one message to a hub-discovered peer through the hub's relay pairing. */
|
|
121
|
-
async sendToPeer(peerDeviceHex, message) {
|
|
122
|
-
const session = this.session;
|
|
123
|
-
if (session === undefined) {
|
|
124
|
-
throw new Error("not connected to a hub");
|
|
125
|
-
}
|
|
126
|
-
await session.sendManageRequest(buildCommand(message), FRAME_SCOPE, hexToBytes(peerDeviceHex));
|
|
127
|
-
}
|
|
128
121
|
/** Gossips a raw `gossip` frame carrying every given entry's own advert onto the hub, over the raw connection rather than sendGossipUpdate (which can only ever advertise this side's own single device) -- the outbound leg of agent-comms#155's gateway forwarding. relay-hub.ts registers each advert's own `device` field against the CONNECTION it arrives on, so every entry passed here becomes reachable, cross-machine, as "via this gateway" -- callers are responsible for only ever passing entries that are actually meant to be advertised (WireMeshTransport filters to local, visible-agent-bearing entries before calling this). Throws if this side isn't currently connected to a hub, matching sendToPeer's own contract -- callers gate on isConnected first. */
|
|
129
122
|
async advertiseDevices(entries) {
|
|
130
123
|
const connection = this.connection;
|
|
@@ -173,9 +166,9 @@ function hexToBytes(hex) {
|
|
|
173
166
|
/**
|
|
174
167
|
* Decides what to do with one inbound relayed manage-request, keeping every downstream consumer's handle keyed by the SENDING device (request.fromDevice names it on relay-routed requests) rather than by this gateway, exactly as it already did for the legacy opaque-frame path and now also for a real room-domain verb (agent-comms#155's "remote to local" leg).
|
|
175
168
|
*
|
|
176
|
-
* A request with no fromDevice at all is refused before
|
|
169
|
+
* A request with no fromDevice at all is refused before anything below sees it. A room-domain verb's own downstream handler (room-router.ts's resolveHandle, then a capability-token check such as verifyRoomToken) calls deviceIdFromHex on whatever handle.id it's given, which throws on anything that isn't valid hex rather than answering with an ordinary unauthorized outcome. Answering unauthorized here, before that, is what keeps this a clean, fast failure for the request's own caller instead of an unhandled rejection that would kill this session's whole drain loop.
|
|
177
170
|
*
|
|
178
|
-
* The legacy FRAME_VERB
|
|
171
|
+
* Only room-domain verbs are accepted over a relay at all. The legacy FRAME_VERB carriage (an opaque MeshMessage, which local peer sessions still use for mesh-state gossip) is refused here with unsupported_verb whoever sent it and whether or not that sender is trusted: a relayed sender has passed neither connect_request approval nor the coordinator's own membership, so it has no business feeding state_sync or state_update into this side's mesh state, and nothing on the relay path has any other use for a bare frame. Refusing the verb wholesale is the allow-list; there is no per-message-type list to keep in step with new MeshMessage variants (agent-comms#169, agent-comms#268).
|
|
179
172
|
*
|
|
180
173
|
* A real room-domain verb (room.send, room.join, room.notify, ...) is never gated on isTrusted at all (agent-comms#192): it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over, and already principal-aware since agent-comms#187 (device-membership-verification.ts's own chain-walk). Stacking the coarse bare-device allowlist in front of that check would be redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is a human decision) it would actively defeat the protocol's own intended design by never letting an untrusted device reach that approval step at all. onKnownPeer is still only called here for a sender isTrusted already recognises: reaching this branch has not yet had its own token verified (that happens inside handleRoomRequest/forwardToLocalPeer), so hubPeersKnown stays "gateway-trusted hub peers", never "every device that has sent a syntactically valid room-domain request".
|
|
181
174
|
*
|
|
@@ -183,32 +176,20 @@ function hexToBytes(hex) {
|
|
|
183
176
|
*/
|
|
184
177
|
export async function dispatchHubRequest(options) {
|
|
185
178
|
const { request, ownDeviceHex, deps, onKnownPeer, hubAddress } = options;
|
|
179
|
+
if (request.command.verb === FRAME_VERB) {
|
|
180
|
+
await request
|
|
181
|
+
.respond({ result: "error", code: "unsupported_verb" })
|
|
182
|
+
.catch(() => undefined);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
186
185
|
if (request.fromDevice === undefined) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
else {
|
|
191
|
-
await request
|
|
192
|
-
.respond({ result: "error", code: "unauthorized" })
|
|
193
|
-
.catch(() => undefined);
|
|
194
|
-
}
|
|
186
|
+
await request
|
|
187
|
+
.respond({ result: "error", code: "unauthorized" })
|
|
188
|
+
.catch(() => undefined);
|
|
195
189
|
return;
|
|
196
190
|
}
|
|
197
191
|
const senderHex = deviceIdToHex(request.fromDevice);
|
|
198
192
|
const handle = { id: senderHex };
|
|
199
|
-
if (request.command.verb === FRAME_VERB) {
|
|
200
|
-
if (!deps.isTrusted(senderHex)) {
|
|
201
|
-
await request.respond({ result: "ok" }).catch(() => undefined);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
onKnownPeer(senderHex);
|
|
205
|
-
const message = extractMessage(request.command);
|
|
206
|
-
if (message !== undefined && !isStateMutatingMessage(message)) {
|
|
207
|
-
deps.events.onMessage(handle, message);
|
|
208
|
-
}
|
|
209
|
-
await request.respond({ result: "ok" }).catch(() => undefined);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
193
|
if (deps.isTrusted(senderHex))
|
|
213
194
|
onKnownPeer(senderHex);
|
|
214
195
|
const toDeviceHex = request.toDevice !== undefined
|
|
@@ -231,8 +212,4 @@ export async function dispatchHubRequest(options) {
|
|
|
231
212
|
}
|
|
232
213
|
await deps.handleRoomRequest(request, handle, hubAddress !== undefined ? { relayHubAddress: hubAddress } : {});
|
|
233
214
|
}
|
|
234
|
-
/** A state_sync or state_update carries authority to directly overwrite or patch this side's own mesh state (agents, rooms, messages, deliveries) -- on an ordinary peer session that authority is meaningful because the peer already passed connect_request/introduce approval or the coordinator's own trusted mesh membership. A hub-relayed sender has passed neither, and gateway trust (agent-comms#156, dispatchHubRequest's own isTrusted gate for the legacy frame path) doesn't grant it either: being on the allowlist means "this device's traffic is worth acting on," not "this device may directly overwrite this side's mesh state" -- a strictly stronger claim no hub-relayed sender has ever been asked to prove, since the hub itself still accepts any self-generated identity with no admission control of its own (gating the hub is deliberately out of scope for agent-comms#156). Treating a trusted sender's state_sync/state_update as equally authoritative would let it inject an outcome indistinguishable from a genuine mesh event, e.g. a spoofed inbound delivery -- so this filter stays unconditional, applied even to a sender dispatchHubRequest has already let past the trust gate. Every other legacy message method this session might relay is already inert on receipt (PeerLifecycle's own handleDataMessage only reacts to these two), so filtering exactly these two is a complete fix for this specific path, not a partial one. */
|
|
235
|
-
function isStateMutatingMessage(message) {
|
|
236
|
-
return message.method === "state_sync" || message.method === "state_update";
|
|
237
|
-
}
|
|
238
215
|
//# sourceMappingURL=hub-session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,iBAAiB,GAKlB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAc9E,OAAO,EACL,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"hub-session.js","sourceRoot":"","sources":["../../src/core/hub-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The proof a device gossips to show that its user principal vouches for it (agent-comms#266): a short-lived group:member token, minted by that principal for that one device, carried as one line of text. A receiver that trusts the principal verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal is for.
|
|
3
|
+
*
|
|
4
|
+
* A proof shows that the principal vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the principal, so anyone connected to the hub can see which devices belong to which principal.
|
|
5
|
+
*
|
|
6
|
+
* Every bridge of one account shares that account's user identity, so any of them can mint a proof for any device id, which is the same trust the principal is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the account's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared user-identity.json.
|
|
7
|
+
*/
|
|
8
|
+
import type { Clock } from "wire-mesh-core/ports/clock";
|
|
9
|
+
import type { IdentityPort } from "wire-mesh-core/ports/identity";
|
|
10
|
+
import type { RevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
11
|
+
import type { DeviceId } from "wire-mesh-core/generated/protocol";
|
|
12
|
+
/** The longest proof text a receiver will look at. A real proof is a single root-level token, well under half of this; a longer one can only be something else, and the length also bounds how deep a delegation chain could be smuggled in. */
|
|
13
|
+
export declare const MAX_MEMBERSHIP_PROOF_LENGTH = 4096;
|
|
14
|
+
/** How long a minted proof stays valid. Long enough to ride out a missed refresh or a gossip gap, short enough that a device an account stops running stops being trusted soon after. */
|
|
15
|
+
export declare const MEMBERSHIP_PROOF_LIFETIME_MS: number;
|
|
16
|
+
export interface MintMembershipProofOptions {
|
|
17
|
+
/** The user principal vouching for the device. */
|
|
18
|
+
userIdentity: IdentityPort;
|
|
19
|
+
clock: Clock;
|
|
20
|
+
/** The device the proof is for, and the only device it will verify for. */
|
|
21
|
+
deviceId: DeviceId;
|
|
22
|
+
}
|
|
23
|
+
/** A fresh proof that options.userIdentity vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
|
|
24
|
+
export declare function mintMembershipProof(options: Readonly<MintMembershipProofOptions>): Promise<string>;
|
|
25
|
+
export interface VerifyMembershipProofOptions {
|
|
26
|
+
proof: string;
|
|
27
|
+
/** The device the proof is claimed for: the gossiping device the directory entry names. */
|
|
28
|
+
deviceId: DeviceId;
|
|
29
|
+
/** The principal the proof must have been minted by. */
|
|
30
|
+
principalId: DeviceId;
|
|
31
|
+
/** The verifying node's own identity, clock and revocation view. */
|
|
32
|
+
identity: IdentityPort;
|
|
33
|
+
clock: Clock;
|
|
34
|
+
revocation: RevocationView;
|
|
35
|
+
}
|
|
36
|
+
export type MembershipProofVerdict = {
|
|
37
|
+
ok: true;
|
|
38
|
+
expires: number;
|
|
39
|
+
} | {
|
|
40
|
+
ok: false;
|
|
41
|
+
reason: string;
|
|
42
|
+
};
|
|
43
|
+
/** Whether options.proof shows that options.principalId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or principal, an expired or revoked one, all come back as a refusal. */
|
|
44
|
+
export declare function verifyMembershipProof(options: Readonly<VerifyMembershipProofOptions>): Promise<MembershipProofVerdict>;
|
|
45
|
+
//# sourceMappingURL=membership-proof.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-proof.d.ts","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAUlE,gPAAgP;AAChP,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD,yLAAyL;AACzL,eAAO,MAAM,4BAA4B,QACU,CAAC;AAEpD,MAAM,WAAW,0BAA0B;IACzC,kDAAkD;IAClD,YAAY,EAAE,YAAY,CAAC;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,2EAA2E;IAC3E,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,mJAAmJ;AACnJ,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,QAAQ,CAAC,0BAA0B,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,2FAA2F;IAC3F,QAAQ,EAAE,QAAQ,CAAC;IACnB,wDAAwD;IACxD,WAAW,EAAE,QAAQ,CAAC;IACtB,oEAAoE;IACpE,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,2PAA2P;AAC3P,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,QAAQ,CAAC,4BAA4B,CAAC,GAC9C,OAAO,CAAC,sBAAsB,CAAC,CAqBjC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The proof a device gossips to show that its user principal vouches for it (agent-comms#266): a short-lived group:member token, minted by that principal for that one device, carried as one line of text. A receiver that trusts the principal verifies it against the gossiping device and then trusts the device without it being listed individually, which is what trusting a principal is for.
|
|
3
|
+
*
|
|
4
|
+
* A proof shows that the principal vouches for a device id. It does not show that whoever gossips it is that device: both arrive in the same unauthenticated advert and anyone who has read one can repeat it, so it earns only what GatewayTrust.isReachable grants (see directory-admission.ts). It is also public: it names the principal, so anyone connected to the hub can see which devices belong to which principal.
|
|
5
|
+
*
|
|
6
|
+
* Every bridge of one account shares that account's user identity, so any of them can mint a proof for any device id, which is the same trust the principal is given anyway. Proofs are short-lived rather than revocable, so removeDevice (which revokes an admitDevice grant) does not affect them and a removed device holding the account's key can keep minting its own: they are minted without recording an issued-grant, so a bridge refreshing its own does not rewrite the shared user-identity.json.
|
|
7
|
+
*/
|
|
8
|
+
import { mintDeviceMembership } from "./device-membership.js";
|
|
9
|
+
import { verifyDeviceMembership } from "./device-membership-verification.js";
|
|
10
|
+
import { randomId } from "./random-id.js";
|
|
11
|
+
import { CommsError } from "./store.js";
|
|
12
|
+
import { decodeTokenText, encodeTokenText } from "./token-text.js";
|
|
13
|
+
const MS_PER_MINUTE = 60_000;
|
|
14
|
+
const MEMBERSHIP_PROOF_LIFETIME_MINUTES = 15;
|
|
15
|
+
/** The longest proof text a receiver will look at. A real proof is a single root-level token, well under half of this; a longer one can only be something else, and the length also bounds how deep a delegation chain could be smuggled in. */
|
|
16
|
+
export const MAX_MEMBERSHIP_PROOF_LENGTH = 4096;
|
|
17
|
+
/** How long a minted proof stays valid. Long enough to ride out a missed refresh or a gossip gap, short enough that a device an account stops running stops being trusted soon after. */
|
|
18
|
+
export const MEMBERSHIP_PROOF_LIFETIME_MS = MEMBERSHIP_PROOF_LIFETIME_MINUTES * MS_PER_MINUTE;
|
|
19
|
+
/** A fresh proof that options.userIdentity vouches for options.deviceId, as one line of text. Throws MINT_FAILED if the token cannot be minted. */
|
|
20
|
+
export async function mintMembershipProof(options) {
|
|
21
|
+
const verdict = await mintDeviceMembership({
|
|
22
|
+
userIdentity: options.userIdentity,
|
|
23
|
+
clock: options.clock,
|
|
24
|
+
tokenId: randomId(),
|
|
25
|
+
deviceId: options.deviceId,
|
|
26
|
+
expires: options.clock.now() + MEMBERSHIP_PROOF_LIFETIME_MS,
|
|
27
|
+
});
|
|
28
|
+
if (!verdict.ok) {
|
|
29
|
+
throw new CommsError(`Failed to mint a membership proof: ${verdict.reason}`, "MINT_FAILED");
|
|
30
|
+
}
|
|
31
|
+
return encodeTokenText(verdict.token);
|
|
32
|
+
}
|
|
33
|
+
/** Whether options.proof shows that options.principalId vouches for options.deviceId right now. Never throws for a bad proof: text that is not a token, a token for another device or principal, an expired or revoked one, all come back as a refusal. */
|
|
34
|
+
export async function verifyMembershipProof(options) {
|
|
35
|
+
if (options.proof.length > MAX_MEMBERSHIP_PROOF_LENGTH) {
|
|
36
|
+
return { ok: false, reason: "too_long" };
|
|
37
|
+
}
|
|
38
|
+
let token;
|
|
39
|
+
try {
|
|
40
|
+
token = decodeTokenText(options.proof);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { ok: false, reason: "not_a_token" };
|
|
44
|
+
}
|
|
45
|
+
const verdict = await verifyDeviceMembership(token, {
|
|
46
|
+
identity: options.identity,
|
|
47
|
+
clock: options.clock,
|
|
48
|
+
revocation: options.revocation,
|
|
49
|
+
expectedBearer: options.deviceId,
|
|
50
|
+
userDeviceId: options.principalId,
|
|
51
|
+
});
|
|
52
|
+
if (!verdict.ok)
|
|
53
|
+
return { ok: false, reason: verdict.reason };
|
|
54
|
+
// Every proof is minted at the root and cannot be delegated; a chain here would be someone else's construction.
|
|
55
|
+
if (verdict.depth !== 0)
|
|
56
|
+
return { ok: false, reason: "not_root_level" };
|
|
57
|
+
return { ok: true, expires: verdict.claims.expires };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=membership-proof.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-proof.js","sourceRoot":"","sources":["../../src/core/membership-proof.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAE7C,gPAAgP;AAChP,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,yLAAyL;AACzL,MAAM,CAAC,MAAM,4BAA4B,GACvC,iCAAiC,GAAG,aAAa,CAAC;AAUpD,mJAAmJ;AACnJ,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,OAA6C;IAE7C,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC;QACzC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,QAAQ,EAAE;QACnB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,4BAA4B;KAC5D,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,UAAU,CAClB,sCAAsC,OAAO,CAAC,MAAM,EAAE,EACtD,aAAa,CACd,CAAC;IACJ,CAAC;IACD,OAAO,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC;AAiBD,2PAA2P;AAC3P,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA+C;IAE/C,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,2BAA2B,EAAE,CAAC;QACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAC9C,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,KAAK,EAAE;QAClD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,cAAc,EAAE,OAAO,CAAC,QAAQ;QAChC,YAAY,EAAE,OAAO,CAAC,WAAW;KAClC,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC9D,gHAAgH;IAChH,IAAI,OAAO,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACxE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A device's own membership proof, kept fresh, and the check for other devices' proofs (agent-comms#266). Split out of MeshStore, which hands it its identity, peer id and error sink.
|
|
3
|
+
*/
|
|
4
|
+
import type { MeshStoreIdentity } from "./mesh-store-shared.js";
|
|
5
|
+
import { type MembershipProofVerdict } from "./membership-proof.js";
|
|
6
|
+
export interface MembershipProofsDeps {
|
|
7
|
+
/** Throws if the store has no identity yet. */
|
|
8
|
+
getIdentity: () => MeshStoreIdentity;
|
|
9
|
+
/** This device's own id (hex). */
|
|
10
|
+
getPeerId: () => string;
|
|
11
|
+
onError: (error: Error) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare class MembershipProofs {
|
|
14
|
+
private readonly deps;
|
|
15
|
+
private proof;
|
|
16
|
+
private minting;
|
|
17
|
+
private timer;
|
|
18
|
+
constructor(deps: Readonly<MembershipProofsDeps>);
|
|
19
|
+
/** The agent/self advert field carrying this device's current proof, empty until the first has been minted. */
|
|
20
|
+
advertField(): {
|
|
21
|
+
membership?: string;
|
|
22
|
+
};
|
|
23
|
+
/** Mints a proof now and again on an interval. A failed mint is reported and retried on the next tick; the previous proof stays until it lapses. */
|
|
24
|
+
start(): void;
|
|
25
|
+
stop(): void;
|
|
26
|
+
/** Whether a gossiped proof shows that principalHex vouches for deviceHex, as this node sees it: its own identity, clock and revocation view. */
|
|
27
|
+
verify(claim: Readonly<{
|
|
28
|
+
proof: string;
|
|
29
|
+
deviceHex: string;
|
|
30
|
+
principalHex: string;
|
|
31
|
+
}>): Promise<MembershipProofVerdict>;
|
|
32
|
+
private refresh;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=membership-proofs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-proofs.d.ts","sourceRoot":"","sources":["../../src/core/membership-proofs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,WAAW,EAAE,MAAM,iBAAiB,CAAC;IACrC,kCAAkC;IAClC,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAED,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAA6C;gBAE7B,IAAI,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IAEjE,+GAA+G;IAC/G,WAAW,IAAI;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAItC,oJAAoJ;IACpJ,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,IAAI;IAKZ,iJAAiJ;IAC3I,MAAM,CACV,KAAK,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,GAC1E,OAAO,CAAC,sBAAsB,CAAC;IAYlC,OAAO,CAAC,OAAO;CAuBhB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A device's own membership proof, kept fresh, and the check for other devices' proofs (agent-comms#266). Split out of MeshStore, which hands it its identity, peer id and error sink.
|
|
3
|
+
*/
|
|
4
|
+
import { deviceIdFromHex } from "wire-mesh-core/domain/device-id";
|
|
5
|
+
import { MEMBERSHIP_PROOF_LIFETIME_MS, mintMembershipProof, verifyMembershipProof, } from "./membership-proof.js";
|
|
6
|
+
/** How many times per proof lifetime the proof is re-minted, so a missed tick or two never lets the advertised proof lapse. */
|
|
7
|
+
const REFRESHES_PER_LIFETIME = 3;
|
|
8
|
+
const REFRESH_INTERVAL_MS = MEMBERSHIP_PROOF_LIFETIME_MS / REFRESHES_PER_LIFETIME;
|
|
9
|
+
export class MembershipProofs {
|
|
10
|
+
deps;
|
|
11
|
+
proof;
|
|
12
|
+
minting = false;
|
|
13
|
+
timer;
|
|
14
|
+
constructor(deps) {
|
|
15
|
+
this.deps = deps;
|
|
16
|
+
}
|
|
17
|
+
/** The agent/self advert field carrying this device's current proof, empty until the first has been minted. */
|
|
18
|
+
advertField() {
|
|
19
|
+
return this.proof === undefined ? {} : { membership: this.proof };
|
|
20
|
+
}
|
|
21
|
+
/** Mints a proof now and again on an interval. A failed mint is reported and retried on the next tick; the previous proof stays until it lapses. */
|
|
22
|
+
start() {
|
|
23
|
+
this.stop();
|
|
24
|
+
this.refresh();
|
|
25
|
+
this.timer = setInterval(() => {
|
|
26
|
+
this.refresh();
|
|
27
|
+
}, REFRESH_INTERVAL_MS);
|
|
28
|
+
this.timer.unref();
|
|
29
|
+
}
|
|
30
|
+
stop() {
|
|
31
|
+
clearInterval(this.timer);
|
|
32
|
+
this.timer = undefined;
|
|
33
|
+
}
|
|
34
|
+
/** Whether a gossiped proof shows that principalHex vouches for deviceHex, as this node sees it: its own identity, clock and revocation view. */
|
|
35
|
+
async verify(claim) {
|
|
36
|
+
const { identity, clock, revocation } = this.deps.getIdentity();
|
|
37
|
+
return verifyMembershipProof({
|
|
38
|
+
proof: claim.proof,
|
|
39
|
+
deviceId: deviceIdFromHex(claim.deviceHex),
|
|
40
|
+
principalId: deviceIdFromHex(claim.principalHex),
|
|
41
|
+
identity,
|
|
42
|
+
clock,
|
|
43
|
+
revocation,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
refresh() {
|
|
47
|
+
if (this.minting)
|
|
48
|
+
return;
|
|
49
|
+
this.minting = true;
|
|
50
|
+
const { userIdentity, clock } = this.deps.getIdentity();
|
|
51
|
+
mintMembershipProof({
|
|
52
|
+
userIdentity,
|
|
53
|
+
clock,
|
|
54
|
+
deviceId: deviceIdFromHex(this.deps.getPeerId()),
|
|
55
|
+
})
|
|
56
|
+
.then((proof) => {
|
|
57
|
+
this.proof = proof;
|
|
58
|
+
}, (error) => {
|
|
59
|
+
this.deps.onError(error instanceof Error ? error : new Error(String(error)));
|
|
60
|
+
})
|
|
61
|
+
.finally(() => {
|
|
62
|
+
this.minting = false;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=membership-proofs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership-proofs.js","sourceRoot":"","sources":["../../src/core/membership-proofs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,EACnB,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC;AAE/B,+HAA+H;AAC/H,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,mBAAmB,GACvB,4BAA4B,GAAG,sBAAsB,CAAC;AAUxD,MAAM,OAAO,gBAAgB;IAKE;IAJrB,KAAK,CAAqB;IAC1B,OAAO,GAAG,KAAK,CAAC;IAChB,KAAK,CAA6C;IAE1D,YAA6B,IAAoC;QAApC,SAAI,GAAJ,IAAI,CAAgC;IAAG,CAAC;IAErE,+GAA+G;IAC/G,WAAW;QACT,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACpE,CAAC;IAED,oJAAoJ;IACpJ,KAAK;QACH,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI;QACF,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAED,iJAAiJ;IACjJ,KAAK,CAAC,MAAM,CACV,KAA2E;QAE3E,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAChE,OAAO,qBAAqB,CAAC;YAC3B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC;YAC1C,WAAW,EAAE,eAAe,CAAC,KAAK,CAAC,YAAY,CAAC;YAChD,QAAQ;YACR,KAAK;YACL,UAAU;SACX,CAAC,CAAC;IACL,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACxD,mBAAmB,CAAC;YAClB,YAAY;YACZ,KAAK;YACL,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;SACjD,CAAC;aACC,IAAI,CACH,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YACjB,IAAI,CAAC,IAAI,CAAC,OAAO,CACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC,CACF;aACA,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC,CAAC,CAAC;IACP,CAAC;CACF"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* MeshStore itself is an orchestrator: it owns the shared state (the core agents/rooms/messages/dms/deliveryQueues Maps and a handful of smaller fields) and constructs the collaborators that implement almost every behaviour against direct references into that state -- DeliveryEngine, RoomProtocol, RoomMessaging, RoomLifecycle, AgentRegistry, ConnectionApproval, StaleAgentChecker, and PeerLifecycle. Every public method below that isn't inherently a MeshStore-level concern (transport/identity wiring, init/shutdown lifecycle, the events getter, mesh visibility, listener management) is a thin delegating wrapper to whichever collaborator now owns the real implementation, kept here only because CommsStore/MeshOnlyFeatures and a handful of concrete-only call sites (tests, bridge-mesh.ts, the web server, etc.) reach these names directly on a MeshStore-typed value.
|
|
9
9
|
*/
|
|
10
|
+
import { MembershipProofs } from "./membership-proofs.js";
|
|
10
11
|
import { DiscoveryManager } from "./discovery.js";
|
|
11
12
|
import type { MeshStoreIdentity } from "./mesh-store-shared.js";
|
|
12
13
|
import { GatewayTrust } from "./gateway-trust.js";
|
|
@@ -39,6 +40,8 @@ export declare class MeshStore implements CommsStore {
|
|
|
39
40
|
private readonly hubUrl;
|
|
40
41
|
/** Read by whoever wires this store to a transport, so both halves of a room.join agree on the approval window. */
|
|
41
42
|
readonly roomJoinApprovalTimeoutMs: number;
|
|
43
|
+
/** This device's own membership proof and the check for other devices' proofs (agent-comms#266). */
|
|
44
|
+
readonly membership: MembershipProofs;
|
|
42
45
|
private readonly agents;
|
|
43
46
|
private readonly rooms;
|
|
44
47
|
private readonly messages;
|
|
@@ -99,6 +102,11 @@ export declare class MeshStore implements CommsStore {
|
|
|
99
102
|
private requireTransport;
|
|
100
103
|
/** Sets the identity/clock/slot this store mints and persists room-membership grants against. Must be called before createRoom() or any other identity-using method, mirroring setTransport()'s own contract. */
|
|
101
104
|
setIdentity(identity: MeshStoreIdentity): void;
|
|
105
|
+
/** Every device this side trusts only because a trusted principal vouches for it, with that principal. */
|
|
106
|
+
listVerifiedMembers(): {
|
|
107
|
+
device: string;
|
|
108
|
+
principal: string;
|
|
109
|
+
}[];
|
|
102
110
|
/** The set identity, or throws if setIdentity() hasn't been called yet -- the single place every identity-using method reads through, mirroring requireTransport() above. */
|
|
103
111
|
private requireIdentity;
|
|
104
112
|
init(): Promise<void>;
|