From beec369367df998d5579a79ad2d4fb1e16930eb1 Mon Sep 17 00:00:00 2001 From: ThinkOff Date: Sat, 4 Jul 2026 18:55:52 +0300 Subject: [PATCH] fix(daemon): only the owner may settle intents via room /approve replies The chat-reply poller accepted /approve and /deny from any room sender, so any fleet agent could settle a pending intent and trigger its gated command. Restrict settlement to the human owner: plain "petrus" sender (CodeWatch button taps included, which post with isHuman=false) or a human-flagged message. Agent senders are logged and skipped. Co-Authored-By: Claude Fable 5 --- bin/iak-mcp-daemon.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bin/iak-mcp-daemon.mjs b/bin/iak-mcp-daemon.mjs index 35edb61..32dce80 100755 --- a/bin/iak-mcp-daemon.mjs +++ b/bin/iak-mcp-daemon.mjs @@ -137,6 +137,16 @@ function startChatReplyPoller({ apiKey, room, intervalMs }) { const text = (m.body || '').trim(); const match = text.match(/^\/(approve|deny)\s+([a-f0-9]+)$/i); if (!match) continue; + // Only the human owner may settle intents. Fleet agents share the room + // and can echo "/approve " (one did), which would execute gated + // commands without the owner. The owner posts as plain "petrus" — + // including CodeWatch button taps, which arrive with isHuman=false — + // while agents carry a handle ("@ether", "hermes"). + const sender = String(m.from || '').replace(/^@/, '').toLowerCase(); + if (sender !== 'petrus' && m.isHuman !== true) { + console.log(`[iak-mcp-daemon] ${text} from ${m.from}: sender is not the owner — ignoring`); + continue; + } const decision = match[1].toLowerCase(); const id = match[2]; const intent = getIntent(id);