Skip to content

heyfinal/imsg-bridge

Repository files navigation

imsg-bridge

A lightweight REST + WebSocket bridge for iMessage on macOS.
Send and receive messages programmatically. No Electron. No Firebase. Just a thin API.

Quick StartAPIWebSocketDeploymentHow It Works


Why

Existing iMessage automation tools are either bloated (BlueBubbles, 200MB+ Electron app) or painfully slow (AppleScript with 120s+ send times). imsg-bridge wraps the imsg CLI — which talks directly to Apple's private iMessage frameworks — and exposes a clean async API that sends messages in under a second.

BlueBubbles AppleScript imsg-bridge
Send latency ~2s ~120s <1s
Memory ~200MB ~50MB ~30MB
Dependencies Electron, Firebase osascript FastAPI, uvicorn
Real-time receive Polling None WebSocket stream

Prerequisites

  • macOS (Sonoma 15+ recommended)
  • Python 3.12+
  • imsg CLI installed at /opt/homebrew/bin/imsg
  • iMessage signed in and working in Messages.app
  • Full Disk Access granted to your terminal app and Python (for chat.db reads)

Install

Preferred: AI-assisted install (Claude Code / Codex / Gemini)

Use a one-liner like:

claude -p "Install, configure, test, and optionally deploy imsg-bridge from https://github.com/heyfinal/imsg-bridge.git on this macOS machine."

Or:

codex exec "Install, configure, test, and optionally deploy imsg-bridge from https://github.com/heyfinal/imsg-bridge.git on this macOS machine."
gemini -p "Install, configure, test, and optionally deploy imsg-bridge from https://github.com/heyfinal/imsg-bridge.git on this macOS machine."

If you already have the repo locally, generate a tool-specific one-liner and copy it to clipboard:

./setup.sh --ai-prompt codex
# or: ./setup.sh --ai-prompt claude
# or: ./setup.sh --ai-prompt gemini

If you are already inside an interactive AI terminal, generate a plain copy/paste task prompt:

./setup.sh --ai-task

Paste this inside Claude/Codex/Gemini interactive mode:

Install, configure, test, and optionally deploy imsg-bridge from https://github.com/heyfinal/imsg-bridge.git on this macOS machine.

Manual one-liner (no package manager needed)

curl -fsSL https://raw.githubusercontent.com/heyfinal/imsg-bridge/main/install.sh | bash

This clones the repo to ~/.imsg-bridge, creates a virtual environment, and installs dependencies using only Python's built-in venv and pip. No Homebrew, no uv, no global installs.

Manual: with uv

git clone https://github.com/heyfinal/imsg-bridge.git
cd imsg-bridge
uv sync

Manual: with pip

git clone https://github.com/heyfinal/imsg-bridge.git
cd imsg-bridge
python3 -m venv .venv
.venv/bin/pip install -e .

Quick Start

cd imsg-bridge  # or ~/.imsg-bridge if you used the one-liner

# Generate auth token and install as a background service
./setup.sh

# Or run manually
imsg-bridge
# → Listening on configured host:5100

API

All endpoints require a bearer token in the Authorization header:

Authorization: Bearer <your-token>

The token is generated by setup.sh and stored in your macOS Keychain.

Send a message

curl -X POST http://127.0.0.1:5100/send \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "+15551234567", "text": "Hello from the bridge"}'

List chats

curl http://127.0.0.1:5100/chats \
  -H "Authorization: Bearer $TOKEN"

Get message history

curl "http://127.0.0.1:5100/history/23?limit=50" \
  -H "Authorization: Bearer $TOKEN"

Health check

curl http://127.0.0.1:5100/health \
  -H "Authorization: Bearer $TOKEN"

Returns {"status": "ok", "imsg_version": "<detected-version>"} when everything is working.

Ping (unauthenticated)

curl http://127.0.0.1:5100/ping

Returns {"status": "ok"} — useful for monitoring, load balancers, and launchd health checks.

Resolve contact name

curl "http://127.0.0.1:5100/contact-name?identifier=%2B15551234567" \
  -H "Authorization: Bearer $TOKEN"

Returns {"identifier": "+15551234567", "name": "John Doe"} from the macOS Contacts database.

Get contact avatar

curl "http://127.0.0.1:5100/avatar?identifier=%2B15551234567" \
  -H "Authorization: Bearer $TOKEN" --output avatar.jpg

Returns the contact's photo as image/jpeg, image/png, or image/tiff.

WebSocket

Connect to /ws for a real-time stream of incoming messages:

websocat "ws://127.0.0.1:5100/ws?token=$TOKEN"

Each message arrives as a JSON object:

{
  "id": 43327,
  "guid": "A1B2C3D4-...",
  "chat_id": 23,
  "text": "Hey, what's up?",
  "sender": "+15551234567",
  "is_from_me": false,
  "created_at": "2026-03-03T06:00:00.000Z",
  "attachments": [],
  "reactions": []
}

Authentication via Authorization: Bearer <token> header or ?token=<token> query parameter.

Deployment

As a launchd service (recommended)

The setup.sh script handles everything:

./setup.sh

This will:

  1. Generate a secure auth token and store it in macOS Keychain
  2. Let you choose bind mode (0.0.0.0 for LAN clients or 127.0.0.1 for local-only)
  3. Install a LaunchAgent that starts on login and auto-restarts on crash
  4. Optionally deploy Linux client via:
    • LAN scan for SSH hosts (or manual IP entry) + username/password prompt
    • USB/external drive scan (or manual mount path)
  5. If Messages DB access is blocked, automatically open Full Disk Access settings and walk you through enabling it
  6. Verify the service is running

Logs are written to ~/Library/Logs/imessage-bridge.log and ~/Library/Logs/imessage-bridge.err.

LAN access (optional)

By default, the LaunchAgent binds to 127.0.0.1 (loopback only). If you want to access the bridge from other machines on your LAN (e.g. imsg-gtk on Linux), bind to 0.0.0.0 instead:

IMSG_BRIDGE_BIND_HOST=0.0.0.0 ./setup.sh

This exposes the bridge to your local network. Keep the bearer token private and consider using a firewall, Tailscale, or SSH tunneling.

Manual

imsg-bridge --host 127.0.0.1 --port 5100

Or with uvicorn directly:

uvicorn imsg_bridge.bridge:app --host 127.0.0.1 --port 5100

How It Works

Your app / bot / agent
    ↕ HTTP + WebSocket
imsg-bridge (FastAPI, port 5100)
    ↕ async subprocess
imsg CLI (/opt/homebrew/bin/imsg)
    ↕ private frameworks
~/Library/Messages/chat.db
  • REST endpoints spawn imsg subprocesses for each request (send, chats, history)
  • WebSocket runs a persistent imsg watch subprocess that streams new messages as JSONL
  • State persistence tracks the last processed message ROWID in ~/.imessage-bridge/state.json so no messages are lost across restarts
  • Auth uses a bearer token stored in macOS Keychain — no config files with secrets

Linux Client (imsg-gtk)

A native GTK4/libadwaita desktop app for reading and sending iMessages from Linux over your LAN.

Features

  • Real-time message streaming via WebSocket
  • Contact name resolution and avatar display from macOS Contacts
  • Inline image attachments
  • Desktop notifications for incoming messages (when window is not focused)
  • Reconnection banner with automatic retry
  • Send failure indicator with visual feedback
  • Search/filter conversations
  • Right-click context menu (open, copy contact, clear)
  • Dark mode support via libadwaita

Requirements

  • Linux with GTK 4.10+, libadwaita 1.3+
  • Python 3.12+ with PyGObject
  • Network access to the Mac running imsg-bridge

Deploy

The easiest way is via setup.sh on the Mac, which offers SSH push or USB copy:

./setup.sh --deploy-ssh user@linux-machine
./setup.sh --deploy-usb /Volumes/USB

Or install manually on Linux:

cd imsg_gtk
pip install -e .
imsg-gtk

Configuration is stored in ~/.config/imsg-gtk/config.json:

{
  "host": "192.168.1.100",
  "port": 5100,
  "token": "your-bearer-token"
}

Security

  • Setup supports both bind modes:
    • 127.0.0.1 local-only (recommended default)
    • 0.0.0.0 LAN-accessible (required for direct Linux client access)
  • Bearer token required on all endpoints (REST and WebSocket) except /ping
  • Token stored in macOS Keychain, never on disk
  • Uses constant-time token comparison for auth checks
  • Rate limiting on /send (20 requests/minute sliding window)
  • imsg binary path configurable via IMSG_PATH environment variable
  • For remote access, use Tailscale or an SSH tunnel

Development

# Install dev tooling
python3 -m pip install -e ".[dev]"

# Optional: run checks automatically on commit
pre-commit install

# Lint + tests
ruff check imsg_bridge imsg_gtk tests
pytest -q

CI runs these checks on every push and pull request.

License

MIT

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages