agent-comms 8.4.1 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +13 -3
- package/dist/bridges/user/web/dist/mesh-worker.js +1 -1
- package/dist/bridges/user/web/dist/sw.js +1 -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/atomic-file.d.ts +12 -0
- package/dist/core/atomic-file.d.ts.map +1 -0
- package/dist/core/atomic-file.js +37 -0
- package/dist/core/atomic-file.js.map +1 -0
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +4 -0
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/bridge.d.ts +1 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +4 -0
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/device-membership-verification.d.ts +23 -9
- package/dist/core/device-membership-verification.d.ts.map +1 -1
- package/dist/core/device-membership-verification.js +38 -11
- package/dist/core/device-membership-verification.js.map +1 -1
- package/dist/core/device-membership.d.ts +12 -3
- package/dist/core/device-membership.d.ts.map +1 -1
- package/dist/core/device-membership.js +10 -7
- package/dist/core/device-membership.js.map +1 -1
- package/dist/core/directory-admission.d.ts +5 -3
- package/dist/core/directory-admission.d.ts.map +1 -1
- package/dist/core/directory-admission.js +55 -32
- package/dist/core/directory-admission.js.map +1 -1
- package/dist/core/gateway-trust-actions.d.ts +5 -8
- package/dist/core/gateway-trust-actions.d.ts.map +1 -1
- package/dist/core/gateway-trust-actions.js +79 -15
- package/dist/core/gateway-trust-actions.js.map +1 -1
- package/dist/core/gateway-trust.d.ts +38 -17
- package/dist/core/gateway-trust.d.ts.map +1 -1
- package/dist/core/gateway-trust.js +86 -37
- package/dist/core/gateway-trust.js.map +1 -1
- package/dist/core/gossip-directory.d.ts +23 -3
- package/dist/core/gossip-directory.d.ts.map +1 -1
- package/dist/core/gossip-directory.js +45 -6
- 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/gossip-extensions.js +1 -0
- package/dist/core/gossip-extensions.js.map +1 -1
- package/dist/core/group-proofs.d.ts +43 -0
- package/dist/core/group-proofs.d.ts.map +1 -0
- package/dist/core/group-proofs.js +69 -0
- package/dist/core/group-proofs.js.map +1 -0
- package/dist/core/identity-store.d.ts +5 -4
- package/dist/core/identity-store.d.ts.map +1 -1
- package/dist/core/identity-store.js +13 -16
- package/dist/core/identity-store.js.map +1 -1
- package/dist/core/issuer-identity-file.d.ts +25 -0
- package/dist/core/issuer-identity-file.d.ts.map +1 -0
- package/dist/core/issuer-identity-file.js +124 -0
- package/dist/core/issuer-identity-file.js.map +1 -0
- package/dist/core/machine-grouping.d.ts +34 -0
- package/dist/core/machine-grouping.d.ts.map +1 -0
- package/dist/core/machine-grouping.js +66 -0
- package/dist/core/machine-grouping.js.map +1 -0
- package/dist/core/machine-identity.d.ts +15 -0
- package/dist/core/machine-identity.d.ts.map +1 -0
- package/dist/core/machine-identity.js +18 -0
- package/dist/core/machine-identity.js.map +1 -0
- package/dist/core/machine-list-groups.d.ts +12 -0
- package/dist/core/machine-list-groups.d.ts.map +1 -0
- package/dist/core/machine-list-groups.js +41 -0
- package/dist/core/machine-list-groups.js.map +1 -0
- package/dist/core/membership-proof.d.ts +20 -9
- package/dist/core/membership-proof.d.ts.map +1 -1
- package/dist/core/membership-proof.js +41 -15
- package/dist/core/membership-proof.js.map +1 -1
- package/dist/core/membership-proofs.d.ts +14 -8
- package/dist/core/membership-proofs.d.ts.map +1 -1
- package/dist/core/membership-proofs.js +21 -10
- package/dist/core/membership-proofs.js.map +1 -1
- package/dist/core/mesh-store-shared.d.ts +2 -1
- package/dist/core/mesh-store-shared.d.ts.map +1 -1
- package/dist/core/mesh-store-shared.js.map +1 -1
- package/dist/core/mesh-store.d.ts +17 -10
- package/dist/core/mesh-store.d.ts.map +1 -1
- package/dist/core/mesh-store.js +35 -38
- package/dist/core/mesh-store.js.map +1 -1
- package/dist/core/network-interfaces.d.ts +7 -0
- package/dist/core/network-interfaces.d.ts.map +1 -0
- package/dist/core/network-interfaces.js +23 -0
- package/dist/core/network-interfaces.js.map +1 -0
- package/dist/core/peer-versions.d.ts +6 -0
- package/dist/core/peer-versions.d.ts.map +1 -1
- package/dist/core/peer-versions.js +17 -0
- package/dist/core/peer-versions.js.map +1 -1
- package/dist/core/room-path.d.ts +2 -0
- package/dist/core/room-path.d.ts.map +1 -1
- package/dist/core/room-path.js +5 -1
- package/dist/core/room-path.js.map +1 -1
- package/dist/core/tool.d.ts +10 -5
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +16 -3
- package/dist/core/tool.js.map +1 -1
- package/dist/core/types.d.ts +4 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/user-identity.d.ts +4 -6
- package/dist/core/user-identity.d.ts.map +1 -1
- package/dist/core/user-identity.js +9 -113
- package/dist/core/user-identity.js.map +1 -1
- package/dist/core/wire-mesh-transport-options.d.ts +2 -2
- package/dist/core/wire-mesh-transport-options.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.d.ts.map +1 -1
- package/dist/core/wire-mesh-transport.js +7 -7
- package/dist/core/wire-mesh-transport.js.map +1 -1
- package/dist/test/delivery-engine-delivery.unit.test.js +1 -0
- package/dist/test/delivery-engine-delivery.unit.test.js.map +1 -1
- package/dist/test/delivery-engine-directed-notify.unit.test.js +1 -0
- package/dist/test/delivery-engine-directed-notify.unit.test.js.map +1 -1
- package/dist/test/delivery-engine-own-agent.unit.test.js +1 -0
- package/dist/test/delivery-engine-own-agent.unit.test.js.map +1 -1
- package/dist/test/delivery-engine.unit.test.js +1 -0
- package/dist/test/delivery-engine.unit.test.js.map +1 -1
- package/dist/test/device-membership-verification.unit.test.js +25 -25
- package/dist/test/device-membership-verification.unit.test.js.map +1 -1
- package/dist/test/device-membership.unit.test.js +6 -6
- package/dist/test/device-membership.unit.test.js.map +1 -1
- package/dist/test/directory-admission.unit.test.js +103 -7
- package/dist/test/directory-admission.unit.test.js.map +1 -1
- package/dist/test/gateway-trust-tool.integration.test.js +115 -3
- package/dist/test/gateway-trust-tool.integration.test.js.map +1 -1
- package/dist/test/gateway-trust.unit.test.js +62 -13
- package/dist/test/gateway-trust.unit.test.js.map +1 -1
- package/dist/test/gossip-directory.unit.test.js +14 -1
- package/dist/test/gossip-directory.unit.test.js.map +1 -1
- package/dist/test/hub-exposure.integration.test.js +1 -1
- package/dist/test/hub-exposure.integration.test.js.map +1 -1
- package/dist/test/identity-restart.integration.test.js +2 -0
- package/dist/test/identity-restart.integration.test.js.map +1 -1
- package/dist/test/identity-store.integration.test.js +64 -14
- package/dist/test/identity-store.integration.test.js.map +1 -1
- package/dist/test/machine-grouping.unit.test.d.ts +2 -0
- package/dist/test/machine-grouping.unit.test.d.ts.map +1 -0
- package/dist/test/machine-grouping.unit.test.js +61 -0
- package/dist/test/machine-grouping.unit.test.js.map +1 -0
- package/dist/test/machine-identity.unit.test.d.ts +5 -0
- package/dist/test/machine-identity.unit.test.d.ts.map +1 -0
- package/dist/test/machine-identity.unit.test.js +65 -0
- package/dist/test/machine-identity.unit.test.js.map +1 -0
- package/dist/test/machine-list-groups.unit.test.d.ts +5 -0
- package/dist/test/machine-list-groups.unit.test.d.ts.map +1 -0
- package/dist/test/machine-list-groups.unit.test.js +57 -0
- package/dist/test/machine-list-groups.unit.test.js.map +1 -0
- package/dist/test/machine-membership-trust.integration.test.d.ts +5 -0
- package/dist/test/machine-membership-trust.integration.test.d.ts.map +1 -0
- package/dist/test/machine-membership-trust.integration.test.js +151 -0
- package/dist/test/machine-membership-trust.integration.test.js.map +1 -0
- package/dist/test/membership-proof.unit.test.js +105 -13
- package/dist/test/membership-proof.unit.test.js.map +1 -1
- package/dist/test/principal-membership-trust.integration.test.js +2 -1
- package/dist/test/principal-membership-trust.integration.test.js.map +1 -1
- package/dist/test/room-join-admission.integration.test.js +2 -0
- package/dist/test/room-join-admission.integration.test.js.map +1 -1
- package/dist/test/room-lifecycle-bare-name.unit.test.js +1 -0
- package/dist/test/room-lifecycle-bare-name.unit.test.js.map +1 -1
- package/dist/test/room-lifecycle-membership.unit.test.js +1 -0
- package/dist/test/room-lifecycle-membership.unit.test.js.map +1 -1
- package/dist/test/room-lifecycle-remote.unit.test.js +1 -0
- package/dist/test/room-lifecycle-remote.unit.test.js.map +1 -1
- package/dist/test/room-lifecycle.unit.test.js +1 -0
- package/dist/test/room-lifecycle.unit.test.js.map +1 -1
- package/dist/test/room-messaging-durable-send.unit.test.js +1 -0
- package/dist/test/room-messaging-durable-send.unit.test.js.map +1 -1
- package/dist/test/room-messaging.unit.test.js +1 -0
- package/dist/test/room-messaging.unit.test.js.map +1 -1
- package/dist/test/room-protocol-notify.unit.test.js +1 -0
- package/dist/test/room-protocol-notify.unit.test.js.map +1 -1
- package/dist/test/room-protocol.helper.d.ts.map +1 -1
- package/dist/test/room-protocol.helper.js +1 -0
- package/dist/test/room-protocol.helper.js.map +1 -1
- package/dist/test/room-send-retry.integration.test.js +2 -0
- package/dist/test/room-send-retry.integration.test.js.map +1 -1
- package/dist/test/test-transport.d.ts +3 -0
- package/dist/test/test-transport.d.ts.map +1 -1
- package/dist/test/test-transport.js +6 -1
- package/dist/test/test-transport.js.map +1 -1
- package/dist/test/user-identity.unit.test.js +40 -14
- package/dist/test/user-identity.unit.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommsTool's gateway-trust action handlers (agent-comms#156, extended by agent-comms#187's principal-keyed allowlist, agent-comms#193's tool-level `principal` flag and agent-comms#343's `machine` flag), split out of tool.ts to keep that file under the repo's max-lines cap, the same reason agent-registry.ts, delivery-engine.ts, and their siblings were split from mesh-store.ts. Free functions over MeshOnlyFeatures's own gateway-trust methods rather than class methods, since CommsTool owns no state of its own for this concern: every one of these is a pure translation from a CommsAction to a CommsResult against whatever store methods are present.
|
|
3
|
+
*/
|
|
4
|
+
import { isDeviceIdHex } from "./room-path.js";
|
|
5
|
+
/** The result for an id named as both a principal and a machine: the two are different kinds of issuer, so one id is at most one of them. */
|
|
6
|
+
function principalOrMachine() {
|
|
7
|
+
return {
|
|
8
|
+
content: "Pass principal or machine, not both.",
|
|
9
|
+
isError: true,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** The result for an id that is not a full device-id. Trusting one would persist an entry that matches no real device, principal or machine, yet still count towards hasAny and so start this side advertising on the hub. */
|
|
13
|
+
function notADeviceId(device) {
|
|
14
|
+
return {
|
|
15
|
+
content: `${JSON.stringify(device)} is not a device-id: pass the full 64-character hex id (whoami prints this side's).`,
|
|
16
|
+
isError: true,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
1
19
|
/** Uniform "gateway trust isn't available on this store" result, mirroring tool.ts's own notMeshBacked helper for the other MeshOnlyFeatures methods. */
|
|
2
20
|
function gatewayTrustUnavailable() {
|
|
3
21
|
return {
|
|
@@ -5,8 +23,23 @@ function gatewayTrustUnavailable() {
|
|
|
5
23
|
isError: true,
|
|
6
24
|
};
|
|
7
25
|
}
|
|
8
|
-
/** Trusts action.device: as a user principal (GatewayTrust.addPrincipal, agent-comms#187) when action.principal is true, or as a bare remote device-id (the original agent-comms#156 behaviour) otherwise. */
|
|
26
|
+
/** Trusts action.device: as a user principal (GatewayTrust.addPrincipal, agent-comms#187) when action.principal is true, as a machine (GatewayTrust.addMachine, agent-comms#343) when action.machine is true, or as a bare remote device-id (the original agent-comms#156 behaviour) otherwise. */
|
|
9
27
|
export function gatewayTrust(store, action) {
|
|
28
|
+
if (action.principal === true && action.machine === true) {
|
|
29
|
+
return principalOrMachine();
|
|
30
|
+
}
|
|
31
|
+
if (!isDeviceIdHex(action.device.toLowerCase())) {
|
|
32
|
+
return notADeviceId(action.device);
|
|
33
|
+
}
|
|
34
|
+
if (action.machine === true) {
|
|
35
|
+
if (!store.addTrustedGatewayMachine)
|
|
36
|
+
return gatewayTrustUnavailable();
|
|
37
|
+
store.addTrustedGatewayMachine(action.device);
|
|
38
|
+
return {
|
|
39
|
+
content: `Trusted remote machine ${action.device}: every device it vouches for is now reachable.`,
|
|
40
|
+
isError: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
10
43
|
if (action.principal === true) {
|
|
11
44
|
if (!store.addTrustedGatewayPrincipal)
|
|
12
45
|
return gatewayTrustUnavailable();
|
|
@@ -24,8 +57,24 @@ export function gatewayTrust(store, action) {
|
|
|
24
57
|
isError: false,
|
|
25
58
|
};
|
|
26
59
|
}
|
|
27
|
-
/** Withdraws trust from action.device: from the principal allowlist (GatewayTrust.removePrincipal, agent-comms#187) when action.principal is true, or from the bare-device allowlist (the original agent-comms#156 behaviour) otherwise. */
|
|
60
|
+
/** Withdraws trust from action.device: from the principal allowlist (GatewayTrust.removePrincipal, agent-comms#187) when action.principal is true, from the machine allowlist (GatewayTrust.removeMachine, agent-comms#343, revoking every device it vouches for at once) when action.machine is true, or from the bare-device allowlist (the original agent-comms#156 behaviour) otherwise. */
|
|
28
61
|
export function gatewayUntrust(store, action) {
|
|
62
|
+
if (action.principal === true && action.machine === true) {
|
|
63
|
+
return principalOrMachine();
|
|
64
|
+
}
|
|
65
|
+
if (!isDeviceIdHex(action.device.toLowerCase()) &&
|
|
66
|
+
!listedForUntrust(store, action)) {
|
|
67
|
+
return notADeviceId(action.device);
|
|
68
|
+
}
|
|
69
|
+
if (action.machine === true) {
|
|
70
|
+
if (!store.removeTrustedGatewayMachine)
|
|
71
|
+
return gatewayTrustUnavailable();
|
|
72
|
+
store.removeTrustedGatewayMachine(action.device);
|
|
73
|
+
return {
|
|
74
|
+
content: `Untrusted remote machine ${action.device}: no device is reachable through it any more.`,
|
|
75
|
+
isError: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
29
78
|
if (action.principal === true) {
|
|
30
79
|
if (!store.removeTrustedGatewayPrincipal)
|
|
31
80
|
return gatewayTrustUnavailable();
|
|
@@ -43,29 +92,44 @@ export function gatewayUntrust(store, action) {
|
|
|
43
92
|
isError: false,
|
|
44
93
|
};
|
|
45
94
|
}
|
|
46
|
-
/**
|
|
95
|
+
/** Whether action.device is already in the set gateway_untrust would remove it from, so an entry an earlier version persisted without checking its shape can still be withdrawn through the tool. */
|
|
96
|
+
function listedForUntrust(store, action) {
|
|
97
|
+
const listed = action.machine === true
|
|
98
|
+
? store.listTrustedGatewayMachines?.()
|
|
99
|
+
: action.principal === true
|
|
100
|
+
? store.listTrustedGatewayPrincipals?.()
|
|
101
|
+
: store.listTrustedGateways?.();
|
|
102
|
+
return listed?.includes(action.device.toLowerCase()) === true;
|
|
103
|
+
}
|
|
104
|
+
/** Reports every currently trusted remote device-id, principal (agent-comms#187) and machine (agent-comms#343) together, and the devices reachable through a trusted principal or machine. Listing is never mutually exclusive between the sets, so, unlike gatewayTrust/gatewayUntrust above, this needs no flag of its own. */
|
|
47
105
|
export function gatewayListTrusted(store) {
|
|
48
106
|
if (!store.listTrustedGateways)
|
|
49
107
|
return gatewayTrustUnavailable();
|
|
50
108
|
const devices = store.listTrustedGateways();
|
|
51
109
|
const principals = store.listTrustedGatewayPrincipals?.() ?? [];
|
|
110
|
+
const machines = store.listTrustedGatewayMachines?.() ?? [];
|
|
52
111
|
const members = store.listVerifiedMembers?.() ?? [];
|
|
53
|
-
|
|
112
|
+
const sections = [];
|
|
113
|
+
const section = (title, lines) => {
|
|
114
|
+
if (lines.length > 0) {
|
|
115
|
+
sections.push(`${title}:\n${lines.map((line) => ` ${line}`).join("\n")}`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
section("Trusted remote gateway devices", devices);
|
|
119
|
+
section("Trusted remote gateway principals", principals);
|
|
120
|
+
section("Trusted remote machines", machines);
|
|
121
|
+
section("Devices trusted through a principal", members
|
|
122
|
+
.filter((member) => member.kind === "principal")
|
|
123
|
+
.map((member) => `${member.device} (vouched for by ${member.issuer})`));
|
|
124
|
+
section("Devices trusted through a machine", members
|
|
125
|
+
.filter((member) => member.kind === "machine")
|
|
126
|
+
.map((member) => `${member.device} (runs on ${member.issuer})`));
|
|
127
|
+
if (sections.length === 0) {
|
|
54
128
|
return {
|
|
55
|
-
content: "No remote gateway devices or
|
|
129
|
+
content: "No remote gateway devices, principals or machines are trusted.",
|
|
56
130
|
isError: false,
|
|
57
131
|
};
|
|
58
132
|
}
|
|
59
|
-
const sections = [];
|
|
60
|
-
if (devices.length > 0) {
|
|
61
|
-
sections.push(`Trusted remote gateway devices:\n${devices.map((device) => ` ${device}`).join("\n")}`);
|
|
62
|
-
}
|
|
63
|
-
if (principals.length > 0) {
|
|
64
|
-
sections.push(`Trusted remote gateway principals:\n${principals.map((principal) => ` ${principal}`).join("\n")}`);
|
|
65
|
-
}
|
|
66
|
-
if (members.length > 0) {
|
|
67
|
-
sections.push(`Devices trusted through a principal:\n${members.map((member) => ` ${member.device} (vouched for by ${member.principal})`).join("\n")}`);
|
|
68
|
-
}
|
|
69
133
|
return {
|
|
70
134
|
content: sections.join("\n"),
|
|
71
135
|
isError: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-trust-actions.js","sourceRoot":"","sources":["../../src/core/gateway-trust-actions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gateway-trust-actions.js","sourceRoot":"","sources":["../../src/core/gateway-trust-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,15 +5,28 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Keyed by individual device-id, not by "one entry per remote machine": wire-mesh-core's relay-hub protocol (relay-hub.ts, gossip-frame, relay-data-frame) carries no field identifying which remote gateway connection a given directory entry or relayed request actually originated from -- only the entry/request's own device-id, which may be an ordinary local peer forwarded on a remote machine's behalf rather than that machine's own coordinator. Gating per individual device-id is therefore the finest-grained, and only wire-protocol-honest, trust boundary actually implementable without a wire-mesh-core protocol change (deliberately out of scope here, matching agent-comms#156's own "gating the hub itself is out of scope" framing) -- confirmed as the intended granularity by hub-session.ts's own pre-existing isStateMutatingMessage doc comment, which already named this exact gap as "agent-comms#156's own future deliverable" of "per-peer" admission control. An operator who wants every local peer on a remote machine reachable trusts each of that machine's device-ids individually, not just its coordinator's.
|
|
7
7
|
*
|
|
8
|
+
* Machine-keyed trust (agent-comms#343) is the same shape as the principal's: a machine is another grouping issuer (machine-identity.ts), so trusting one makes every device its gossiped proofs vouch for reachable, and untrusting it cuts all of them off at once, which is how every bridge one account runs on a host is revoked in one act (the key is per OS account, machine-identity.ts). Principals and machines are separate sets because they answer different questions (whose device, which host), and a device may be vouched for by one of each.
|
|
9
|
+
*
|
|
8
10
|
* Principal-keyed trust (agent-comms#187): alongside the bare-device allowlist above, an operator may also trust a user-principal device-id (user-identity.ts) directly -- the root a remote peer's own dm:send-style delegation chain can terminate at, verified the same way device-membership-verification.ts already verifies a device's own group:member chain (`verifyCapabilityToken`'s chain-walk and its `rootIssuer` output). This is purely a second, parallel allowlist: `isTrusted`/`add`/`remove`/`list` keep checking only the bare-device set, unchanged, for a peer with no principal at all -- `isTrustedFor` is the additive entrypoint a caller who has already chain-verified a token uses to decide trust from that verified bearer and chain-root pair, accepting either a directly trusted device or a bearer rooted at a trusted principal. Persisted alongside the bare-device set (agent-comms#186's own persistence, extended here per that issue's own "agent-comms#187 covers what gets stored" framing): loadGatewayTrust/saveGatewayTrust now carry both sets.
|
|
9
11
|
*/
|
|
10
12
|
import type { IdentitySlot } from "./identity-store.js";
|
|
11
13
|
/** The read-only slice of GatewayTrust every consumer of the trust boundary actually needs (WireMeshTransport, HubSession, hub-forwarding.ts) -- named so call sites that only ever read trust decisions, never mutate them, don't repeat the same `Pick<GatewayTrust, "isTrusted" | "hasAny" | "isTrustedPrincipal" | "isTrustedFor">` inline at every field/parameter that takes one. */
|
|
12
|
-
export type GatewayTrustReader = Pick<GatewayTrust, "isTrusted" | "isReachable" | "hasAny" | "isTrustedPrincipal" | "isTrustedFor" | "listPrincipals" | "noteVerifiedMember">;
|
|
14
|
+
export type GatewayTrustReader = Pick<GatewayTrust, "isTrusted" | "isReachable" | "isVouchedBy" | "hasAny" | "isTrustedPrincipal" | "isTrustedFor" | "listPrincipals" | "listMachines" | "noteVerifiedMember">;
|
|
15
|
+
/** Which kind of grouping issuer vouches for a device: its user principal or its machine. */
|
|
16
|
+
export type GroupKind = "principal" | "machine";
|
|
17
|
+
/** A grouping issuer that vouched for a device: its kind and its device-id (lowercase hex). */
|
|
18
|
+
export interface Voucher {
|
|
19
|
+
kind: GroupKind;
|
|
20
|
+
issuer: string;
|
|
21
|
+
}
|
|
22
|
+
/** A device reachable only because a trusted issuer vouches for it, and that issuer. */
|
|
23
|
+
export interface VerifiedMember extends Voucher {
|
|
24
|
+
device: string;
|
|
25
|
+
}
|
|
13
26
|
export declare class GatewayTrust {
|
|
14
27
|
private readonly trusted;
|
|
15
|
-
private readonly
|
|
16
|
-
/** Devices whose gossiped membership proof was verified against a trusted
|
|
28
|
+
private readonly trustedIssuers;
|
|
29
|
+
/** Devices whose gossiped membership proof was verified against a trusted issuer, keyed by memberKey (a device can hold one of each kind), with that issuer and when the proof lapses. In memory only: a proof is short-lived and re-verified from the next advert, so nothing here is worth persisting. */
|
|
17
30
|
private readonly verifiedMembers;
|
|
18
31
|
private readonly location;
|
|
19
32
|
/** gatewayTrustStamp as of the last load or write this instance made, so refresh() can tell when another process has replaced the file since. */
|
|
@@ -26,38 +39,46 @@ export declare class GatewayTrust {
|
|
|
26
39
|
private refresh;
|
|
27
40
|
/** Marks a remote device-id (hex, case-insensitive) as trusted: this side will merge its gossiped directory entries, dispatch its relayed requests, and route outbound hub requests to it. Idempotent. Persists the updated set when this instance was constructed with a location. */
|
|
28
41
|
add(deviceHex: string): void;
|
|
29
|
-
/** Withdraws a previously trusted device-id (hex, case-insensitive). A no-op if it was never trusted.
|
|
42
|
+
/** Withdraws a previously trusted device-id (hex, case-insensitive). A no-op if it was never trusted. Requests already in flight are unaffected; a directory entry merged from the hub because of this trust alone leaves the transport's view on its next read (KnownDevices, gossip-directory.ts). Persists the updated set when this instance was constructed with a location. */
|
|
30
43
|
remove(deviceHex: string): void;
|
|
31
|
-
/** Writes the complete current trusted device and
|
|
44
|
+
/** Writes the complete current trusted device, principal and machine sets back to the shared trust file, if this instance was constructed with a location. A no-op for the in-memory-only case. */
|
|
32
45
|
private persist;
|
|
33
46
|
/** Every currently trusted device-id, lowercase hex, in insertion order. */
|
|
34
47
|
list(): string[];
|
|
35
|
-
/** Whether the given device-id (hex, case-insensitive) is trusted by id: on the bare-device allowlist. A device trusted only because a principal vouches for it (noteVerifiedMember) is not, on purpose: its proof is a claim that anyone who has read the device's gossiped advert can repeat, so it earns only what isReachable grants. */
|
|
48
|
+
/** Whether the given device-id (hex, case-insensitive) is trusted by id: on the bare-device allowlist. A device trusted only because a principal or machine vouches for it (noteVerifiedMember) is not, on purpose: its proof is a claim that anyone who has read the device's gossiped advert can repeat, so it earns only what isReachable grants. */
|
|
36
49
|
isTrusted(deviceHex: string): boolean;
|
|
37
|
-
/** Whether this side may merge the device's gossiped directory entry and route requests to it: trusted by id, or a verified member of a trusted principal. Requests routed to a device are still authenticated end to end by the session inside the relay, so a false claim can misroute one (which then fails its handshake) but not read or forge one. */
|
|
50
|
+
/** Whether this side may merge the device's gossiped directory entry and route requests to it: trusted by id, or a verified member of a trusted principal or machine. Requests routed to a device are still authenticated end to end by the session inside the relay, so a false claim can misroute one (which then fails its handshake) but not read or forge one. */
|
|
38
51
|
isReachable(deviceHex: string): boolean;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}[];
|
|
52
|
+
/** Whether the device (hex, case-insensitive) is currently a verified member through a trusted issuer of the given kind: its proof of that kind has not lapsed and its issuer is still trusted. Unlike isReachable, this answers for one kind alone, so a device reachable through its principal is not taken to hold a live machine record too. */
|
|
53
|
+
isVouchedBy(deviceHex: string, kind: GroupKind): boolean;
|
|
54
|
+
/** Records that deviceHex presented a membership proof, valid until expiresAt (epoch ms), verified against voucher (agent-comms#266, agent-comms#343). From then until the proof lapses, or the voucher's issuer stops being trusted, isReachable treats the device as reachable. A later proof from the same kind of issuer never shortens an earlier one's window. The caller has already verified the proof: this class does no cryptography. */
|
|
55
|
+
noteVerifiedMember(deviceHex: string, voucher: Readonly<Voucher>, expiresAt: number): void;
|
|
56
|
+
/** Every device currently reachable because a trusted principal or machine vouches for it, once per vouching issuer. Excludes proofs that have lapsed and members whose issuer is no longer trusted. */
|
|
57
|
+
listVerifiedMembers(): VerifiedMember[];
|
|
46
58
|
private isVerifiedMember;
|
|
47
|
-
|
|
59
|
+
private addIssuer;
|
|
60
|
+
/** Withdraws trust from one issuer and, in the same act, from every device reachable only through it; the transport's directory drops those devices on its next read (KnownDevices, gossip-directory.ts). */
|
|
61
|
+
private removeIssuer;
|
|
62
|
+
/** Marks a remote user-principal device-id (hex, case-insensitive; user-identity.ts) as trusted (agent-comms#187): a peer presenting a token whose delegation chain roots at this principal is trusted via `isTrustedFor` below, without that peer's own bare device-id ever needing individual trust, and a device whose gossiped proof this principal vouches for becomes reachable. Idempotent, and entirely independent of the bare-device allowlist `add` manages. Persists the updated set when this instance was constructed with a location. */
|
|
48
63
|
addPrincipal(deviceHex: string): void;
|
|
49
|
-
/** Withdraws a previously trusted principal (hex, case-insensitive). A no-op if it was never trusted.
|
|
64
|
+
/** Withdraws a previously trusted principal (hex, case-insensitive), and every device reachable only through it. A no-op if it was never trusted. Persists the updated set when this instance was constructed with a location. */
|
|
50
65
|
removePrincipal(deviceHex: string): void;
|
|
51
66
|
/** Every currently trusted principal device-id, lowercase hex, in insertion order. */
|
|
52
67
|
listPrincipals(): string[];
|
|
53
68
|
/** Whether the given device-id (hex, case-insensitive) is currently trusted as a user principal. */
|
|
54
69
|
isTrustedPrincipal(deviceHex: string): boolean;
|
|
70
|
+
/** Marks a remote machine device-id (hex, case-insensitive; machine-identity.ts) as trusted (agent-comms#343): every device whose gossiped proof this machine vouches for becomes reachable, without being trusted individually. Idempotent. Persists the updated set when this instance was constructed with a location. */
|
|
71
|
+
addMachine(deviceHex: string): void;
|
|
72
|
+
/** Withdraws a previously trusted machine (hex, case-insensitive), and in the same act every device reachable only because that machine vouched for it, revoking that machine key's bridges on the host. A device also trusted by id or through a trusted principal stays reachable by that route. A no-op if the machine was never trusted. Persists the updated set when this instance was constructed with a location. */
|
|
73
|
+
removeMachine(deviceHex: string): void;
|
|
74
|
+
/** Every currently trusted machine device-id, lowercase hex, in insertion order. */
|
|
75
|
+
listMachines(): string[];
|
|
55
76
|
/**
|
|
56
77
|
* Whether a peer is trusted, given a token's own bearer and chain-root device-ids (hex, case-insensitive) a caller has already chain-verified via `verifyCapabilityToken` (the same `rootIssuer` output device-membership-verification.ts's own chain check already relies on). Passes when the bearer itself is directly trusted (the existing bare-device path, unmodified for a peer with no principal at all), OR when the chain's root is a trusted principal -- so a device sub-delegated from an admitted principal (mirroring agent-comms#161's own "principal admits its own devices" pattern) is trusted without ever being individually allowlisted. This class performs no cryptographic verification itself; the caller supplies already-verified device-ids, keeping GatewayTrust the same plain allowlist it always was.
|
|
57
78
|
*/
|
|
58
79
|
isTrustedFor(bearerHex: string, rootIssuerHex: string): boolean;
|
|
59
80
|
/**
|
|
60
|
-
* Whether at least one remote device or
|
|
81
|
+
* Whether at least one remote device, principal or machine is currently trusted: the outbound gossip gate. wire-mesh-core's relay-hub broadcasts a gossiped advert to every connected hub peer with no per-recipient targeting (RelayHub.handleConnection's own "re-broadcasts each gossip frame to every other connected client"), so "advertise local agents only to allowlisted remote gateways" can only be approximated at the coarse granularity this side actually controls: don't advertise anything at all until the operator has opted in by trusting at least one remote device, principal or machine. Once true, an advertisement still reaches every hub-connected peer, trusted or not; the per-device isTrusted()/isTrustedFor() checks above are what keep this side from ACTING on anything an untrusted peer sends back, which is the boundary that actually matters.
|
|
61
82
|
*/
|
|
62
83
|
hasAny(): boolean;
|
|
63
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-trust.d.ts","sourceRoot":"","sources":["../../src/core/gateway-trust.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gateway-trust.d.ts","sourceRoot":"","sources":["../../src/core/gateway-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,qBAAqB,CAAC;AAO5E,2XAA2X;AAC3X,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,YAAY,EACV,WAAW,GACX,aAAa,GACb,aAAa,GACb,QAAQ,GACR,oBAAoB,GACpB,cAAc,GACd,gBAAgB,GAChB,cAAc,GACd,oBAAoB,CACvB,CAAC;AAEF,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;AAEhD,+FAA+F;AAC/F,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG7B;IACF,4SAA4S;IAC5S,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG5B;IACJ,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkD;IAC3E,iJAAiJ;IACjJ,OAAO,CAAC,KAAK,CAAqB;IAElC,kXAAkX;gBACtW,QAAQ,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAK1D,kQAAkQ;IAClQ,OAAO,CAAC,IAAI;IAkBZ,wMAAwM;IACxM,OAAO,CAAC,OAAO;IAKf,uRAAuR;IACvR,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAM5B,qXAAqX;IACrX,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAM/B,mMAAmM;IACnM,OAAO,CAAC,OAAO;IAWf,4EAA4E;IAC5E,IAAI,IAAI,MAAM,EAAE;IAKhB,wVAAwV;IACxV,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKrC,uWAAuW;IACvW,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAUvC,oVAAoV;IACpV,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO;IAKxD,obAAob;IACpb,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC1B,SAAS,EAAE,MAAM,GAChB,IAAI;IAgBP,wMAAwM;IACxM,mBAAmB,IAAI,cAAc,EAAE;IAOvC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,SAAS;IAMjB,6MAA6M;IAC7M,OAAO,CAAC,YAAY;IAYpB,whBAAwhB;IACxhB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,kOAAkO;IAClO,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIxC,sFAAsF;IACtF,cAAc,IAAI,MAAM,EAAE;IAK1B,oGAAoG;IACpG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAK9C,6TAA6T;IAC7T,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAInC,6ZAA6Z;IAC7Z,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,oFAAoF;IACpF,YAAY,IAAI,MAAM,EAAE;IAKxB;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO;IAI/D;;OAEG;IACH,MAAM,IAAI,OAAO;CAQlB"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { gatewayTrustStamp, loadGatewayTrust, saveGatewayTrust, } from "./identity-store.js";
|
|
2
2
|
export class GatewayTrust {
|
|
3
3
|
trusted = new Set();
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
trustedIssuers = {
|
|
5
|
+
principal: new Set(),
|
|
6
|
+
machine: new Set(),
|
|
7
|
+
};
|
|
8
|
+
/** Devices whose gossiped membership proof was verified against a trusted issuer, keyed by memberKey (a device can hold one of each kind), with that issuer and when the proof lapses. In memory only: a proof is short-lived and re-verified from the next advert, so nothing here is worth persisting. */
|
|
6
9
|
verifiedMembers = new Map();
|
|
7
10
|
location;
|
|
8
11
|
/** gatewayTrustStamp as of the last load or write this instance made, so refresh() can tell when another process has replaced the file since. */
|
|
@@ -19,12 +22,16 @@ export class GatewayTrust {
|
|
|
19
22
|
this.stamp = gatewayTrustStamp(this.location);
|
|
20
23
|
const loaded = loadGatewayTrust(this.location);
|
|
21
24
|
this.trusted.clear();
|
|
22
|
-
this.
|
|
25
|
+
this.trustedIssuers.principal.clear();
|
|
26
|
+
this.trustedIssuers.machine.clear();
|
|
23
27
|
for (const deviceHex of loaded.devices) {
|
|
24
28
|
this.trusted.add(deviceHex);
|
|
25
29
|
}
|
|
26
30
|
for (const principalHex of loaded.principals) {
|
|
27
|
-
this.
|
|
31
|
+
this.trustedIssuers.principal.add(principalHex);
|
|
32
|
+
}
|
|
33
|
+
for (const machineHex of loaded.machines) {
|
|
34
|
+
this.trustedIssuers.machine.add(machineHex);
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
/** Reloads from disk when another process has replaced the file since this instance last looked. Called first by every method that reads the sets, so a trust decision is never older than the file. */
|
|
@@ -40,17 +47,22 @@ export class GatewayTrust {
|
|
|
40
47
|
this.trusted.add(deviceHex.toLowerCase());
|
|
41
48
|
this.persist();
|
|
42
49
|
}
|
|
43
|
-
/** Withdraws a previously trusted device-id (hex, case-insensitive). A no-op if it was never trusted.
|
|
50
|
+
/** Withdraws a previously trusted device-id (hex, case-insensitive). A no-op if it was never trusted. Requests already in flight are unaffected; a directory entry merged from the hub because of this trust alone leaves the transport's view on its next read (KnownDevices, gossip-directory.ts). Persists the updated set when this instance was constructed with a location. */
|
|
44
51
|
remove(deviceHex) {
|
|
45
52
|
this.refresh();
|
|
46
53
|
this.trusted.delete(deviceHex.toLowerCase());
|
|
47
54
|
this.persist();
|
|
48
55
|
}
|
|
49
|
-
/** Writes the complete current trusted device and
|
|
56
|
+
/** Writes the complete current trusted device, principal and machine sets back to the shared trust file, if this instance was constructed with a location. A no-op for the in-memory-only case. */
|
|
50
57
|
persist() {
|
|
51
58
|
if (this.location === undefined)
|
|
52
59
|
return;
|
|
53
|
-
|
|
60
|
+
const trust = {
|
|
61
|
+
devices: [...this.trusted],
|
|
62
|
+
principals: [...this.trustedIssuers.principal],
|
|
63
|
+
machines: [...this.trustedIssuers.machine],
|
|
64
|
+
};
|
|
65
|
+
saveGatewayTrust(this.location, trust);
|
|
54
66
|
this.stamp = gatewayTrustStamp(this.location);
|
|
55
67
|
}
|
|
56
68
|
/** Every currently trusted device-id, lowercase hex, in insertion order. */
|
|
@@ -58,70 +70,101 @@ export class GatewayTrust {
|
|
|
58
70
|
this.refresh();
|
|
59
71
|
return [...this.trusted];
|
|
60
72
|
}
|
|
61
|
-
/** Whether the given device-id (hex, case-insensitive) is trusted by id: on the bare-device allowlist. A device trusted only because a principal vouches for it (noteVerifiedMember) is not, on purpose: its proof is a claim that anyone who has read the device's gossiped advert can repeat, so it earns only what isReachable grants. */
|
|
73
|
+
/** Whether the given device-id (hex, case-insensitive) is trusted by id: on the bare-device allowlist. A device trusted only because a principal or machine vouches for it (noteVerifiedMember) is not, on purpose: its proof is a claim that anyone who has read the device's gossiped advert can repeat, so it earns only what isReachable grants. */
|
|
62
74
|
isTrusted(deviceHex) {
|
|
63
75
|
this.refresh();
|
|
64
76
|
return this.trusted.has(deviceHex.toLowerCase());
|
|
65
77
|
}
|
|
66
|
-
/** Whether this side may merge the device's gossiped directory entry and route requests to it: trusted by id, or a verified member of a trusted principal. Requests routed to a device are still authenticated end to end by the session inside the relay, so a false claim can misroute one (which then fails its handshake) but not read or forge one. */
|
|
78
|
+
/** Whether this side may merge the device's gossiped directory entry and route requests to it: trusted by id, or a verified member of a trusted principal or machine. Requests routed to a device are still authenticated end to end by the session inside the relay, so a false claim can misroute one (which then fails its handshake) but not read or forge one. */
|
|
67
79
|
isReachable(deviceHex) {
|
|
68
80
|
this.refresh();
|
|
69
81
|
const key = deviceHex.toLowerCase();
|
|
70
|
-
return this.trusted.has(key) ||
|
|
82
|
+
return (this.trusted.has(key) ||
|
|
83
|
+
this.isVerifiedMember(key, "principal") ||
|
|
84
|
+
this.isVerifiedMember(key, "machine"));
|
|
71
85
|
}
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
/** Whether the device (hex, case-insensitive) is currently a verified member through a trusted issuer of the given kind: its proof of that kind has not lapsed and its issuer is still trusted. Unlike isReachable, this answers for one kind alone, so a device reachable through its principal is not taken to hold a live machine record too. */
|
|
87
|
+
isVouchedBy(deviceHex, kind) {
|
|
88
|
+
this.refresh();
|
|
89
|
+
return this.isVerifiedMember(deviceHex.toLowerCase(), kind);
|
|
90
|
+
}
|
|
91
|
+
/** Records that deviceHex presented a membership proof, valid until expiresAt (epoch ms), verified against voucher (agent-comms#266, agent-comms#343). From then until the proof lapses, or the voucher's issuer stops being trusted, isReachable treats the device as reachable. A later proof from the same kind of issuer never shortens an earlier one's window. The caller has already verified the proof: this class does no cryptography. */
|
|
92
|
+
noteVerifiedMember(deviceHex, voucher, expiresAt) {
|
|
93
|
+
const device = deviceHex.toLowerCase();
|
|
75
94
|
const now = Date.now();
|
|
76
|
-
for (const [
|
|
95
|
+
for (const [key, member] of this.verifiedMembers) {
|
|
77
96
|
if (member.expiresAt <= now)
|
|
78
|
-
this.verifiedMembers.delete(
|
|
97
|
+
this.verifiedMembers.delete(key);
|
|
79
98
|
}
|
|
99
|
+
const key = memberKey(device, voucher.kind);
|
|
80
100
|
const existing = this.verifiedMembers.get(key);
|
|
81
101
|
this.verifiedMembers.set(key, {
|
|
82
|
-
|
|
102
|
+
device,
|
|
103
|
+
kind: voucher.kind,
|
|
104
|
+
issuer: voucher.issuer.toLowerCase(),
|
|
83
105
|
expiresAt: Math.max(expiresAt, existing?.expiresAt ?? 0),
|
|
84
106
|
});
|
|
85
107
|
}
|
|
86
|
-
/** Every device currently reachable
|
|
108
|
+
/** Every device currently reachable because a trusted principal or machine vouches for it, once per vouching issuer. Excludes proofs that have lapsed and members whose issuer is no longer trusted. */
|
|
87
109
|
listVerifiedMembers() {
|
|
88
110
|
this.refresh();
|
|
89
|
-
return [...this.verifiedMembers]
|
|
90
|
-
.filter((
|
|
91
|
-
.map((
|
|
111
|
+
return [...this.verifiedMembers.values()]
|
|
112
|
+
.filter((member) => this.isVerifiedMember(member.device, member.kind))
|
|
113
|
+
.map(({ device, kind, issuer }) => ({ device, kind, issuer }));
|
|
92
114
|
}
|
|
93
|
-
isVerifiedMember(
|
|
94
|
-
const member = this.verifiedMembers.get(
|
|
115
|
+
isVerifiedMember(device, kind) {
|
|
116
|
+
const member = this.verifiedMembers.get(memberKey(device, kind));
|
|
95
117
|
return (member !== undefined &&
|
|
96
118
|
member.expiresAt > Date.now() &&
|
|
97
|
-
this.
|
|
119
|
+
this.trustedIssuers[kind].has(member.issuer));
|
|
98
120
|
}
|
|
99
|
-
|
|
100
|
-
addPrincipal(deviceHex) {
|
|
121
|
+
addIssuer(kind, deviceHex) {
|
|
101
122
|
this.refresh();
|
|
102
|
-
this.
|
|
123
|
+
this.trustedIssuers[kind].add(deviceHex.toLowerCase());
|
|
103
124
|
this.persist();
|
|
104
125
|
}
|
|
105
|
-
/** Withdraws
|
|
106
|
-
|
|
126
|
+
/** Withdraws trust from one issuer and, in the same act, from every device reachable only through it; the transport's directory drops those devices on its next read (KnownDevices, gossip-directory.ts). */
|
|
127
|
+
removeIssuer(kind, deviceHex) {
|
|
107
128
|
this.refresh();
|
|
108
|
-
const
|
|
109
|
-
this.
|
|
110
|
-
for (const [
|
|
111
|
-
if (member.
|
|
112
|
-
this.verifiedMembers.delete(
|
|
129
|
+
const issuer = deviceHex.toLowerCase();
|
|
130
|
+
this.trustedIssuers[kind].delete(issuer);
|
|
131
|
+
for (const [key, member] of this.verifiedMembers) {
|
|
132
|
+
if (member.kind === kind && member.issuer === issuer) {
|
|
133
|
+
this.verifiedMembers.delete(key);
|
|
134
|
+
}
|
|
113
135
|
}
|
|
114
136
|
this.persist();
|
|
115
137
|
}
|
|
138
|
+
/** Marks a remote user-principal device-id (hex, case-insensitive; user-identity.ts) as trusted (agent-comms#187): a peer presenting a token whose delegation chain roots at this principal is trusted via `isTrustedFor` below, without that peer's own bare device-id ever needing individual trust, and a device whose gossiped proof this principal vouches for becomes reachable. Idempotent, and entirely independent of the bare-device allowlist `add` manages. Persists the updated set when this instance was constructed with a location. */
|
|
139
|
+
addPrincipal(deviceHex) {
|
|
140
|
+
this.addIssuer("principal", deviceHex);
|
|
141
|
+
}
|
|
142
|
+
/** Withdraws a previously trusted principal (hex, case-insensitive), and every device reachable only through it. A no-op if it was never trusted. Persists the updated set when this instance was constructed with a location. */
|
|
143
|
+
removePrincipal(deviceHex) {
|
|
144
|
+
this.removeIssuer("principal", deviceHex);
|
|
145
|
+
}
|
|
116
146
|
/** Every currently trusted principal device-id, lowercase hex, in insertion order. */
|
|
117
147
|
listPrincipals() {
|
|
118
148
|
this.refresh();
|
|
119
|
-
return [...this.
|
|
149
|
+
return [...this.trustedIssuers.principal];
|
|
120
150
|
}
|
|
121
151
|
/** Whether the given device-id (hex, case-insensitive) is currently trusted as a user principal. */
|
|
122
152
|
isTrustedPrincipal(deviceHex) {
|
|
123
153
|
this.refresh();
|
|
124
|
-
return this.
|
|
154
|
+
return this.trustedIssuers.principal.has(deviceHex.toLowerCase());
|
|
155
|
+
}
|
|
156
|
+
/** Marks a remote machine device-id (hex, case-insensitive; machine-identity.ts) as trusted (agent-comms#343): every device whose gossiped proof this machine vouches for becomes reachable, without being trusted individually. Idempotent. Persists the updated set when this instance was constructed with a location. */
|
|
157
|
+
addMachine(deviceHex) {
|
|
158
|
+
this.addIssuer("machine", deviceHex);
|
|
159
|
+
}
|
|
160
|
+
/** Withdraws a previously trusted machine (hex, case-insensitive), and in the same act every device reachable only because that machine vouched for it, revoking that machine key's bridges on the host. A device also trusted by id or through a trusted principal stays reachable by that route. A no-op if the machine was never trusted. Persists the updated set when this instance was constructed with a location. */
|
|
161
|
+
removeMachine(deviceHex) {
|
|
162
|
+
this.removeIssuer("machine", deviceHex);
|
|
163
|
+
}
|
|
164
|
+
/** Every currently trusted machine device-id, lowercase hex, in insertion order. */
|
|
165
|
+
listMachines() {
|
|
166
|
+
this.refresh();
|
|
167
|
+
return [...this.trustedIssuers.machine];
|
|
125
168
|
}
|
|
126
169
|
/**
|
|
127
170
|
* Whether a peer is trusted, given a token's own bearer and chain-root device-ids (hex, case-insensitive) a caller has already chain-verified via `verifyCapabilityToken` (the same `rootIssuer` output device-membership-verification.ts's own chain check already relies on). Passes when the bearer itself is directly trusted (the existing bare-device path, unmodified for a peer with no principal at all), OR when the chain's root is a trusted principal -- so a device sub-delegated from an admitted principal (mirroring agent-comms#161's own "principal admits its own devices" pattern) is trusted without ever being individually allowlisted. This class performs no cryptographic verification itself; the caller supplies already-verified device-ids, keeping GatewayTrust the same plain allowlist it always was.
|
|
@@ -130,11 +173,17 @@ export class GatewayTrust {
|
|
|
130
173
|
return this.isTrusted(bearerHex) || this.isTrustedPrincipal(rootIssuerHex);
|
|
131
174
|
}
|
|
132
175
|
/**
|
|
133
|
-
* Whether at least one remote device or
|
|
176
|
+
* Whether at least one remote device, principal or machine is currently trusted: the outbound gossip gate. wire-mesh-core's relay-hub broadcasts a gossiped advert to every connected hub peer with no per-recipient targeting (RelayHub.handleConnection's own "re-broadcasts each gossip frame to every other connected client"), so "advertise local agents only to allowlisted remote gateways" can only be approximated at the coarse granularity this side actually controls: don't advertise anything at all until the operator has opted in by trusting at least one remote device, principal or machine. Once true, an advertisement still reaches every hub-connected peer, trusted or not; the per-device isTrusted()/isTrustedFor() checks above are what keep this side from ACTING on anything an untrusted peer sends back, which is the boundary that actually matters.
|
|
134
177
|
*/
|
|
135
178
|
hasAny() {
|
|
136
179
|
this.refresh();
|
|
137
|
-
return this.trusted.size > 0 ||
|
|
180
|
+
return (this.trusted.size > 0 ||
|
|
181
|
+
this.trustedIssuers.principal.size > 0 ||
|
|
182
|
+
this.trustedIssuers.machine.size > 0);
|
|
138
183
|
}
|
|
139
184
|
}
|
|
185
|
+
/** verifiedMembers' key: one entry per device per kind of issuer. */
|
|
186
|
+
function memberKey(device, kind) {
|
|
187
|
+
return `${kind}:${device}`;
|
|
188
|
+
}
|
|
140
189
|
//# sourceMappingURL=gateway-trust.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-trust.js","sourceRoot":"","sources":["../../src/core/gateway-trust.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gateway-trust.js","sourceRoot":"","sources":["../../src/core/gateway-trust.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AA8B7B,MAAM,OAAO,YAAY;IACN,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5B,cAAc,GAA6C;QAC1E,SAAS,EAAE,IAAI,GAAG,EAAU;QAC5B,OAAO,EAAE,IAAI,GAAG,EAAU;KAC3B,CAAC;IACF,4SAA4S;IAC3R,eAAe,GAAG,IAAI,GAAG,EAGvC,CAAC;IACa,QAAQ,CAAkD;IAC3E,iJAAiJ;IACzI,KAAK,CAAqB;IAElC,kXAAkX;IAClX,YAAY,QAA8C;QACxD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,kQAAkQ;IAC1P,IAAI;QACV,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO;QACxC,IAAI,CAAC,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACtC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACpC,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAC7C,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAClD,CAAC;QACD,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACzC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,wMAAwM;IAChM,OAAO;QACb,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO;QACxC,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACnE,CAAC;IAED,uRAAuR;IACvR,GAAG,CAAC,SAAiB;QACnB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,qXAAqX;IACrX,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,mMAAmM;IAC3L,OAAO;QACb,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO;QACxC,MAAM,KAAK,GAAuB;YAChC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;YAC1B,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC;YAC9C,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC;SAC3C,CAAC;QACF,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,4EAA4E;IAC5E,IAAI;QACF,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,wVAAwV;IACxV,SAAS,CAAC,SAAiB;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,uWAAuW;IACvW,WAAW,CAAC,SAAiB;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACpC,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YACrB,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,WAAW,CAAC;YACvC,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,SAAS,CAAC,CACtC,CAAC;IACJ,CAAC;IAED,oVAAoV;IACpV,WAAW,CAAC,SAAiB,EAAE,IAAe;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC;IAC9D,CAAC;IAED,obAAob;IACpb,kBAAkB,CAChB,SAAiB,EACjB,OAA0B,EAC1B,SAAiB;QAEjB,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACjD,IAAI,MAAM,CAAC,SAAS,IAAI,GAAG;gBAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE;YAC5B,MAAM;YACN,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE;YACpC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,IAAI,CAAC,CAAC;SACzD,CAAC,CAAC;IACL,CAAC;IAED,wMAAwM;IACxM,mBAAmB;QACjB,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;aACtC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;aACrE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACnE,CAAC;IAEO,gBAAgB,CAAC,MAAc,EAAE,IAAe;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QACjE,OAAO,CACL,MAAM,KAAK,SAAS;YACpB,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;YAC7B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAC7C,CAAC;IACJ,CAAC;IAEO,SAAS,CAAC,IAAe,EAAE,SAAiB;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,6MAA6M;IACrM,YAAY,CAAC,IAAe,EAAE,SAAiB;QACrD,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACjD,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBACrD,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,whBAAwhB;IACxhB,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,kOAAkO;IAClO,eAAe,CAAC,SAAiB;QAC/B,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,sFAAsF;IACtF,cAAc;QACZ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,oGAAoG;IACpG,kBAAkB,CAAC,SAAiB;QAClC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,6TAA6T;IAC7T,UAAU,CAAC,SAAiB;QAC1B,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IACvC,CAAC;IAED,6ZAA6Z;IAC7Z,aAAa,CAAC,SAAiB;QAC7B,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,oFAAoF;IACpF,YAAY;QACV,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,SAAiB,EAAE,aAAqB;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAC7E,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC;YACrB,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC;YACtC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CACrC,CAAC;IACJ,CAAC;CACF;AAED,qEAAqE;AACrE,SAAS,SAAS,CAAC,MAAc,EAAE,IAAe;IAChD,OAAO,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -9,8 +9,26 @@ import { type AgentSelfAdvert, type HostedRoomAdvert } from "./gossip-extensions
|
|
|
9
9
|
import type { HubSession } from "./hub-session.js";
|
|
10
10
|
/** Merges one session event's own directory into the mesh-wide knownDevices view (mutated in place), keeping the newer advert (by snapshot-seconds) whenever a device-id is already known from an earlier event or a different session. An equal snapshot replaces the held advert, unlike the relay hub, which lets an advert take a device's registration over from a different connection only when it is strictly newer: the hub is guarding a route against a replayed advert, whereas every entry here has already been verified by the session that produced it, so an equal one carries the same content and the freshest copy is the one to keep. */
|
|
11
11
|
export declare function mergeKnownDevices(knownDevices: Map<string, PeerAdvert>, directory: readonly DirectoryEntry[]): void;
|
|
12
|
+
/**
|
|
13
|
+
* The mesh-wide gossip directory a transport reads (agent-comms#48): the latest advert of every device it has heard of, from its local peer sessions and from the hub, kept across disconnects so a device's last-known advert stays queryable while its session is down.
|
|
14
|
+
*
|
|
15
|
+
* A device heard of through a local peer session is kept for good, since the local mesh admits its peers by its own approval rather than by gateway trust. A device heard of only through the hub is kept only while the hub would still admit it (admitted, the same decision directoryAdmission makes), and that is checked on every read (agent-comms#343): withdrawing trust from a device, principal or machine, in this process or in another that shares the trust file, removes from the view every device that trust alone let in, and so does a membership proof lapsing. Nothing re-adds such a device unless the hub admits it again.
|
|
16
|
+
*/
|
|
17
|
+
export declare class KnownDevices {
|
|
18
|
+
private readonly admitted;
|
|
19
|
+
private readonly adverts;
|
|
20
|
+
private readonly heardLocally;
|
|
21
|
+
/** admitted answers whether the hub would still admit the device (hex) today. */
|
|
22
|
+
constructor(admitted: (deviceHex: string) => boolean);
|
|
23
|
+
/** Merges a local peer session's directory (see mergeKnownDevices). */
|
|
24
|
+
mergeLocal(directory: readonly DirectoryEntry[]): void;
|
|
25
|
+
/** Merges the hub session's admitted directory (see mergeKnownDevices). */
|
|
26
|
+
mergeHub(directory: readonly DirectoryEntry[]): void;
|
|
27
|
+
/** The current view, keyed by device-id hex, after dropping every hub-only device the hub would no longer admit. */
|
|
28
|
+
current(): ReadonlyMap<string, Readonly<PeerAdvert>>;
|
|
29
|
+
}
|
|
12
30
|
/** Merges a hub session's own admitted directory into knownDevices and announces each device in it as reachable. Every entry the hub hands over here has already passed admitEntries, so a device named in one is both present on the hub and trusted or vouched for by this side: exactly the moment a request queued for it becomes worth retrying, and the only such moment for a device that has no local peer session of its own to connect. */
|
|
13
|
-
export declare function mergeAndAnnounceReachable(knownDevices:
|
|
31
|
+
export declare function mergeAndAnnounceReachable(knownDevices: KnownDevices, directory: readonly DirectoryEntry[], events: Readonly<Pick<TransportEvents, "onDeviceReachable">>): void;
|
|
14
32
|
/** Every device this side has ever heard gossip from, mesh-wide -- not just its own directly-connected peers -- with each one's own latest full advert (addresses, snapshot-seconds, and every open-extension field such as presence/status). The trivial array-conversion half of WireMeshTransport's own listKnownDevices, split out purely to keep that file under the repo's max-lines cap, the same reason mergeKnownDevices above already lives here rather than there. */
|
|
15
33
|
export declare function listKnownDevicesEntries(knownDevices: ReadonlyMap<string, Readonly<PeerAdvert>>): readonly {
|
|
16
34
|
deviceId: string;
|
|
@@ -18,8 +36,10 @@ export declare function listKnownDevicesEntries(knownDevices: ReadonlyMap<string
|
|
|
18
36
|
}[];
|
|
19
37
|
/** Reads a presence extension from one specific device's own gossiped self-advert, if this event's directory carries a fresh one for exactly that device-id -- never for any other device-id a multi-hop directory might mention, since only the session's own authenticated peer's advert is that session's business to report. Returns undefined for a missing presence/status key, or a value that isn't a recognised AgentStatus -- an advert simply not participating in this convention, not an error (the same verifier obligation peer-advert's own open extension tail is documented under). */
|
|
20
38
|
export declare function findPresenceAdvert(deviceIdHex: string, directory: readonly DirectoryEntry[]): AgentStatus | undefined;
|
|
21
|
-
/**
|
|
22
|
-
export
|
|
39
|
+
/** Which agent/self field carries which issuer's proof: `membership` the user principal's, `machine` the machine's. */
|
|
40
|
+
export type MembershipProofField = "membership" | "machine";
|
|
41
|
+
/** The membership proof a peer gossiped in its agent/self advert under field, 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. */
|
|
42
|
+
export declare function readMembershipProof(advert: Readonly<Record<string, unknown>>, field: MembershipProofField): string | undefined;
|
|
23
43
|
/** The one thing gossip re-advertisement needs from a session: somewhere to send the extension bag. */
|
|
24
44
|
export type GossipSession = Pick<AcceptedMeshSession, "sendGossipUpdate">;
|
|
25
45
|
/** Which session, if any, is the hub's. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gossip-directory.d.ts","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACf,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAKhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,8nBAA8nB;AAC9nB,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACrC,SAAS,EAAE,SAAS,cAAc,EAAE,GACnC,IAAI,CAWN;AAED,
|
|
1
|
+
{"version":3,"file":"gossip-directory.d.ts","sourceRoot":"","sources":["../../src/core/gossip-directory.ts"],"names":[],"mappings":"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"}
|