fix(broker): topic-tagged sends bypass direct-target pre-flight
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

handleSend's pre-flight check rejected #<topicId> sends because the
target wasn't matched by @group / * / pubkey, so it fell into the
"direct" branch and looked for a peer with that pubkey. Topic targets
need their own class — delivery happens via topic_member, not by
matching connected peers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-05-02 02:01:35 +01:00
parent 1afae7a507
commit f98c2de5a3

View File

@@ -1855,16 +1855,22 @@ async function handleSend(
return; return;
} }
// Pre-flight: for direct sends (not @group, not *), verify at least one // Pre-flight: for direct sends (not @group, not #topic, not *), verify
// matching connected peer exists BEFORE queueing. Prevents silent drops // at least one matching connected peer exists BEFORE queueing. Prevents
// when a user sends to a typo, their own pubkey with no other session, // silent drops when a user sends to a typo, their own pubkey with no
// or a peer who has disconnected. The CLI's resolveClient already guards // other session, or a peer who has disconnected. The CLI's
// name-based targets; this catches raw-pubkey and CLI-bypassing clients. // resolveClient already guards name-based targets; this catches
// raw-pubkey and CLI-bypassing clients.
const isGroupTargetEarly = msg.targetSpec.startsWith("@"); const isGroupTargetEarly = msg.targetSpec.startsWith("@");
const isTopicTargetEarly = msg.targetSpec.startsWith("#");
const isBroadcastEarly = const isBroadcastEarly =
msg.targetSpec === "*" || msg.targetSpec === "*" ||
(isGroupTargetEarly && msg.targetSpec === "@all"); (isGroupTargetEarly && msg.targetSpec === "@all");
const isDirectEarly = !isGroupTargetEarly && !isBroadcastEarly && msg.targetSpec !== "*"; const isDirectEarly =
!isGroupTargetEarly &&
!isTopicTargetEarly &&
!isBroadcastEarly &&
msg.targetSpec !== "*";
if (isDirectEarly) { if (isDirectEarly) {
// Identify candidate recipient connections — anyone in the mesh whose // Identify candidate recipient connections — anyone in the mesh whose
// member or session pubkey matches the target. Then check grants to // member or session pubkey matches the target. Then check grants to