runtime: RPC replies ride mesh.rpc; an invoke subcommand (ADR 0052)
Replies go through the RPC exchange keyed by the caller's reply-queue name, not the default exchange — so a serving module's scoped account answers with write on mesh.rpc alone, never the default exchange (which would let it publish into any queue). 'mesh-tools invoke <module> <tool> [args]' is the caller's side, the sibling of emit. Verified against a real broker: a scoped account serves its tool and is refused another module's serve queue.
This commit is contained in:
+7
-1
@@ -75,6 +75,10 @@ export async function connectAmqp(
|
||||
async function ensureReply(): Promise<string> {
|
||||
if (replyQueue) return replyQueue;
|
||||
const { queue } = await ch.assertQueue("", { exclusive: true });
|
||||
// Replies come back through the RPC exchange keyed by this queue's own name, not the default
|
||||
// exchange (novox/hq ADR 0052): a serving module's scoped account may write to mesh.rpc but not
|
||||
// the default exchange, which would let it publish into any queue on the broker.
|
||||
await ch.bindQueue(queue, RPC_EXCHANGE, queue);
|
||||
replyQueue = queue;
|
||||
await ch.consume(
|
||||
queue,
|
||||
@@ -161,7 +165,9 @@ export async function connectAmqp(
|
||||
reply = { error: err instanceof Error ? err.message : String(err) };
|
||||
}
|
||||
if (msg.properties.replyTo) {
|
||||
ch.sendToQueue(msg.properties.replyTo, Buffer.from(JSON.stringify(reply)), {
|
||||
// Reply through the RPC exchange, keyed by the caller's reply-queue name, so a scoped
|
||||
// account answers with write on mesh.rpc alone — never the default exchange (ADR 0052).
|
||||
ch.publish(RPC_EXCHANGE, msg.properties.replyTo, Buffer.from(JSON.stringify(reply)), {
|
||||
correlationId: msg.properties.correlationId,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user