8.8 KiB
Messenger Integration
How life-manager connects to the messenger platform. Four integration points, each with a distinct data flow and auth mechanism.
Overview
┌──────────────────────┐ ┌──────────────────────┐
│ LIFE-MANAGER :3700 │ │ MESSENGER :3100 │
│ │ │ │
│ ┌─────────────────┐ │ ── proxy ──────────▶ │ Browse API │
│ │ Messages Proxy │ │ ── enqueue ────────▶ │ Send Queue │
│ │ AI Loop │ │ ── poll ───────────▶ │ Service API │
│ │ Health Checker │ │ ── health ─────────▶ │ Health Controller │
│ └─────────────────┘ │ │ │
│ │ ◀── credentials ──── │ Credential Service │
│ Credential Keyring │ │ │
└──────────────────────┘ └──────────────────────┘
1. Messenger Proxy Module
Life-manager's /api/messages/* endpoints proxy to messenger's browse and queue APIs. The web frontend talks to life-manager only — never directly to messenger.
Life-Manager UI → /api/messages/conversations → Messenger /api/browse/conversations
/api/messages/folders /api/browse/folders
/api/messages/search /api/browse/search
/api/messages/stats /api/browse/stats
/api/messages/self /api/messages/self
/api/messages/queue-stats /api/send-queue/stats
/api/messages/queue-history /api/send-queue/history
/api/messages/attachment (streams file directly)
Auth: X-Service-Key header, value from MESSENGER_API_KEY env var.
Config: MESSENGER_API_URL (default http://localhost:3100).
Life-manager code: messages-proxy.service.ts, messages.controller.ts
2. Messaging AI Loop
Polls messenger every minute for self-addressed messages (sent from the user's own phone to themselves). Each new message routes through the agent system, gets an LLM response, and replies via the send queue.
life-manager messenger
┌──────────────┐ GET /messages/self ┌──────────────┐
│messaging-loop│─────────────────────▶│ service-api │
│ (cron 1m) │ │ controller │
│ │◀─────────────────────│ │
│ new msgs │ messages since T └──────────────┘
│ │ │
│ ▼ │
│ agent-router │───▶ LLM :8210
│ │ │
│ ▼ │
│ send reply │ POST /send-queue/enqueue ┌──────────────┐
│ │───────────────────────────▶│ send-queue │
└──────────────┘ └──────┬───────┘
│
macOS agent polls
▼
sends via Messages.app
Guard: Only runs when user_awake setting is true.
Auth: X-Service-Key header on all requests to messenger.
Timestamp tracking: messaging.lastProcessed setting stores the high-water mark.
Life-manager code: messaging-loop.service.ts, messaging-conversation.service.ts
3. Credential Keyring
Messenger fetches IMAP/SMTP credentials from life-manager's keyring at startup. Falls back to env vars if life-manager is unavailable.
messenger (onModuleInit) life-manager
┌──────────────┐ GET /api/credentials ┌──────────────┐ SELECT ┌─────────┐
│ credential │──────────────────────▶│ keyring │──────────▶│ Postgres│
│ service │ │ controller │ decrypt │ :25471 │
│ │◀──────────────────────│ │◀──────────│ │
│ imap creds │ {user, pass, host} └──────────────┘ └─────────┘
└──────┬───────┘
│
▼
connect IMAP/SMTP (Proton Bridge :1143, iCloud :993)
Direction: Messenger → life-manager (reverse of the other integrations).
Encryption: pgcrypto at rest in keyring_credentials table.
Credentials stored: icloud-imap, protonmail-imap (each with host, port, user, pass, tls, poll interval).
Messenger code: credential.service.ts
4. Service Health Check
Life-manager's health module pings messenger to report combined system status.
life-manager messenger
┌──────────────┐ GET /api/health/status ┌──────────────┐
│ messenger │─────────────────────────▶│ health │
│ checker │ │ controller │
│ │◀─────────────────────────│ │
│ {healthy, │ {db_ok, device_info, └──────────────┘
│ latency} │ last_seen}
└──────────────┘
Degraded status: If last device sync was >5 minutes ago, reports degraded rather than healthy.
Config: MESSENGER_API_URL, optional MESSENGER_API_KEY.
Life-manager code: messenger.checker.ts
Message Send — Two Entry Points
Both life-manager (AI loop) and Claude Code (MCP) can send messages. They converge on the same queue:
┌──────────┐ ┌──────────┐ INSERT INTO ┌──────────┐ GET /pending ┌──────────┐
│ Claude │ send_message │ MCP │ pending_msgs │ Postgres │◀──────────────│ Swift │
│ Code │──────────────▶│ Server │────────────────▶│ messenger│ │ Agent │
└──────────┘ └──────────┘ (direct SQL) └──────────┘ └────┬─────┘
│
┌──────────┐ ┌──────────┐ POST /result ┌──────────┐ AppleScript │
│ Life │ POST enqueue │ Sync │◀──────────────│ Swift │◀───────────────┘
│ Manager │──────────────▶│ API │ │ Agent │ send via
└──────────┘ (HTTP) │ :3100 │ └──────────┘ Messages.app
└──────────┘
- MCP Server: Writes directly to
pending_messagestable via SQL - Life-Manager: Calls
POST /api/send-queue/enqueuevia HTTP with service key auth
Both paths result in the macOS agent picking up the message on its next poll.
Environment Variables
| Variable | Used By | Purpose |
|---|---|---|
MESSENGER_API_URL |
life-manager | Base URL for messenger API (default: http://localhost:3100) |
MESSENGER_API_KEY |
life-manager | Service key sent as X-Service-Key header |
LIFE_MANAGER_API_URL |
messenger | Base URL for credential fetches (default: http://localhost:3700/api) |
SERVICE_API_KEY |
messenger | Validates incoming X-Service-Key headers |