agent-comms#187 built the principal-trust primitive (GatewayTrust.addPrincipal/isTrustedFor, admitAgentForDm's delegable-principal path, delegateDmSendToDevice) and proved it with a real end-to-end test, but the actual gateway enforcement gate never got wired to consume it. Confirmed by reading hub-session.ts directly: deps.isTrusted(deviceHex) is checked against every gossiped directory entry and every relayed manage-request before either is acted on, and it's a bare boolean over a device-id string, never isTrustedFor. Gossiped adverts and relayed frames carry no verifiable capability token at this layer at all, so there is nothing to chain-walk against a trusted principal even if the call site were changed.
Practical effect: the gateway's own admission gate, the thing deciding whether cross-machine traffic gets acted on at all, still requires trusting each of the other person's devices individually via the bare gateway_trust path. #187's principal path only helps one layer later, for dm:send admission once traffic is already past the gateway gate. This is the real remaining friction confirmed while answering "are we able to do user-user agent comms."
Closing it for real needs a new wire mechanism, not just a call-site change: a gossiped advert or relayed request would need to carry a verifiable token (or at minimum a signature back to a principal) the gateway can check against isTrustedFor, since bare device-ids carry no such proof today. GatewayTrust's own doc comment already names "gating the hub itself" as deliberately out of scope for #156; this is a related but distinct question, since it's about what a gateway checks on already-flowing gossip/relay traffic, not about gating the hub's own connection acceptance.
Decided: stop gating room-domain verbs behind the coarse bare-device isTrusted check at all. This session's own hub-session.ts already argues the right answer in its own doc comment without the code following through on it: "A real room-domain verb (room.send, room.join, room.notify, ...) carries no equivalent risk, it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over." That check is real cryptographic verification (signature plus chain-walk) and, since #187, already principal-aware via isTrustedFor. Stacking the coarse bare-device gate in front of it is redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is "ask a human") it actively defeats the protocol's own intended design by never letting an untrusted device reach that approval step at all.
Concretely: consume()'s isTrusted check should apply only to traffic with no independent per-message security of its own, the legacy FRAME_VERB path (state_sync/state_update, the genuine spoofed-delivery risk PR #169's review caught and fixed) and gossip directory-merge. Room-domain verbs bypass the gateway-level device gate entirely and rely on their own capability-token verification (or, for room.join, the existing human-approval flow) exactly as the code's own comment already describes as the intended model.
One real, free improvement while touching this: gossip directory-merge currently checks only bare-device isTrusted. Extend it to also accept GatewayTrust's principal set, so a gossiped entry from a device already known to belong to a trusted principal (via some prior verified interaction) surfaces without needing its own separate individual bootstrap.
No wire-mesh-core protocol change needed (rejected: gossip/relay frames carrying a verifiable token, the biggest and slowest option, when the actual fix is removing a redundant check rather than adding a new one). No new human-approval mechanism needed (rejected: treating #188's connection-code bootstrap as sufficient on its own, since it still requires re-bootstrapping every one of a principal's devices individually, which is exactly the friction principal-keyed trust exists to remove).
agent-comms#187 built the principal-trust primitive (
GatewayTrust.addPrincipal/isTrustedFor,admitAgentForDm's delegable-principal path,delegateDmSendToDevice) and proved it with a real end-to-end test, but the actual gateway enforcement gate never got wired to consume it. Confirmed by readinghub-session.tsdirectly:deps.isTrusted(deviceHex)is checked against every gossiped directory entry and every relayed manage-request before either is acted on, and it's a bare boolean over a device-id string, neverisTrustedFor. Gossiped adverts and relayed frames carry no verifiable capability token at this layer at all, so there is nothing to chain-walk against a trusted principal even if the call site were changed.Practical effect: the gateway's own admission gate, the thing deciding whether cross-machine traffic gets acted on at all, still requires trusting each of the other person's devices individually via the bare
gateway_trustpath. #187's principal path only helps one layer later, fordm:sendadmission once traffic is already past the gateway gate. This is the real remaining friction confirmed while answering "are we able to do user-user agent comms."Closing it for real needs a new wire mechanism, not just a call-site change: a gossiped advert or relayed request would need to carry a verifiable token (or at minimum a signature back to a principal) the gateway can check against
isTrustedFor, since bare device-ids carry no such proof today.GatewayTrust's own doc comment already names "gating the hub itself" as deliberately out of scope for #156; this is a related but distinct question, since it's about what a gateway checks on already-flowing gossip/relay traffic, not about gating the hub's own connection acceptance.Decided: stop gating room-domain verbs behind the coarse bare-device isTrusted check at all. This session's own hub-session.ts already argues the right answer in its own doc comment without the code following through on it: "A real room-domain verb (room.send, room.join, room.notify, ...) carries no equivalent risk, it is independently gated by its own room:member capability token, verified regardless of which transport path it arrived over." That check is real cryptographic verification (signature plus chain-walk) and, since #187, already principal-aware via isTrustedFor. Stacking the coarse bare-device gate in front of it is redundant, not protective, and for room.join specifically (the one deliberately ungated verb, whose own security model is "ask a human") it actively defeats the protocol's own intended design by never letting an untrusted device reach that approval step at all.
Concretely: consume()'s isTrusted check should apply only to traffic with no independent per-message security of its own, the legacy FRAME_VERB path (state_sync/state_update, the genuine spoofed-delivery risk PR #169's review caught and fixed) and gossip directory-merge. Room-domain verbs bypass the gateway-level device gate entirely and rely on their own capability-token verification (or, for room.join, the existing human-approval flow) exactly as the code's own comment already describes as the intended model.
One real, free improvement while touching this: gossip directory-merge currently checks only bare-device isTrusted. Extend it to also accept GatewayTrust's principal set, so a gossiped entry from a device already known to belong to a trusted principal (via some prior verified interaction) surfaces without needing its own separate individual bootstrap.
No wire-mesh-core protocol change needed (rejected: gossip/relay frames carrying a verifiable token, the biggest and slowest option, when the actual fix is removing a redundant check rather than adding a new one). No new human-approval mechanism needed (rejected: treating #188's connection-code bootstrap as sufficient on its own, since it still requires re-bootstrapping every one of a principal's devices individually, which is exactly the friction principal-keyed trust exists to remove).