# pimsg

Python library and HTTP server for interacting with macOS iMessage and SMS via [imsg](https://github.com/steipete/imsg).

## Overview

`pimsg` wraps the `imsg rpc` JSON-RPC 2.0 subprocess interface to provide:

- **Python library** — typed, Pythonic access to chats, messages, sending, and live watching
- **HTTP server** — FastAPI-based REST + Server-Sent Events (SSE) API for use by web frontends or other services

`imsg` must be installed separately on the same macOS machine.

## Architecture

pimsg is one layer in a three-tier stack:

```
[imsg]  ──JSON-RPC 2.0──▶  [pimsg]  ──HTTP/SSE──▶  [web interface]
Swift CLI                   this repo               separate project
macOS only                  Python lib +            Docker container
                            FastAPI server          behind reverse proxy
```

- **imsg** — Swift binary, macOS only. Reads `~/Library/Messages/chat.db` and sends via AppleScript. Speaks JSON-RPC 2.0 over stdio.
- **pimsg** — this package. Spawns `imsg rpc` as a subprocess, wraps it as a Python library and HTTP server. Also runs on the Mac.
- **web interface** — a separate project (to be created) that consumes the pimsg HTTP API. Can run in Docker on the same Mac or elsewhere on the network.

### Connecting Docker to pimsg

The web interface container talks to pimsg on the Mac via `host.docker.internal`:

```
Docker container
  └── http://host.docker.internal:8765  →  pimsg (Mac, bound to 0.0.0.0)
```

For a remote Docker host (different machine), use a Tailscale mesh or SSH tunnel — pimsg itself doesn't need to change. See [INSTALL.md](INSTALL.md) for details.

### This is local-first software

pimsg **only runs on macOS** — it cannot be deployed to a cloud server or Linux machine. The Mac running pimsg must have `imsg` installed, Messages.app signed in, and Full Disk Access granted. The web interface is the only part that's network-portable.

## Requirements

- macOS 14+ with Messages.app signed in
- `imsg` installed (`brew install steipete/tap/imsg` or built from source)
- Full Disk Access granted to the terminal running pimsg
- Python 3.10+

## Install

```bash
pip install pimsg
```

Or from source:

```bash
git clone https://github.com/dynacylabs/pimsg.git
cd pimsg
pip install -e ".[dev]"
```

## Library Usage

```python
import pimsg

# List chats
chats = pimsg.list_chats()
for chat in chats:
    print(chat.name, chat.service)

# Get message history for a chat
messages = chat.messages(limit=50)
for message in messages:
    print(message.sender, message.text)

# Send a message
pimsg.send(to="+15551234567", text="Hello from Python!")

# Watch for new messages (async generator)
async for message in pimsg.watch(chat_id=1):
    print(message.sender, message.text)
```

## HTTP Server Usage

```bash
# Start on default 127.0.0.1:8765
pimsg-server

# Bind to all interfaces (e.g. for Docker — ensure this connection is secured)
pimsg-server --host 0.0.0.0 --port 8765
```

### Endpoints

| Method | Path | Description |
|---|---|---|
| `GET` | `/chats` | List recent chats |
| `GET` | `/chats/{id}/messages` | Message history for a chat |
| `POST` | `/messages/send` | Send a message |
| `GET` | `/chats/{id}/stream` | SSE stream of new messages |
| `GET` | `/health` | Health check |

## Security

The HTTP server is **unauthenticated by default** and binds to `127.0.0.1` only. This is intentional — auth is delegated to the user's infrastructure:

- **Local Python use**: no network involved, no risk.
- **Same-Mac Docker**: bind to `0.0.0.0`, ensure the port is firewalled from external access.
- **Remote Docker / web interface**: place pimsg behind a reverse proxy or VPN. The web interface project is responsible for end-user authentication.

See [INSTALL.md](INSTALL.md) for recommended network setups.

## Development

```bash
pip install -e ".[dev]"
pytest
```

See [DEVELOPMENT.md](DEVELOPMENT.md) for the full development guide.

## License

MIT — see [LICENSE](LICENSE).
