imsg-rpc

$npx mdskill add joelhooks/joelclaw/imsg-rpc

Manages the imsg-rpc Unix socket daemon to enable iMessage access via JSON-RPC for the gateway.

  • Helps set up and debug the launchd service bridging the gateway to iMessage through a Unix socket.
  • Integrates with the imsg source repository, SQLite chat database, and launchd for service management.
  • Uses status checks on PID, exit codes, and socket connections to determine operational health.
  • Delivers results via logs in /tmp/joelclaw and status outputs for monitoring and troubleshooting.

SKILL.md

.github/skills/imsg-rpcView on GitHub ↗
---
name: imsg-rpc
displayName: imsg-rpc Socket Daemon
description: Set up, maintain, and debug the imsg-rpc Unix socket daemon that gives the gateway iMessage access via JSON-RPC. Covers FDA setup, code signing, launchd service, and the imsg source repo.
version: 1.1.0
author: joelclaw
tags: [imessage, gateway, imsg, launchd, fda, rpc]
---

# imsg-rpc Skill

Manages the `com.joel.imsg-rpc` launchd service that bridges the gateway daemon to iMessage via a Unix socket.

## Architecture (ADR-0121)

```
gateway daemon (bun, no FDA)
    ↕ JSON-RPC over /tmp/imsg.sock
imsg-rpc (com.joel.imsg-rpc launchd agent, has FDA)
    ↕ SQLite reads
~/Library/Messages/chat.db
```

- **Source**: `~/Code/steipete/imsg` (we own it — modify freely)
- **Build binary**: `~/Code/steipete/imsg/bin/imsg`
- **Launch binary**: `/Applications/imsg-rpc.app/Contents/MacOS/imsg`
- **Socket**: `/tmp/imsg.sock`
- **launchd plist**: `~/Library/LaunchAgents/com.joel.imsg-rpc.plist`
- **Logs**: `/tmp/joelclaw/imsg-rpc.{log,err}`
- **Gateway channel**: `packages/gateway/src/channels/imessage.ts`

## Status Check

```bash
launchctl print gui/$(id -u)/com.joel.imsg-rpc | rg "state =|pid =|runs =|last exit code"
lsof -p "$(launchctl print gui/$(id -u)/com.joel.imsg-rpc | awk '/pid =/{print $3; exit}')" | rg "imsg.sock|chat.db"
lsof -nP -U | rg "imsg.sock|com.joel.gateway|/tmp/imsg.sock"  # gateway socket peer
tail -10 /tmp/joelclaw/gateway.log | rg imessage
```

Healthy state: PID present, exit code 0, gateway shows `watch.subscribe OK`.

## Restart

```bash
launchctl unload ~/Library/LaunchAgents/com.joel.imsg-rpc.plist
launchctl load ~/Library/LaunchAgents/com.joel.imsg-rpc.plist
```

## Rebuild imsg

Always use `build-local.sh` — NOT `make build` — so signing stays stable and `/Applications/imsg-rpc.app` stays in sync with source builds:

```bash
cd ~/Code/steipete/imsg && ./build-local.sh
```

`build-local.sh` now:
- builds `bin/imsg`
- signs with `imsg Local Signing`
- refreshes/signs `/Applications/imsg-rpc.app` via `scripts/install-rpc-app.sh`

## FDA Setup (new machine)

The `imsg` binary needs Full Disk Access. macOS requires a verifiable code signature to accept it.

### 1. Create local signing cert (once per machine)

```bash
cat > /tmp/imsg-ext.cnf << 'EOF'
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
[req_distinguished_name]
[v3_req]
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, codeSigning
basicConstraints = CA:FALSE
EOF

openssl req -x509 -newkey rsa:2048 \
  -keyout /tmp/imsg-key.pem -out /tmp/imsg-cert.pem \
  -days 3650 -nodes \
  -subj "/CN=imsg Local Signing/O=Joel Hooks" \
  -config /tmp/imsg-ext.cnf -extensions v3_req

openssl pkcs12 -export \
  -out /tmp/imsg-sign.p12 \
  -inkey /tmp/imsg-key.pem -in /tmp/imsg-cert.pem \
  -passout pass:imsg123 -name "imsg Local Signing" \
  -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1

security import /tmp/imsg-sign.p12 \
  -k ~/Library/Keychains/login.keychain-db \
  -P imsg123 -T /usr/bin/codesign

security add-trusted-cert -d -r trustRoot \
  -k ~/Library/Keychains/login.keychain-db /tmp/imsg-cert.pem

security find-identity -v -p codesigning  # should show "imsg Local Signing"
```

### 2. Build and sign

```bash
cd ~/Code/steipete/imsg && ./build-local.sh
```

### 3. Grant FDA in System Settings

```bash
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
```

- Click **+**
- Press **⌘⇧G**, paste `/Applications/imsg-rpc.app`, Enter
- Toggle **ON**

### 4. Load service

```bash
launchctl load ~/Library/LaunchAgents/com.joel.imsg-rpc.plist
```

Verify: `tail -f /tmp/joelclaw/gateway.log | grep imessage` — should show `watch.subscribe OK`.

## Troubleshooting

### `permissionDenied` in imsg-rpc.log

FDA is missing or csreq mismatch. Check:

```bash
sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db \
  "SELECT client, auth_value FROM access WHERE client LIKE '%imsg%';"
# auth_value 2 = allowed, 0 = denied

/usr/bin/log show --last 10m --style compact \
  --predicate 'process == "tccd" && (eventMessage CONTAINS "com.steipete.imsg" || eventMessage CONTAINS "kTCCServiceSystemPolicyAllFiles")' \
  | tail -80
```

If denied (0): go to System Settings → Full Disk Access → toggle imsg ON.
If missing: redo FDA setup step 3.
If `tccd` shows `AUTHREQ_RESULT ... authValue=2` for `/Applications/imsg-rpc.app/Contents/MacOS/imsg`, FDA is granted.

### Socket exists but gateway can't subscribe

imsg-rpc crashed after accepting. Check `/tmp/joelclaw/imsg-rpc.err`. Restart service.

### `connect ENOENT /tmp/imsg.sock` but launchd says running

The daemon process can stay alive while the Unix socket path is unlinked. Gateway cannot connect until the socket path is recreated.

```bash
launchctl kickstart -k gui/$(id -u)/com.joel.imsg-rpc
ls -l /tmp/imsg.sock
```

Gateway now attempts this heal automatically on repeated ENOENT, but manual kickstart is the fastest recovery during incidents.

### FDA toggle didn't help after rebuild

You likely rebuilt without refreshing the app bundle. Re-run:

```bash
cd ~/Code/steipete/imsg && ./build-local.sh
```

### imsg-rpc keeps restarting (exit code 1)

Likely FDA denial. Run from terminal to test:

```bash
/Applications/imsg-rpc.app/Contents/MacOS/imsg chats --limit 1
```

If that works but launchd still fails → FDA entry is for wrong path or wrong signature.

## JSON-RPC Protocol Reference

The gateway uses these methods over `/tmp/imsg.sock`:

```json
// Subscribe to incoming messages
{"jsonrpc":"2.0","method":"watch.subscribe","params":{"participants":["handle"]},"id":1}

// Send a message
{"jsonrpc":"2.0","method":"send","params":{"to":"handle","text":"..."},"id":2}

// Inbound notification format
{"jsonrpc":"2.0","method":"message","params":{"subscription":1,"message":{...}}}
```

## Files

| Path | Purpose |
|------|---------|
| `~/Code/steipete/imsg/` | imsg source (we own) |
| `~/Code/steipete/imsg/bin/imsg` | built binary |
| `/Applications/imsg-rpc.app` | FDA target app bundle for launchd process |
| `~/Code/steipete/imsg/build-local.sh` | build + sign + app sync |
| `~/Code/steipete/imsg/scripts/install-rpc-app.sh` | creates/signs `/Applications/imsg-rpc.app` |
| `~/Library/LaunchAgents/com.joel.imsg-rpc.plist` | launchd service (not in git) |
| `packages/gateway/src/channels/imessage.ts` | gateway socket client |
| `apps/web/content/adrs/0121-imsg-rpc-socket-daemon.md` | ADR |

More from joelhooks/joelclaw

SkillDescription
add-skillCreate new joelclaw skills with the idiomatic process — repo-canonical, symlinked, git-tracked, slogged. Triggers on 'add a skill', 'create skill', 'new skill', 'canonical skill', 'make a skill for', or any request to formalize a process or domain into a reusable skill.
adr-skillCreate and maintain Architecture Decision Records (ADRs) optimized for agentic coding workflows. Use when you need to propose, write, update, accept/reject, deprecate, or supersede an ADR; bootstrap an adr folder and index; consult existing ADRs before implementing changes; or enforce ADR conventions. This skill uses Socratic questioning to capture intent before drafting, and validates output against an agent-readiness checklist.
agent-discovery"Optimize websites, docs, and product surfaces for agent discoverability and operator UX. Use when working on agent SEO/AEO/GEO, crawl policy, markdown or JSON projections, llms.txt, sitemap.md, AGENTS.md guidance, content negotiation, accessibility for browser agents, or any request to make a site easier for pi, OpenCode, Claude Code, ChatGPT, Perplexity, or other agent harnesses to find and use."
agent-loopStart, monitor, and cancel durable multi-agent coding loops via Inngest. Use when the user wants to run autonomous coding workloads, execute a PRD with multiple stories, kick off an AFK coding session, have agents implement features from a plan, or manage running loops. Triggers on "start a coding loop", "run this PRD", "implement these stories", "go AFK and code this", "check loop status", "cancel the loop", "joelclaw loop", or any request for autonomous multi-story code execution.
agent-mail>-
agent-workloads"Compatibility alias for the canonical `workflow-rig` front door. Use when older prompts mention `agent-workloads` or when you need the legacy workload-planning guidance; for new work, load `workflow-rig` first."
clawmail>-
cli-design"Design and build agent-first CLIs with HATEOAS JSON responses, context-protecting output, and self-documenting command trees. Use when creating new CLI tools, adding commands to existing CLIs (joelclaw, slog), or reviewing CLI design for agent-friendliness. Triggers on 'build a CLI', 'add a command', 'CLI design', 'agent-friendly output', or any task involving command-line tool creation."
codex-prompting"Use this skill for any request to trigger, coordinate, or craft prompts for Codex. Use when user says 'send to codex', 'use codex', 'prompt codex', 'ask codex', 'delegate to codex', 'run in codex', or asks for a Codex-first execution handoff."
content-publish"Publish content to joelclaw.com via the Convex-first pipeline. Covers the full lifecycle: draft → review → publish → revalidate → verify. Handles secret leasing, tag conventions, content types (article, tutorial, note, essay), and verification gates. Use when: 'write article about X', 'publish article <slug>', 'draft a tutorial', 'publish this', 'push to convex', or any content publishing task."