Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions docs-web/architecture/chat-connectors/imessage.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# iMessage Connector Profile
# iMessage Connector Architecture

iMessage is registered with `managed_bridge` and `native_bridge` transports. Its module owns the unchanged setup schemas, iMessage bridge normalizer, bearer authentication metadata, managed HTTP mapping, native command mapping, configuration verification, and session requirement metadata.
The iMessage profile is a transparent third-party bridge contract. It advertises only `managed_bridge` and `native_bridge`; it does not expose `official_api`, imply Apple endorsement, or verify an Apple provider endpoint.

The baseline profile has no live test and does not implement `official_api`. Command execution remains in the shared outbound adapter.
Apple documents the [Messages framework](https://developer.apple.com/documentation/messages) for app extensions, [iMessage apps and Messages for Business experiences](https://developer.apple.com/imessage/), and [Message UI](https://developer.apple.com/documentation/messageui) for composing messages inside apps. Those public surfaces do not define a general-purpose personal-iMessage server bot API or public personal-account sandbox. Consequently, Code UX models bridge-owned identifiers without treating third-party payloads as undocumented Apple objects.

## Profile boundary

`src/domain/chat-connectors/providers/imessage.ts` owns:

- the unchanged persisted setup schemas for existing managed and native records;
- shared bearer authentication metadata for both inbound modes;
- opaque message/chat GUID normalization and reply/thread identity mapping;
- the version `1.0` send and health envelope;
- managed HTTP and native command request mapping; and
- the explicit `liveTest.available = false` provider-native verification declaration.

The protocol envelope always contains `protocolVersion`, `operation`, `correlation`, `message`, `chat`, `sender`, `reply`, `result`, and `error`. Send requests fill message/chat/reply data and leave result/error null. Health requests leave identity fields null. Send parsing requires `operation: send`, `result.status: sent`, every stable field, and the request's correlation id, so a health response or malformed envelope cannot complete outbound delivery. Failures return stable `error.code`, `error.message`, and `error.retryable` values. Existing native records receive transitional top-level send aliases, and recognized legacy message-id send responses remain readable; health negotiation is strict.

Inbound HTTP routes still pass through `ChatProviderIngressSecurity`. Both modes require a fresh timestamp and use the shared constant-time bearer check plus nonce replay cache. The native setup schema keeps `bridgeToken` optional for outbound-only commands, but a native bridge posting inbound events must configure it because ingress fails closed without a secret.

Outbound credential lookup preserves stored-record compatibility. Managed delivery checks `bridgeApiKey`, `bridgeToken`, `botToken`, then `webhookSecret`; native delivery checks `bridgeToken`, `botToken`, then `webhookSecret`. Inbound verification remains restricted to the declared credential for its mode.

## Native execution boundary

`src/services/chat-providers/imessage-native-bridge.ts` is the process and bridge-health boundary. The default outbound adapter routes iMessage native sends through it.

The service:

- parses legacy command records into an executable plus argv without shell evaluation;
- preserves quoted arguments, spaces, macOS paths, and Windows drive-path separators;
- spawns with `shell: false` and writes exactly one JSON request to stdin;
- inherits only a minimal OS environment and exposes the bridge secret solely as `CODEUX_CHAT_BRIDGE_TOKEN`;
- bounds stdout, stderr, managed response bodies, and execution time;
- redacts the configured credential from errors;
- negotiates protocol/correlation/result fields before accepting a health result; and
- tracks active children so cancellation, timeout, output overflow, disposal, or runtime shutdown terminates the process group and escalates to a forced kill.

Native health diagnostics distinguish `unsupported_platform`, `missing_executable`, `permission_denied`, `protocol_version_mismatch`, `correlation_mismatch`, `timeout`, `cancelled`, `shutdown`, `output_limit_exceeded`, `malformed_response`, `nonzero_exit`, `spawn_failed`, and bridge-declared errors. Managed checks add deterministic network and HTTP diagnostics.

Managed health accepts only third-party HTTP(S) URLs and rejects Apple-owned hosts as `provider_native_verification_unavailable`. Tests use Node subprocess fixtures and mocked Fetch responses on every platform. No test calls an Apple network endpoint, uses AppleScript, reads the local Messages database, or signs into a real Messages account.
72 changes: 68 additions & 4 deletions docs/settings/chat-connectors/imessage.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,73 @@
# iMessage Chat Connector

The baseline iMessage profile supports `managed_bridge` and `native_bridge`. Native delivery keeps the local command contract, writing the outbound JSON payload to stdin and optionally exposing a bridge token to the child process environment.
Code UX supports iMessage only through operator-selected third-party bridge contracts. It does not connect directly to an Apple messaging endpoint, claim Apple endorsement, or read the local Messages database.

Setup remains compatible with stored connections: managed setup uses optional `workspaceId` and `deviceLabel` with `bridgeApiKey`; native setup uses `command`, optional `workingDirectory`, and optional `bridgeToken`.
## Supported modes

The profile requires a reachable bridge session. Live provider testing and direct `official_api` transport are not implemented.
| Mode | Contract | Configuration |
| --- | --- | --- |
| `managed_bridge` | A third-party managed HTTP bridge operated or selected by the user | Existing managed records retain `workspaceId`, `deviceLabel`, and the required `bridgeApiKey`; the bridge URL continues to resolve from the stored bridge URL fields. |
| `native_bridge` | A local third-party command controlled by the operator | Existing native records retain `command`, optional `workingDirectory`, and optional outbound `bridgeToken`. A `bridgeToken` is required if the command also submits inbound requests to Code UX. |

Official reference: [Apple Messages](https://developer.apple.com/documentation/messages).
These are the only supported modes. `official_api` is not an iMessage mode, and provider-native endpoint verification is explicitly unavailable.

Apple's public material describes [Messages framework app extensions](https://developer.apple.com/documentation/messages), [iMessage apps, stickers, and Messages for Business](https://developer.apple.com/imessage/), and [in-app message composition UI](https://developer.apple.com/documentation/messageui). Those references do not publish a general-purpose server bot API for personal iMessage accounts or a public personal-iMessage sandbox. The bridge contract is therefore a Code UX contract for third-party/local software, not an Apple API contract.

## Bridge protocol v1

Native commands receive one UTF-8 JSON object on stdin and return one UTF-8 JSON object on stdout. Managed health and send operations use the same envelope as the HTTP request and response body. Protocol version `1.0` defines these stable top-level fields:

```json
{
"protocolVersion": "1.0",
"operation": "send",
"correlation": { "id": "request-correlation-id" },
"message": {
"guid": "local-message-id",
"text": "Reply text",
"timestamp": null
},
"chat": { "guid": "opaque-chat-guid", "name": "Display name" },
"sender": { "id": null, "name": null },
"reply": {
"messageGuid": "opaque-message-guid-being-replied-to",
"threadId": "code-ux-thread-id"
},
"result": null,
"error": null
}
```

Health checks use `"operation": "health_check"`; `message`, `chat`, `sender`, and `reply` are `null`. A successful response echoes the protocol, operation, and correlation fields and supplies:

```json
{
"result": {
"status": "healthy",
"messageGuid": null,
"chatGuid": null,
"metadata": {}
},
"error": null
}
```

A send result uses `"operation": "send"`, `"status": "sent"`, the request correlation id, and the bridge's opaque `messageGuid` and `chatGuid`. Managed and native send delivery reject health-check responses, mismatched correlations, missing protocol fields, and malformed result/error values. An error response sets `result` to `null` and returns stable `code`, `message`, and `retryable` fields under `error`. GUIDs are treated as opaque Unicode identifiers: Code UX trims them, normalizes Unicode composition, rejects control characters and unreasonable lengths, and never infers undocumented Apple payload structure.

For existing native command records, v1 send requests also contain the former top-level send aliases during migration. A legacy response containing `externalMessageId` remains accepted for sends. Health checks always require protocol `1.0` and matching correlation identity.

## Process and secret boundary

The native command string is parsed into an executable and argument array, then spawned with shell interpretation disabled. Quoted paths, spaces, macOS application paths, and Windows drive paths remain arguments rather than executable shell text. The configured executable and working directory remain under explicit operator control.

The child receives a minimal operating-system environment. The bridge credential is exposed only as `CODEUX_CHAT_BRIDGE_TOKEN`; it is never added to argv or stdin. Stdout and stderr have byte limits, error diagnostics redact the configured credential, and every execution has a timeout. Cancellation, runtime shutdown, timeout, or output overflow terminates the complete spawned process group, with a forced-kill fallback.

New records should use `bridgeApiKey` for managed delivery and `bridgeToken` for native delivery. Existing stored outbound records remain compatible with the prior managed fallback order (`bridgeToken`, `botToken`, then `webhookSecret`) and native fallback order (`botToken`, then `webhookSecret`). These outbound fallbacks do not weaken inbound authentication, which still resolves only the mode's declared bearer credential.

Inbound callbacks in both modes use the shared timestamped bearer verifier and replay-nonce cache. Senders should provide `Authorization: Bearer <credential>` (or `X-Code-UX-Bridge-Token`), `X-Code-UX-Timestamp`, and a unique `X-Code-UX-Nonce` or `X-Request-Id`.

## Health verification

Bridge verification is deterministic and reports machine-readable diagnostics for unsupported platforms, missing executables, permission failures, invalid configuration, protocol-version or correlation mismatches, timeouts, cancellation, shutdown, oversized output, malformed JSON, bridge errors, nonzero exits, network errors, and HTTP errors.

Managed verification accepts only HTTP(S) third-party bridge URLs. Apple-owned hostnames are rejected as `provider_native_verification_unavailable`. Native verification runs only the configured command. The automated test suite uses Node-powered local fixtures and mocked HTTP responses; it does not contact Apple, invoke AppleScript, inspect the Messages database, or use a Messages account.
Loading
Loading