🐾 claw-stack
· Orange & Qiushi Wu openclaw imessage macos reliability

OpenClaw로 iMessage 신뢰성 확보하기: 3가지 문제와 해결 방법

Mac mini에서 OpenClaw의 iMessage 채널을 24/7로 운영하면서 발견된 3가지 신뢰성 문제 — FSEvents 병합, 첨부 파일 경로 샌드박싱, TCC 권한 재설정. 무엇이 잘못되었는지와 각각을 어떻게 해결했는지 설명합니다.

OpenClaw는 iMessage를 통신 채널로 사용할 수 있습니다. 즉, AI 에이전트에게 문자를 보내면 에이전트가 답장을 보냅니다. 간단해 보이지만, Mac mini에서 24/7로 운영하면서 3가지 신뢰성 문제가 발견되었으며, 이를 완전히 진단하는 데 몇 주가 걸렸습니다. 무엇이 잘못되었는지와 각각을 어떻게 해결했는지 설명하겠습니다.

설정

OpenClaw의 iMessage 플러그인은 FSEvents를 통해 ~/Library/Messages/chat.db를 감시하는 방식으로 작동합니다. 새 메시지가 도착하면 macOS가 chat.db에 쓰기를 수행하고, 감시자가 변경을 감지한 후 게이트웨이가 메시지를 처리합니다.

이론상으로는 즉시 처리되어야 합니다. 하지만 실제로는 3가지 방식으로 작동하지 않습니다.


문제 1: 유휴 상태에서 최대 5분까지 메시지 지연

증상: 메시지를 보내면 휴대폰에 “전송됨”으로 표시되지만, 에이전트가 응답하는 데 3~5분이 걸립니다. 그 후 갑자기 모든 것을 한 번에 처리합니다.

근본 원인: macOS의 전원 관리는 백그라운드 프로세스에 대해 FSEvents를 병합합니다. LaunchAgent plist의 ProcessType=Interactivecaffeinate 실행에도 불구하고, 커널은 저활성 기간 동안 chat.db의 vnode 이벤트를 배치합니다. imsg rpc 서브프로세스가 파일을 감시하지만, macOS는 “이 프로세스가 활성화되지 않았으니 파일 알림을 배치하자”고 결정합니다.

까다로운 이유: 메시지는 이미 chat.db에 있습니다. 지연되는 것은 메시지 자체가 아니라 알림입니다. 그래서 활성 사용 중에는 완벽하게 작동하지만, 머신이 유휴 상태일 때는 조용히 실패합니다.

해결 방법: 15초마다 chat.db를 확인하고 새 행이 나타나면 파일을 touch하여 새 FSEvent를 생성하는 폴링 스크립트:

#!/usr/bin/env node
// imsg-poller.mjs — Polls chat.db for new messages and wakes FSEvents watcher

import { execSync } from 'child_process';
import { utimesSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';

const CHATDB = join(homedir(), 'Library/Messages/chat.db');
const INTERVAL = 15000; // 15 seconds

function getMaxRowid() {
  try {
    return execSync(
      `/usr/bin/sqlite3 "${CHATDB}" "SELECT MAX(ROWID) FROM message;"`,
      { timeout: 5000, encoding: 'utf8' }
    ).trim() || '0';
  } catch { return '0'; }
}

let lastRowid = getMaxRowid();
if (lastRowid === '0') {
  console.error('ERROR: Cannot read chat.db — check Full Disk Access');
  process.exit(1);
}

console.log(`imsg-poller started. ROWID: ${lastRowid}, interval: ${INTERVAL}ms`);

setInterval(() => {
  const current = getMaxRowid();
  if (current !== '0' && current !== lastRowid) {
    console.log(`New message (ROWID ${lastRowid} -> ${current}), touching chat.db`);
    try {
      const now = new Date();
      utimesSync(CHATDB, now, now);
    } catch (e) {
      console.error(`touch failed: ${e.message}`);
    }
    lastRowid = current;
  }
}, INTERVAL);

bash 대신 Node.js를 사용한 이유는? 처음에는 bash 버전을 시도했지만, launchd에서 생성된 /bin/bash 프로세스는 Full Disk Access(TCC)를 상속받지 않습니다. stat 명령은 작동하지만 sqlite3는 “authorization denied” 오류를 받습니다. /opt/homebrew/bin/node를 사용하면 게이트웨이와 동일한 TCC 권한으로 FDA를 상속받기 때문에 작동합니다.

배포: KeepAlive: true로 LaunchAgent로 실행하세요:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>ai.openclaw.imsg-poller</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/homebrew/bin/node</string>
    <string>/path/to/imsg-poller.mjs</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>EnvironmentVariables</key>
  <dict>
    <key>HOME</key><string>/Users/youruser</string>
  </dict>
  <key>ThrottleInterval</key><integer>10</integer>
</dict>
</plist>

OpenClaw 업데이트를 견디나요? 네 — 독립적인 launchd 작업입니다.


문제 2: iMessage로 전송된 이미지가 “Path Not Allowed” 오류로 실패

증상: 에이전트가 iMessage를 통해 받은 이미지를 보내려고 하지만, “Local media path is not under an allowed directory” 오류가 발생합니다. 이미지는 ~/Library/Messages/Attachments/...에 존재하지만 OpenClaw의 미디어 샌드박싱이 차단합니다.

근본 원인: OpenClaw의 buildMediaLocalRoots() 함수는 미디어 파일 접근이 허용되는 디렉토리를 정의합니다. 작업 영역, 임시 디렉토리, 샌드박스를 포함하지만 ~/Library/Messages/Attachments/는 포함하지 않습니다. 에이전트가 iMessage로 받은 이미지를 전달하거나 처리하려고 하면, 경로가 거부됩니다.

해결 방법: Messages 첨부 파일 디렉토리를 허용된 루트에 추가하는 패치 스크립트:

#!/usr/bin/env bash
# patch-imessage-attachments.sh
# Adds ~/Library/Messages/Attachments to allowed media roots
# Re-run after every `npm update -g openclaw`

DIST="/opt/homebrew/lib/node_modules/openclaw/dist"

patched=0
for f in "$DIST"/ir-*.js; do
  [ -f "$f" ] || continue
  if grep -q "buildMediaLocalRoots" "$f" && \
     ! grep -q "Messages/Attachments" "$f"; then
    sed -i '' 's|path.join(resolvedStateDir, "sandboxes")|path.join(resolvedStateDir, "sandboxes"),\n\t\tpath.join(os.homedir(), "Library/Messages/Attachments")|' "$f"
    echo "Patched: $(basename $f)"
    patched=$((patched + 1))
  fi
done

echo "Done. Patched: $patched files"
echo "Run: openclaw gateway restart"

OpenClaw 업데이트를 견디나요? 아니오 — 컴파일된 JS 파일이 덮어씌워집니다. 모든 업데이트 후에 이를 다시 실행해야 합니다.


문제 3: macOS 업데이트가 Full Disk Access를 조용히 취소

증상: iMessage가 완전히 작동하지 않습니다. 수신된 메시지가 없고, 게이트웨이 로그에 합리적인 오류가 없습니다. 에이전트는 온라인으로 보이지만 응답하지 않습니다.

근본 원인: macOS 시스템 업데이트(및 경우에 따라 사소한 보안 패치)는 TCC(Transparency, Consent, and Control) 권한을 재설정할 수 있습니다. 이 경우 imsg 바이너리는 Full Disk Access를 잃게 되어, ~/Library/Messages/chat.db를 읽을 수 없습니다. 게이트웨이 로그는 다음을 표시합니다:

permissionDenied(path: "~/Library/Messages/chat.db",
  underlying: authorization denied (code: 23))

로그에서 이는 2월 13일2월 24일, 2026에 발생했습니다. 둘 다 macOS 업데이트와 관련된 시간입니다.

해결 방법: 안타깝게도 수동입니다.

  1. 게이트웨이 오류 로그 확인:

    grep "permissionDenied" ~/.openclaw/logs/gateway.err.log | tail -5
  2. code: 23이 보이면, 다음으로 이동하세요: System Settings → Privacy & Security → Full Disk Access

    imsg(또는 게이트웨이를 실행하는 Terminal/iTerm)가 FDA를 활성화했는지 확인하세요. 올바르게 보이지만 작동하지 않으면 토글을 끔/켜기 하세요.

  3. 확인:

    /opt/homebrew/bin/imsg chats --limit 1
    # Should return your most recent chat, not an error
  4. 재시작:

    openclaw gateway restart

OpenClaw 업데이트를 견디나요? 네 — TCC 권한은 시스템 수준입니다. 하지만 macOS 업데이트는 이를 재설정할 수 있습니다.


업데이트 후 체크리스트

매번 npm update -g openclaw를 실행할 때마다 다음을 하세요:

# 1. Re-apply patches (overwritten by update)
bash ~/.openclaw/autopatch/patch-imessage-attachments.sh

# 2. Restart gateway
openclaw gateway restart

# 3. Verify iMessage works
/opt/homebrew/bin/imsg chats --limit 1

macOS 업데이트 후에도 Full Disk Access 권한을 확인하세요.


OpenClaw가 업스트림에서 이를 해결해야 할까요?

문제 1 (FSEvents 병합)은 macOS 커널의 동작입니다. OpenClaw 자체에서 수정하기 어렵습니다. 폴러는 올바른 해결 방법입니다. OpenClaw는 선택적 구성 요소로 배포할 수 있습니다.

문제 2 (첨부 파일 경로)는 명확한 버그/oversight입니다. ~/Library/Messages/Attachments/는 iMessage 플러그인이 활성화되었을 때 기본 허용 루트에 포함되어야 합니다. 이는 업스트림의 한 줄 수정입니다.

문제 3 (TCC 재설정)은 Apple의 문제입니다. OpenClaw가 할 수 있는 것은 이를 감지하고 더 명확한 오류 메시지를 로깅하는 것뿐입니다.


배운 교훈

  1. “내 머신에서는 작동합니다”는 항상 켜져 있는 에이전트에게는 충분하지 않습니다. 이러한 버그는 며칠의 지속적인 운영이나 시스템 업데이트 후에만 나타납니다. AI 에이전트를 몇 주 동안 24/7로 실행해야 이를 찾을 수 있습니다.

  2. macOS는 헤드리스 서버용으로 설계되지 않았습니다. 전원 관리, TCC, FSEvents 병합 — 모두 사람이 화면 앞에 앉아 있다고 가정합니다. Mac mini에서 AI 에이전트를 실행하려면 모든 수준에서 OS와 싸워야 합니다.

  3. 패치 디렉토리를 유지하세요. 우리는 ~/.openclaw/autopatch/에서 모든 패치를 문서화하는 스크립트와 README를 유지합니다. 업데이트가 출시되면 모두 실행합니다. 우아하지는 않지만 신뢰할 수 있습니다.

  4. 모든 것을 로깅하세요. 폴러는 수행하는 모든 touch를 로깅합니다. 게이트웨이는 모든 권한 오류를 로깅합니다. 이 없이는 여전히 “내 메시지가 왜 통과하지 않았을까?”를 디버깅하고 있을 것입니다.