agent-comms 2.18.2 → 2.18.4
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 +1 -1
- package/dist/bridges/user/web/dist/bundle.js +1 -1
- package/dist/bridges/user/web/frontend/project-tree.js +3 -3
- package/dist/bridges/user/web/frontend/project-tree.js.map +1 -1
- package/dist/bridges/user/web/test/port-discovery.unit.test.js +10 -11
- package/dist/bridges/user/web/test/port-discovery.unit.test.js.map +1 -1
- package/dist/bridges/user/web/test/web-server.integration.test.js +12 -13
- package/dist/bridges/user/web/test/web-server.integration.test.js.map +1 -1
- package/dist/core/identity-store.d.ts.map +1 -1
- package/dist/core/identity-store.js +16 -5
- package/dist/core/identity-store.js.map +1 -1
- package/dist/core/identity.d.ts +6 -5
- package/dist/core/identity.d.ts.map +1 -1
- package/dist/core/identity.js +13 -11
- package/dist/core/identity.js.map +1 -1
- package/dist/core/test/bridge.unit.test.js +11 -12
- package/dist/core/test/bridge.unit.test.js.map +1 -1
- package/dist/core/test/identity.unit.test.js +21 -22
- package/dist/core/test/identity.unit.test.js.map +1 -1
- package/dist/test/approval.integration.test.js +52 -45
- package/dist/test/approval.integration.test.js.map +1 -1
- package/dist/test/become-coordinator-actual-port.integration.test.d.ts +0 -2
- package/dist/test/become-coordinator-actual-port.integration.test.d.ts.map +1 -1
- package/dist/test/become-coordinator-actual-port.integration.test.js +14 -39
- package/dist/test/become-coordinator-actual-port.integration.test.js.map +1 -1
- package/dist/test/bridge-mesh.test.js +9 -10
- package/dist/test/bridge-mesh.test.js.map +1 -1
- package/dist/test/broadcast-window.integration.test.js +4 -7
- package/dist/test/broadcast-window.integration.test.js.map +1 -1
- package/dist/test/coordinator-socket-error.integration.test.js +3 -4
- package/dist/test/coordinator-socket-error.integration.test.js.map +1 -1
- package/dist/test/create-room-mints-owner-grant.test.js +9 -8
- package/dist/test/create-room-mints-owner-grant.test.js.map +1 -1
- package/dist/test/create-room-name-sanitisation.test.js +6 -7
- package/dist/test/create-room-name-sanitisation.test.js.map +1 -1
- package/dist/test/dm-admission.integration.test.js +10 -11
- package/dist/test/dm-admission.integration.test.js.map +1 -1
- package/dist/test/federation.integration.test.js +21 -20
- package/dist/test/federation.integration.test.js.map +1 -1
- package/dist/test/filestore.test.js +16 -17
- package/dist/test/filestore.test.js.map +1 -1
- package/dist/test/handshake.test.js +26 -27
- package/dist/test/handshake.test.js.map +1 -1
- package/dist/test/identity-cert.test.js +3 -4
- package/dist/test/identity-cert.test.js.map +1 -1
- package/dist/test/identity-device-id.test.js +6 -7
- package/dist/test/identity-device-id.test.js.map +1 -1
- package/dist/test/identity-restart.integration.test.js +9 -10
- package/dist/test/identity-restart.integration.test.js.map +1 -1
- package/dist/test/identity-store.test.js +36 -21
- package/dist/test/identity-store.test.js.map +1 -1
- package/dist/test/listener-policy.integration.test.js +63 -56
- package/dist/test/listener-policy.integration.test.js.map +1 -1
- package/dist/test/mcp-registry-retry.test.js +10 -11
- package/dist/test/mcp-registry-retry.test.js.map +1 -1
- package/dist/test/mesh-e2e.integration.test.js +26 -19
- package/dist/test/mesh-e2e.integration.test.js.map +1 -1
- package/dist/test/presence-readvertise.integration.test.js +5 -6
- package/dist/test/presence-readvertise.integration.test.js.map +1 -1
- package/dist/test/random-id.test.js +3 -4
- package/dist/test/random-id.test.js.map +1 -1
- package/dist/test/room-admission-tool-actions.test.js +20 -15
- package/dist/test/room-admission-tool-actions.test.js.map +1 -1
- package/dist/test/room-destroy-revocation.integration.test.js +3 -4
- package/dist/test/room-destroy-revocation.integration.test.js.map +1 -1
- package/dist/test/room-invite-directed.integration.test.js +13 -12
- package/dist/test/room-invite-directed.integration.test.js.map +1 -1
- package/dist/test/room-join-admission.test.js +31 -24
- package/dist/test/room-join-admission.test.js.map +1 -1
- package/dist/test/room-kick-revocation.integration.test.js +5 -6
- package/dist/test/room-kick-revocation.integration.test.js.map +1 -1
- package/dist/test/room-leave-revocation.integration.test.js +10 -9
- package/dist/test/room-leave-revocation.integration.test.js.map +1 -1
- package/dist/test/room-path.test.js +16 -17
- package/dist/test/room-path.test.js.map +1 -1
- package/dist/test/room-read-directed.integration.test.js +14 -11
- package/dist/test/room-read-directed.integration.test.js.map +1 -1
- package/dist/test/room-router.test.js +27 -29
- package/dist/test/room-router.test.js.map +1 -1
- package/dist/test/room-send-directed.integration.test.js +11 -12
- package/dist/test/room-send-directed.integration.test.js.map +1 -1
- package/dist/test/room-send-retry-queue.test.js +10 -11
- package/dist/test/room-send-retry-queue.test.js.map +1 -1
- package/dist/test/room-send-retry.integration.test.js +5 -8
- package/dist/test/room-send-retry.integration.test.js.map +1 -1
- package/dist/test/room-state-sync.integration.test.js +18 -15
- package/dist/test/room-state-sync.integration.test.js.map +1 -1
- package/dist/test/room-token-store.test.js +21 -22
- package/dist/test/room-token-store.test.js.map +1 -1
- package/dist/test/room-token-verification.test.js +19 -20
- package/dist/test/room-token-verification.test.js.map +1 -1
- package/dist/test/state-sync-convergence.test.js +25 -26
- package/dist/test/state-sync-convergence.test.js.map +1 -1
- package/dist/test/visibility.integration.test.js +48 -49
- package/dist/test/visibility.integration.test.js.map +1 -1
- package/dist/test/wire-mesh-identity.test.js +7 -8
- package/dist/test/wire-mesh-identity.test.js.map +1 -1
- package/package.json +4 -4
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Run: node dist/test/federation.integration.test.js
|
|
14
14
|
*/
|
|
15
15
|
import { MeshStore } from "../core/mesh-store.js";
|
|
16
|
-
import * as assert from "node:assert/strict";
|
|
17
16
|
import * as net from "node:net";
|
|
17
|
+
import { test, expect } from "vitest";
|
|
18
18
|
import { wireTestTransport } from "./test-transport.js";
|
|
19
19
|
// Use high ports to avoid collisions with real meshes
|
|
20
20
|
const MESH_A_PORT = 28876;
|
|
@@ -78,40 +78,42 @@ async function main() {
|
|
|
78
78
|
await sleep(100);
|
|
79
79
|
// --- Test 0: untrusted inbound connection is rejected ---
|
|
80
80
|
console.log("\nTest 0: untrusted connection is rejected...");
|
|
81
|
-
await
|
|
82
|
-
|
|
81
|
+
await expect(b.store.fedConnect("127.0.0.1", fedPort), "Connecting before either side has pinned the other's fingerprint should be rejected").rejects.toThrow(/rejected|not in the trusted-fingerprint allowlist/i);
|
|
82
|
+
expect(b.store.fedLinks().length, "B should have no federation links after a rejected attempt").toBe(0);
|
|
83
83
|
console.log(" Rejected as expected — no link was created.");
|
|
84
84
|
// --- Pin fingerprints on both sides, mirroring what an operator does out of band ---
|
|
85
85
|
console.log("\nPinning fingerprints on both sides...");
|
|
86
86
|
const fingerprintA = a.store.getFederationFingerprint();
|
|
87
87
|
const fingerprintB = b.store.getFederationFingerprint();
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
expect(fingerprintA.length > 0, "A should report its own fingerprint").toBeTruthy();
|
|
89
|
+
expect(fingerprintB.length > 0, "B should report its own fingerprint").toBeTruthy();
|
|
90
90
|
await a.store.fedTrust(fingerprintB);
|
|
91
91
|
await b.store.fedTrust(fingerprintA);
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
expect(a.store.fedTrustedFingerprints()).toEqual([fingerprintB]);
|
|
93
|
+
expect(b.store.fedTrustedFingerprints()).toEqual([fingerprintA]);
|
|
94
94
|
// --- Test 1: Establish federation link now that both sides trust each other ---
|
|
95
95
|
console.log("\nTest 1: Establish federation link...");
|
|
96
96
|
const linkId = await b.store.fedConnect("127.0.0.1", fedPort);
|
|
97
97
|
console.log(` Link established: ${linkId}`);
|
|
98
|
-
|
|
98
|
+
expect(linkId, "Should return a link ID").toBeTruthy();
|
|
99
99
|
const linksB = b.store.fedLinks();
|
|
100
|
-
|
|
100
|
+
expect(linksB.length, "B should have 1 federation link").toBe(1);
|
|
101
101
|
const link = linksB[0];
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
expect(link, "Link should exist").toBeTruthy();
|
|
103
|
+
if (link === undefined)
|
|
104
|
+
throw new Error("Link should exist");
|
|
105
|
+
expect(link.remoteMeshId.length > 0, "Remote mesh ID should be present").toBeTruthy();
|
|
104
106
|
await sleep(200);
|
|
105
107
|
// --- Test 2: Agent presence propagates ---
|
|
106
108
|
console.log("Test 2: Agent presence propagates...");
|
|
107
109
|
const agentsB = await b.store.listAgents(b.store.peerId);
|
|
108
110
|
console.log(` B sees ${String(agentsB.length)} agent(s)`);
|
|
109
111
|
const fedAgentsB = agentsB.filter((ag) => ag.tags.includes("federated"));
|
|
110
|
-
|
|
112
|
+
expect(fedAgentsB.length >= 1, "B should see at least 1 federated agent from A").toBeTruthy();
|
|
111
113
|
const agentsA = await a.store.listAgents(a.store.peerId);
|
|
112
114
|
console.log(` A sees ${String(agentsA.length)} agent(s)`);
|
|
113
115
|
const fedAgentsA = agentsA.filter((ag) => ag.tags.includes("federated"));
|
|
114
|
-
|
|
116
|
+
expect(fedAgentsA.length >= 1, "A should see at least 1 federated agent from B").toBeTruthy();
|
|
115
117
|
// --- Test 3: Federated room messages propagate ---
|
|
116
118
|
console.log("Test 3: Federated room messages propagate...");
|
|
117
119
|
const fedRoomId = `fed-room-${String(Date.now())}`;
|
|
@@ -143,7 +145,7 @@ async function main() {
|
|
|
143
145
|
await sleep(500);
|
|
144
146
|
const fedMsgs = b.deliveries.filter((e) => e.type === "room_message" && e.message.content === "Hello from mesh A!");
|
|
145
147
|
console.log(` B received ${String(fedMsgs.length)} federated message(s)`);
|
|
146
|
-
|
|
148
|
+
expect(fedMsgs.length >= 1, "B should receive the federated room message").toBeTruthy();
|
|
147
149
|
// --- Test 4: Non-federated rooms are isolated ---
|
|
148
150
|
console.log("Test 4: Non-federated rooms are isolated...");
|
|
149
151
|
const localRoomId = `local-room-${String(Date.now())}`;
|
|
@@ -164,18 +166,18 @@ async function main() {
|
|
|
164
166
|
await sleep(100);
|
|
165
167
|
const leakedMsgs = b.deliveries.filter((e) => e.type === "room_message" && e.message.content === "Secret local message");
|
|
166
168
|
console.log(` B received ${String(leakedMsgs.length)} leaked message(s)`);
|
|
167
|
-
|
|
169
|
+
expect(leakedMsgs.length, "B should NOT receive non-federated room messages").toBe(0);
|
|
168
170
|
// --- Test 5: Federation link listing ---
|
|
169
171
|
console.log("Test 5: Federation link listing...");
|
|
170
172
|
const linksA = a.store.fedLinks();
|
|
171
|
-
|
|
173
|
+
expect(linksA.length, "A should have 1 federation link").toBe(1);
|
|
172
174
|
console.log(` A links: ${linksA.map((l) => l.remoteName).join(", ")}`);
|
|
173
175
|
// --- Test 6: Disconnect federation link ---
|
|
174
176
|
console.log("Test 6: Disconnect federation link...");
|
|
175
177
|
await b.store.fedDisconnect(linkId);
|
|
176
178
|
await sleep(200);
|
|
177
179
|
const linksAfter = b.store.fedLinks();
|
|
178
|
-
|
|
180
|
+
expect(linksAfter.length, "B should have 0 federation links after disconnect").toBe(0);
|
|
179
181
|
// --- Cleanup ---
|
|
180
182
|
console.log("\nCleaning up...");
|
|
181
183
|
await a.store.fedStopListening();
|
|
@@ -183,8 +185,7 @@ async function main() {
|
|
|
183
185
|
await b.store.shutdown();
|
|
184
186
|
console.log("\n✓ All federation tests passed!");
|
|
185
187
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
process.exit(1);
|
|
188
|
+
test("federates two meshes over coordinator-to-coordinator TLS links, propagating presence and room messages while rejecting an untrusted inbound link", async () => {
|
|
189
|
+
await main();
|
|
189
190
|
});
|
|
190
191
|
//# sourceMappingURL=federation.integration.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"federation.integration.test.js","sourceRoot":"","sources":["../../src/test/federation.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"federation.integration.test.js","sourceRoot":"","sources":["../../src/test/federation.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the FileStore, the filesystem-backed legacy store exported from the public API. Covers the membership operation maps (a pending invite must survive an unrelated join, #35) and record parsing (an unparseable record must surface, not read as an empty mesh, #32).
|
|
3
3
|
*/
|
|
4
|
-
import * as assert from "node:assert/strict";
|
|
5
4
|
import * as fs from "node:fs";
|
|
6
5
|
import { tmpdir } from "node:os";
|
|
7
6
|
import path from "node:path";
|
|
8
|
-
import { test } from "
|
|
7
|
+
import { test, expect } from "vitest";
|
|
9
8
|
import { FileStore } from "../core/store.js";
|
|
10
9
|
function tempStore() {
|
|
11
10
|
const root = fs.mkdtempSync(path.join(tmpdir(), "agent-comms-filestore-"));
|
|
12
11
|
return new FileStore(root);
|
|
13
12
|
}
|
|
14
|
-
|
|
13
|
+
test("a pending invite survives an unrelated join to the room", async () => {
|
|
15
14
|
const store = tempStore();
|
|
16
15
|
const owner = await store.registerAgent({
|
|
17
16
|
name: "owner",
|
|
@@ -44,12 +43,12 @@ void test("a pending invite survives an unrelated join to the room", async () =>
|
|
|
44
43
|
description: "x",
|
|
45
44
|
});
|
|
46
45
|
await store.inviteToRoom("room", invitee.id, owner.id);
|
|
47
|
-
|
|
46
|
+
expect((await store.getRoom("room"))?.invited).toEqual([invitee.id]);
|
|
48
47
|
// Any later join re-derives the invited view from the operation maps; the pending invite must survive it (#35).
|
|
49
48
|
await store.joinRoom("room", other.id);
|
|
50
|
-
|
|
49
|
+
expect((await store.getRoom("room"))?.invited).toEqual([invitee.id]);
|
|
51
50
|
});
|
|
52
|
-
|
|
51
|
+
test("declining and kicking clear the invited view consistently", async () => {
|
|
53
52
|
const store = tempStore();
|
|
54
53
|
const owner = await store.registerAgent({
|
|
55
54
|
name: "owner",
|
|
@@ -76,15 +75,15 @@ void test("declining and kicking clear the invited view consistently", async ()
|
|
|
76
75
|
await store.inviteToRoom("room", invitee.id, owner.id);
|
|
77
76
|
await store.declineInvite("room", invitee.id, "not now");
|
|
78
77
|
const declined = await store.getRoom("room");
|
|
79
|
-
|
|
78
|
+
expect(declined?.invited).toEqual([]);
|
|
80
79
|
// Re-inviting after a decline works: the new join op outranks the leave.
|
|
81
80
|
await store.inviteToRoom("room", invitee.id, owner.id);
|
|
82
|
-
|
|
81
|
+
expect((await store.getRoom("room"))?.invited).toEqual([invitee.id]);
|
|
83
82
|
await store.kickFromRoom("room", invitee.id, owner.id);
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
expect((await store.getRoom("room"))?.invited).toEqual([]);
|
|
84
|
+
expect((await store.getRoom("room"))?.members.includes(invitee.id)).toBe(false);
|
|
86
85
|
});
|
|
87
|
-
|
|
86
|
+
test("joining through an invitation consumes it", async () => {
|
|
88
87
|
const store = tempStore();
|
|
89
88
|
const owner = await store.registerAgent({
|
|
90
89
|
name: "owner",
|
|
@@ -111,10 +110,10 @@ void test("joining through an invitation consumes it", async () => {
|
|
|
111
110
|
await store.inviteToRoom("room", invitee.id, owner.id);
|
|
112
111
|
await store.joinRoom("room", invitee.id);
|
|
113
112
|
const room = await store.getRoom("room");
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
expect(room?.invited).toEqual([]);
|
|
114
|
+
expect(room?.members.includes(invitee.id)).toBe(true);
|
|
116
115
|
});
|
|
117
|
-
|
|
116
|
+
test("an unparseable stored record surfaces instead of an empty list", async () => {
|
|
118
117
|
const store = tempStore();
|
|
119
118
|
await store.registerAgent({
|
|
120
119
|
name: "good",
|
|
@@ -127,10 +126,10 @@ void test("an unparseable stored record surfaces instead of an empty list", asyn
|
|
|
127
126
|
// A record missing the required fields (written by an older build, say) must raise from listAgents, not read as an empty mesh (#32).
|
|
128
127
|
const agentsDir = path.join(store.root, "registry", "agents");
|
|
129
128
|
fs.writeFileSync(path.join(agentsDir, "stale.json"), '{"id": "stale"}');
|
|
130
|
-
await
|
|
129
|
+
await expect(store.listAgents("whoever")).rejects.toBeInstanceOf(Error);
|
|
131
130
|
});
|
|
132
|
-
|
|
131
|
+
test("a store with no registry yet lists no agents", async () => {
|
|
133
132
|
const store = tempStore();
|
|
134
|
-
|
|
133
|
+
expect(await store.listAgents("whoever")).toEqual([]);
|
|
135
134
|
});
|
|
136
135
|
//# sourceMappingURL=filestore.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filestore.test.js","sourceRoot":"","sources":["../../src/test/filestore.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"filestore.test.js","sourceRoot":"","sources":["../../src/test/filestore.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAC3E,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,IAAI,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;IACzE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACxC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,OAAO;QAChB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,UAAU,CAAC;QACrB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAErE,gHAAgH;IAChH,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;IAC3E,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACxC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,UAAU,CAAC;QACrB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtC,yEAAyE;IACzE,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACtE,KAAK,CACN,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;IAC3D,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACxC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,UAAU,CAAC;QACrB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,WAAW,EAAE,GAAG;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;IAChF,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,KAAK,CAAC,aAAa,CAAC;QACxB,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,EAAE;KACT,CAAC,CAAC;IACH,qIAAqI;IACrI,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9D,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACxE,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;IAC9D,MAAM,KAAK,GAAG,SAAS,EAAE,CAAC;IAC1B,MAAM,CAAC,MAAM,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for the protocol handshake (#31): the gate state machine, negotiation over wire-mesh-core, and the state-sync wire tolerance.
|
|
3
3
|
*/
|
|
4
|
-
import * as assert from "node:assert/strict";
|
|
5
4
|
import * as net from "node:net";
|
|
6
|
-
import { describe, it } from "
|
|
5
|
+
import { describe, it, expect } from "vitest";
|
|
7
6
|
import { encode as cborEncode, cdeEncodeOptions } from "cbor2";
|
|
8
7
|
import { AGENT_COMMS_DOMAIN, ConnectionHandshake, MESH_PROTOCOL_VERSION, attachSocketHandshake, encodeHandshakeFrame, negotiateMeshProtocol, } from "../core/handshake.js";
|
|
9
8
|
import { normaliseWireState, } from "../core/wire-protocol.js";
|
|
@@ -14,9 +13,9 @@ describe("negotiateMeshProtocol", () => {
|
|
|
14
13
|
version: MESH_PROTOCOL_VERSION + 1,
|
|
15
14
|
domains: [AGENT_COMMS_DOMAIN],
|
|
16
15
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
expect(result.ok).toBe(true);
|
|
17
|
+
expect(result.version).toBe(MESH_PROTOCOL_VERSION);
|
|
18
|
+
expect(result.sharedDomains).toEqual([AGENT_COMMS_DOMAIN]);
|
|
20
19
|
});
|
|
21
20
|
it("refuses a peer with no shared domain", () => {
|
|
22
21
|
const result = negotiateMeshProtocol({
|
|
@@ -24,7 +23,7 @@ describe("negotiateMeshProtocol", () => {
|
|
|
24
23
|
version: MESH_PROTOCOL_VERSION,
|
|
25
24
|
domains: ["example.com/something-else"],
|
|
26
25
|
});
|
|
27
|
-
|
|
26
|
+
expect(result.ok).toBe(false);
|
|
28
27
|
});
|
|
29
28
|
});
|
|
30
29
|
describe("ConnectionHandshake", () => {
|
|
@@ -33,58 +32,58 @@ describe("ConnectionHandshake", () => {
|
|
|
33
32
|
const frame = encodeHandshakeFrame();
|
|
34
33
|
const jsonTail = Buffer.from('{"method":"introduce"}\n', "utf8");
|
|
35
34
|
const outcome = gate.feed(Buffer.concat([frame, jsonTail]));
|
|
36
|
-
|
|
35
|
+
expect(outcome.kind).toBe("negotiated");
|
|
37
36
|
if (outcome.kind !== "negotiated")
|
|
38
37
|
return;
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
expect(outcome.result.version).toBe(MESH_PROTOCOL_VERSION);
|
|
39
|
+
expect(outcome.rest.toString()).toBe(jsonTail.toString());
|
|
41
40
|
// Subsequent feeds pass through unchanged.
|
|
42
41
|
const more = gate.feed(Buffer.from("{}", "utf8"));
|
|
43
|
-
|
|
42
|
+
expect(more.kind).toBe("negotiated");
|
|
44
43
|
if (more.kind !== "negotiated")
|
|
45
44
|
return;
|
|
46
|
-
|
|
45
|
+
expect(more.rest.toString()).toBe("{}");
|
|
47
46
|
});
|
|
48
47
|
it("reassembles a frame split across TCP-sized chunks", () => {
|
|
49
48
|
const gate = new ConnectionHandshake("server");
|
|
50
49
|
const frame = Buffer.from(encodeHandshakeFrame());
|
|
51
50
|
const split = Math.floor(frame.length / 2);
|
|
52
|
-
|
|
51
|
+
expect(gate.feed(frame.subarray(0, split)).kind).toBe("pending");
|
|
53
52
|
const outcome = gate.feed(frame.subarray(split));
|
|
54
|
-
|
|
53
|
+
expect(outcome.kind).toBe("negotiated");
|
|
55
54
|
});
|
|
56
55
|
it("classifies a JSON-first-byte connection as legacy and passes bytes through", () => {
|
|
57
56
|
const gate = new ConnectionHandshake("server");
|
|
58
57
|
const outcome = gate.feed(Buffer.from('{"method":"introduce"}\n', "utf8"));
|
|
59
|
-
|
|
58
|
+
expect(outcome.kind).toBe("legacy");
|
|
60
59
|
if (outcome.kind !== "legacy")
|
|
61
60
|
return;
|
|
62
|
-
|
|
61
|
+
expect(outcome.rest.toString()).toMatch(/introduce/);
|
|
63
62
|
});
|
|
64
63
|
it("refuses a handshake whose negotiation fails (no shared domain)", () => {
|
|
65
64
|
const gate = new ConnectionHandshake("server");
|
|
66
65
|
const hostile = cborEncode({ type: "handshake", version: 1, domains: ["example.com/other"] }, cdeEncodeOptions);
|
|
67
66
|
const outcome = gate.feed(Buffer.from(hostile));
|
|
68
|
-
|
|
67
|
+
expect(outcome.kind).toBe("refused");
|
|
69
68
|
if (outcome.kind !== "refused")
|
|
70
69
|
return;
|
|
71
|
-
|
|
70
|
+
expect(outcome.reason).toMatch(/no shared domain/);
|
|
72
71
|
});
|
|
73
72
|
it("refuses a CBOR item that is not a handshake frame", () => {
|
|
74
73
|
const gate = new ConnectionHandshake("server");
|
|
75
74
|
const notAHandshake = cborEncode({ hello: "world" }, cdeEncodeOptions);
|
|
76
75
|
const outcome = gate.feed(Buffer.from(notAHandshake));
|
|
77
|
-
|
|
76
|
+
expect(outcome.kind).toBe("refused");
|
|
78
77
|
});
|
|
79
78
|
it("refuses undecodable leading bytes that are neither CBOR maps nor JSON", () => {
|
|
80
79
|
const gate = new ConnectionHandshake("server");
|
|
81
80
|
const outcome = gate.feed(Buffer.from([0xff, 0x00, 0x01]));
|
|
82
|
-
|
|
81
|
+
expect(outcome.kind).toBe("refused");
|
|
83
82
|
});
|
|
84
83
|
it("refuses a binary blob far beyond any handshake frame size", () => {
|
|
85
84
|
const gate = new ConnectionHandshake("server");
|
|
86
85
|
const big = Buffer.alloc(2048, 0x61); // 'a' — neither CBOR map head nor '{'
|
|
87
|
-
|
|
86
|
+
expect(gate.feed(big).kind).toBe("refused");
|
|
88
87
|
});
|
|
89
88
|
});
|
|
90
89
|
describe("normaliseWireState (#31 direction 2)", () => {
|
|
@@ -98,8 +97,8 @@ describe("normaliseWireState (#31 direction 2)", () => {
|
|
|
98
97
|
dms: {},
|
|
99
98
|
};
|
|
100
99
|
const normalised = normaliseWireState(legacy);
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
expect(normalised.deliveryQueues).toEqual({});
|
|
101
|
+
expect(normalised.agents.a1.version).toBe(1);
|
|
103
102
|
});
|
|
104
103
|
it("passes a complete modern state through unchanged", () => {
|
|
105
104
|
// Deliberately loose (matching the "legacy" fixture above): this test asserts only the four fields normaliseWireState touches, not a fully valid AgentIdentity/Room for every other field those domain types carry.
|
|
@@ -110,7 +109,7 @@ describe("normaliseWireState (#31 direction 2)", () => {
|
|
|
110
109
|
dms: {},
|
|
111
110
|
deliveryQueues: { a1: [] },
|
|
112
111
|
};
|
|
113
|
-
|
|
112
|
+
expect(normaliseWireState(modern)).toEqual(modern);
|
|
114
113
|
});
|
|
115
114
|
});
|
|
116
115
|
// A live gate over a real socket pair: the client frame goes out first; the server classifies on receipt and replies; the client then sees the reply.
|
|
@@ -143,8 +142,8 @@ describe("ConnectionHandshake over a real socket pair", () => {
|
|
|
143
142
|
});
|
|
144
143
|
});
|
|
145
144
|
client.write(encodeHandshakeFrame());
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
expect(await serverOutcome).toBe("negotiated");
|
|
146
|
+
expect(await clientOutcome).toBe("negotiated");
|
|
148
147
|
client.destroy();
|
|
149
148
|
server.close();
|
|
150
149
|
});
|
|
@@ -161,7 +160,7 @@ describe("ConnectionHandshake over a real socket pair", () => {
|
|
|
161
160
|
const client = net.createConnection({ port, host: "127.0.0.1" });
|
|
162
161
|
await new Promise((resolve) => client.on("connect", resolve));
|
|
163
162
|
client.write('{"method":"introduce"}\n');
|
|
164
|
-
|
|
163
|
+
expect(await serverReceived).toMatch(/introduce/);
|
|
165
164
|
client.destroy();
|
|
166
165
|
server.close();
|
|
167
166
|
});
|
|
@@ -185,7 +184,7 @@ describe("ConnectionHandshake over a real socket pair", () => {
|
|
|
185
184
|
}, cdeEncodeOptions);
|
|
186
185
|
client.write(Buffer.from(foreignFrame));
|
|
187
186
|
const error = await serverError;
|
|
188
|
-
|
|
187
|
+
expect(error.message).toMatch(/no shared domain/);
|
|
189
188
|
await new Promise((resolve) => {
|
|
190
189
|
client.on("close", resolve);
|
|
191
190
|
client.on("error", () => resolve());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handshake.test.js","sourceRoot":"","sources":["../../src/test/handshake.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"handshake.test.js","sourceRoot":"","sources":["../../src/test/handshake.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,17 +4,16 @@
|
|
|
4
4
|
* The hand-rolled DER serial number could begin with a zero byte whenever clearing the sign bit produced 0x00 — a non-minimal INTEGER, which OpenSSL rejects as "illegal padding" when the certificate is loaded. That made tls.createServer fail (even across its retries) for roughly one bridge start in 128. Loading a batch of generated identities catches a reintroduction with high probability while never failing once the encoding is minimal.
|
|
5
5
|
*/
|
|
6
6
|
import * as tls from "node:tls";
|
|
7
|
-
import
|
|
8
|
-
import { test } from "node:test";
|
|
7
|
+
import { test, expect } from "vitest";
|
|
9
8
|
import { generateIdentity } from "../core/identity.js";
|
|
10
9
|
// ~90% detection odds against the original 1-in-128 defect per run.
|
|
11
10
|
const IDENTITIES_TO_LOAD = 300;
|
|
12
|
-
|
|
11
|
+
test("generated certificates are loadable by tls.createServer", () => {
|
|
13
12
|
for (let i = 0; i < IDENTITIES_TO_LOAD; i++) {
|
|
14
13
|
const identity = generateIdentity();
|
|
15
14
|
const server = tls.createServer({ key: identity.privateKey, cert: identity.certificate }, () => { });
|
|
16
15
|
server.close();
|
|
17
|
-
|
|
16
|
+
expect(identity.fingerprint.length > 0).toBe(true);
|
|
18
17
|
}
|
|
19
18
|
});
|
|
20
19
|
//# sourceMappingURL=identity-cert.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-cert.test.js","sourceRoot":"","sources":["../../src/test/identity-cert.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"identity-cert.test.js","sourceRoot":"","sources":["../../src/test/identity-cert.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,oEAAoE;AACpE,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,IAAI,CAAC,yDAAyD,EAAE,GAAG,EAAE;IACnE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAC7B,EAAE,GAAG,EAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,EACxD,GAAG,EAAE,GAAE,CAAC,CACT,CAAC;QACF,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* The device-id wire-mesh's own migration will key peer identity on is SHA-256 of the raw public-key bytes -- never the certificate's own DER encoding, which embeds a serial number and validity window that change on every reissue even for an identical key (the same bug this project's certificate-fingerprint peerId already has, and the reason the migration plan treats it as provably unstable). This is purely additive: nothing yet reads identity.deviceId, so this only proves the value itself is correct, not that anything consumes it.
|
|
3
3
|
*/
|
|
4
4
|
import { createHash, createPublicKey } from "node:crypto";
|
|
5
|
-
import
|
|
6
|
-
import { test } from "node:test";
|
|
5
|
+
import { test, expect } from "vitest";
|
|
7
6
|
import { generateIdentity } from "../core/identity.js";
|
|
8
7
|
/** Re-derives the raw uncompressed SEC1 point independently of identity.ts's own implementation, via the JWK x/y coordinates every Node public KeyObject can export -- an oracle the test checks identity.ts's own derivation against, not a copy of it. */
|
|
9
8
|
function rawPublicKeyFromCertificate(certificatePem) {
|
|
@@ -20,16 +19,16 @@ function rawPublicKeyFromCertificate(certificatePem) {
|
|
|
20
19
|
]);
|
|
21
20
|
}
|
|
22
21
|
const SHA256_BYTE_LENGTH = 32;
|
|
23
|
-
|
|
22
|
+
test("deviceId is SHA-256 of the raw public key, independent of the certificate", () => {
|
|
24
23
|
const identity = generateIdentity();
|
|
25
|
-
|
|
24
|
+
expect(identity.deviceId.length).toBe(SHA256_BYTE_LENGTH);
|
|
26
25
|
const rawPublicKey = rawPublicKeyFromCertificate(identity.certificate);
|
|
27
26
|
const expected = createHash("sha256").update(rawPublicKey).digest();
|
|
28
|
-
|
|
27
|
+
expect(Buffer.from(identity.deviceId).equals(expected)).toBe(true);
|
|
29
28
|
});
|
|
30
|
-
|
|
29
|
+
test("deviceId differs between two freshly generated identities", () => {
|
|
31
30
|
const a = generateIdentity();
|
|
32
31
|
const b = generateIdentity();
|
|
33
|
-
|
|
32
|
+
expect(Buffer.from(a.deviceId).toString("hex")).not.toBe(Buffer.from(b.deviceId).toString("hex"));
|
|
34
33
|
});
|
|
35
34
|
//# sourceMappingURL=identity-device-id.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-device-id.test.js","sourceRoot":"","sources":["../../src/test/identity-device-id.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"identity-device-id.test.js","sourceRoot":"","sources":["../../src/test/identity-device-id.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,4PAA4P;AAC5P,SAAS,2BAA2B,CAAC,cAAsB;IACzD,MAAM,SAAS,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,IAAI,GAAG,CAAC,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,sBAAsB,GAAG,IAAI,CAAC;IACpC,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,CAAC,sBAAsB,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;KAChC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,IAAI,CAAC,2EAA2E,EAAE,GAAG,EAAE;IACrF,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAE1D,MAAM,YAAY,GAAG,2BAA2B,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;IACpE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,MAAM,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,gBAAgB,EAAE,CAAC;IAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CACtD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CACxC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Before persistent identities, every restart generated a fresh keypair, so the device-id-derived agent ID changed and peers kept targeting the old ID: their messages queued for an agent whose local handler (gated on agentId === peerId) never fired again.
|
|
5
5
|
*/
|
|
6
|
-
import * as assert from "node:assert/strict";
|
|
7
6
|
import * as fs from "node:fs";
|
|
8
7
|
import { tmpdir } from "node:os";
|
|
9
8
|
import path from "node:path";
|
|
9
|
+
import { test, expect } from "vitest";
|
|
10
10
|
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
11
11
|
import { createSystemClock } from "wire-mesh-core/adapters/system-clock";
|
|
12
12
|
import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
@@ -38,7 +38,7 @@ async function waitFor(what, check) {
|
|
|
38
38
|
return;
|
|
39
39
|
await sleep(100);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
expect(false, `timed out waiting for ${what}`).toBeTruthy();
|
|
42
42
|
}
|
|
43
43
|
async function main() {
|
|
44
44
|
const dir = fs.mkdtempSync(path.join(tmpdir(), "agent-comms-restart-test-"));
|
|
@@ -97,7 +97,7 @@ async function main() {
|
|
|
97
97
|
await sleep(200);
|
|
98
98
|
// A restarts in the same slot: same key material, same agent ID.
|
|
99
99
|
const identityA2 = loadOrCreateIdentity(slot);
|
|
100
|
-
|
|
100
|
+
expect(deviceIdToHex(Uint8Array.from(identityA2.deviceId))).toBe(deviceIdToHex(Uint8Array.from(identityA.deviceId)));
|
|
101
101
|
const a2 = await makePeer(identityA2, slot);
|
|
102
102
|
a2.store.onDelivery = (_id, ev) => {
|
|
103
103
|
a2.deliveries.push(ev);
|
|
@@ -111,7 +111,7 @@ async function main() {
|
|
|
111
111
|
visibility: "visible",
|
|
112
112
|
tags: [],
|
|
113
113
|
});
|
|
114
|
-
|
|
114
|
+
expect(a2.store.peerId).toBe(agentIdA);
|
|
115
115
|
await waitFor("B to see the restarted agent active", async () => {
|
|
116
116
|
const agents = await b.store.listAgents(b.store.peerId);
|
|
117
117
|
const seen = agents.find((agent) => agent.id === agentIdA);
|
|
@@ -128,7 +128,9 @@ async function main() {
|
|
|
128
128
|
const pendingDm = a2.store
|
|
129
129
|
.listPendingRoomJoins()
|
|
130
130
|
.find((p) => p.requesterId === b.store.peerId);
|
|
131
|
-
|
|
131
|
+
expect(pendingDm).toBeTruthy();
|
|
132
|
+
if (pendingDm === undefined)
|
|
133
|
+
throw new Error("expected a pending DM request");
|
|
132
134
|
a2.store.acceptRoomJoin(pendingDm.roomPath, b.store.peerId);
|
|
133
135
|
await dmAccessPromise;
|
|
134
136
|
await b.store.sendDm(b.store.peerId, agentIdA, "dm after restart");
|
|
@@ -138,10 +140,7 @@ async function main() {
|
|
|
138
140
|
releaseIdentityLock(slot);
|
|
139
141
|
console.log("✓ restart continuity holds: same ID, active, room + DM delivered");
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
process.exitCode = 1;
|
|
144
|
-
// The sequence above keeps mesh handles open when it fails partway; exit explicitly so a failure cannot hang the runner.
|
|
145
|
-
process.exit(1);
|
|
143
|
+
test("a bridge restarted with a persisted identity keeps its agent ID, room membership, and push delivery", async () => {
|
|
144
|
+
await main();
|
|
146
145
|
});
|
|
147
146
|
//# sourceMappingURL=identity-restart.integration.test.js.map
|