Transports, workers, and capability
Backends declare capability truthfully and Most routes only to what is executable. Each chat is pinned to one physical worker; an unavailable pinned worker fails explicitly with 503 rather than silently moving the conversation to a different identity.
Current capability#
The README's capability table is authoritative — planned and unavailable transports must not advertise executable send capability:
most-bootstrap and --dry-run inspect configuration only: they never contact Apple or the database, load anisette, or persist identity material. Exit 0 is not activation or credential verification. --i-understand-im-talking-to-apple refuses with exit 3 before login because FairPlay activation signing is unavailable; --dry-run takes precedence. --no-persist omits the plan's database-setting requirement. The obsolete --two-factor-code option and placeholder-persistence paths were removed.
- HTTP control plane, auth, storage, webhooks — Linux container or native host — Implemented.
- mac-relay text and attachment send — native macOS with Messages.app — Implemented.
- mac-relay receive, reactions, edits, receipts, attachments — native macOS via `chat.db` — Implemented.
- Twilio SMS — Linux or macOS — Implemented when configured.
- Twilio Voice phone calls (place, answer, reject, speak, hang up) — Linux or macOS — code present; not yet qualified by a real call through a configured voice number.
- Remote-worker control plane — Linux or macOS — Implemented.
- Private-API dylib operations — dedicated SIP-disabled Mac — code present; operator injection prerequisite. `MOST_DYLIB_ENABLED` reads `true` or `false` (unset is off); any other value refuses to start the mac-relay pool naming the variable and the value, so a mistyped flag never runs silently off. With `MOST_DYLIB_ENABLED=true` the host declares the loopback port the server and the helper meet on as `MOST_DYLIB_PORT`, and the helper is started with the same value as `WISENT_DIAL_PORT`; no port is assumed, and the server refuses to start the mac-relay pool naming `MOST_DYLIB_PORT` when it is missing.
- Direct-to-Apple outbound send — unavailable; outbound methods return `NotImplemented`, and live bootstrap refuses before Apple login. With `MOST_DIRECT_APPLE_ENABLED` the host declares `ANISETTE_ROTATE_INTERVAL_SECONDS`, the whole seconds between Anisette rotations. This same declaration sets the local freshness lifetime of newly minted validation material; there is no separate five-minute expiry. Anisette headers do not supply a validity lifetime, so this is a host policy, not a guarantee of Apple's acceptance. Most assumes no interval and refuses the backend naming the variable when it is missing, zero or not a whole number.
- Twilio RCS — outbound text code is present, but provider delivery is not qualified. The explicit per-conversation choice uses an RCS Messaging Service sender and disables automatic SMS/MMS fallback.
- Jailbroken-iPhone worker — device code present; operated rollout and automatic recovery are unavailable. Most ships no rack recovery daemon: palera1n cannot select a UDID, so a shared USB rack cannot safely re-exploit one chosen phone.
- Telegram through the operator's own bot, over the Telegram Bot API — send code present, not registered: most-backend-telegram holds the Bot API client and plain-text send to one chat id, and keeps Telegram's own code and words for every refusal without the token or the URL carrying it; most-server does not register it yet, so no message is sent or received. Receiving with getUpdates is not built.
- WhatsApp through the operator's own business number, over Meta's WhatsApp Cloud API — send code present, not registered: most-backend-whatsapp holds the Cloud API client and plain-text send to one E.164 number, keeps Meta's own code and words for every refusal and puts the access token in no error; most-server does not register it yet, so no message is sent or received. Meta delivers free-form text only inside its 24-hour customer service window; receiving through a signed webhook at a public HTTPS address is not built.
Twilio SMS#
Set STADO_INTEGRATION_API_URL to the deployed wisent-integrations HTTPS origin, without credentials or a path, and MOST_STADO_INTEGRATION_TOKEN to the product-specific bearer authorized for most/send-sms. STADO_API_URL is not this endpoint. Twilio account, auth and sender fields stay in the provider-side most-twilio item.
A 401 in an SMS receipt can mean an undeclared action, not just a wrong token. Matching local and host token fingerprints do not authorize most/send-sms. INTEGRATION_CLIENTS must allow that action for the intended token item, and the consumer named by INTEGRATION_SKARBIEC_CONSUMER must have read access to that item's token.
Use stado service env-show wisent-integrations --host HOST --env-file FILE --json to inspect the launcher's environment file; service env only reports the unit file. Inspect the named verifier with stado credentials grant show CONSUMER --host HOST --json. These reads show declarations, not proof that the running service uses that grant or can authorize Most.
An existing most-twilio item with empty account or sender fields is not a configured provider. Access and provider configuration belong to the deployment owner; do not borrow another product's token, expand grants or restart a service to hide an authorization refusal. After the approved configuration is available, qualification still requires an actual send to an approved test recipient, retained receive state and delivery receipts.
With that origin set, make test-messaging-refusal runs the actual server's SMS/RCS requests and the SMS shell example against real wisent-integrations using an intentionally invalid bearer. It checks both response formats, retained conversations, unknown-chat refusal, stored-channel continuation, channel-change refusals and nonzero SMS command failure. An RCS 404 not_found is a missing deployed action, not successful authorization or delivery. Commands, exit statuses, responses and server logs remain under .wisent-output/messaging-evidence (or WISENT_OUTPUT_DIR/messaging-evidence). These refusal tests do not qualify provider delivery.
For a new conversation, set message.preferred_service to SMS or RCS; omitting it selects iMessage. SMS and RCS each require one recipient. Most Desktop's Send screen offers the same channel choice and includes it in confirmation. Subsequent operations resolve the conversation's saved transport; a conflicting SMS/RCS preference returns HTTP 400. A missing or ambiguous registered owner returns HTTP 503 rather than selecting another channel.
Twilio Voice#
Phone calls use the same two routes to Twilio as text: the user's own account (`MOST_TWILIO_ACCOUNT_SID`, `MOST_TWILIO_AUTH_TOKEN`, and `MOST_TWILIO_FROM` as the caller id) or the wisent-integrations actions `most/place-call`, `most/update-call` and `most/verify-voice-request` over the `most-twilio` item. `PUBLIC_BASE_URL` must be this service's public HTTPS origin, and the Twilio number must call the three URLs `GET /v3/calls/readiness` reports. The whole contract is on the Phone calls page.
mac-relay#
mac-relay runs only on a real Mac: it sends through Messages.app via AppleScript and receives through one chat.db, `MOST_CHAT_DB_PATH` when the host declares it, else `~/Library/Messages/chat.db` in the home of the user `most-server` runs as; outbound probes and the account import read the same file. A process with neither HOME nor a home in the user database does not register the pool and names that cause in the startup log. It is not a container feature.
Downloaded attachments and transcoded voice memos are written only to Messages.app's sandbox tmp (`~/Library/Containers/com.apple.MobileSMS/Data/tmp`), the one directory it reads an outgoing file from. When that directory is missing, a send that downloads an attachment is refused naming it (open Messages.app once as that user), and a voice memo is sent untranscoded with the cause logged.
Before registering the local worker, Most reads `sysctl -n kern.osproductversion` once. The same observed major version controls backend capability and the stored `host_mac_ver`; the registration log includes `host_mac_major`. A failed command or unparseable answer prevents this pool from being registered and names the command and cause in the startup log. Most never substitutes an assumed macOS version. The server may remain available with its other backends; `/healthz` is not proof of messaging readiness.
The version observation must be valid UTF-8 with nonempty decimal components separated by dots. A numeric major followed by malformed components is refused as a whole rather than accepted as a partially parsed host identity.
The private-API helper owns one immutable main-actor initialization task created before the helper can be used; every request awaits that task. It observes account state rather than waiting through a warmup window. History lookup and Anisette requests await Apple's actual completion while leaving the main run loop free. The transport cancels pending actions when their connection ends; a late callback cannot resume a cancelled request or continue its message mutation. A connected caller remains pending until the framework answers, rather than guessing failure from elapsed time. A failed dial waits for network readiness or the local listener-ready Darwin notification; the Rust listener publishes that notification after binding and when a caller finds the helper disconnected. A notification failure names notifyutil and its cause. The listener and injected helper must run on the same Mac for this notification path.
`diagnose_nickname_runtime` returns every matching class unless the request supplies `max_classes`. That nonnegative integer is used without another cap; zero returns no class rows, negative or non-integer input is refused by name. `class_count` always reports the matching total and `truncated` indicates whether rows were omitted. Message operations in the CLI and Desktop share this helper transport.
The HTTP diagnostics route, CLI and MCP preserve this optional count without adding a default. Use most-cli diagnostics imessage nickname-runtime with optional --filter and --max-classes. In Most Desktop, select Operations, Nickname support, then Read nickname report: leave Maximum classes blank for all results or enter a count. The complete JSON and any service refusal are shown separately from the availability probe.
Most Desktop also exposes Operations → Contact card and Operations → Personal profile. Read current identity fetches the full native report. Review identity change requires explicit names, optional image path on the Messages Mac, and email for the contact card, then confirmation before the same loopback-only POST used by the operation scripts. The screen keeps the configuration response and separate readback, shows complete HTTP/helper refusals and partial-store failures, and does not send a message. tests/identity/NativeIdentityRefusal.swift in most-desktop exercises the real client refusal path against an isolated Mac fixture; it does not substitute for signed-app visual qualification.
Both helper transport modes require `WISENT_HELPER_MAX_LINE_BYTES` in the Messages.app environment: a positive deployment-declared bound on one NDJSON request, excluding its newline. Socket receive sizing uses this same declaration rather than a separate fixed chunk size. Missing or invalid values refuse startup by name. An exactly bounded request remains valid when its newline arrives later. A complete or incomplete request that exceeds the bound closes only that connection and logs the observed size and declared limit; response content is not truncated.
A newline-terminated helper request containing invalid UTF-8 closes its connection and cancels that connection's pending actions, with the encoding cause in the helper log. Neither transport silently drops the malformed frame while the caller waits for a reply. Other client connections remain available.
A newline-terminated line that is not a request is answered, not dropped, with a refusal that names the stage: `invalid-utf8` for text the helper cannot read as UTF-8, `invalid-json: <the parser's own error>` for text that is not JSON, and `not-an-object: <type>` for JSON that is not an object; the helper log carries the same cause. A response the helper cannot serialize is answered with `serialization-failed: <the serializer's own error>` and keeps the request's transaction id; only when that second line cannot be written either does the bare `serialization-failed` literal reach the peer, with the double failure in the helper log. tests/mac-relay/helper-runtime.mjs sends such lines to the live helper.
A present `WISENT_DIAL_PORT` selects outbound helper mode. Empty, malformed and zero values refuse with the declaration named; the helper does not switch to inbound mode using `WISENT_HELPER_PORT`. Listen mode is selected only when the dial declaration is absent.
Outbound helper startup retains a connection already opened by listener readiness. A notification arriving before the queued startup action cannot make startup overwrite the active connection and lose its request-cancellation ownership.
The legacy deploy-to-mac.sh preserves complete compiler output and Messages stderr on a failed helper probe. Those logs diagnose the attempt; they do not establish listener readiness, process ownership or a qualified deployment lifecycle.
Speech synthesis sends all normalized text to the system engine through standard input; it does not silently shorten the text or place it in a command-line argument. Empty text is refused. Sender names and profile URLs keep semantic validation (nonblank name, absolute HTTP(S) URL), without guessed character caps. Binary link-preview archives use a flat object graph: shared objects are decoded once, object-reference cycles refuse the archive, and cyclic URL references cannot become a preview.
MOST_SENDER_PROFILE_IMAGE_PATH is optional only when absent. A present empty/non-Unicode declaration, failed filesystem inspection or non-file path refuses sender identity application with its setting, path and cause; it is not silently omitted. Expanding ~/ requires a readable, nonempty HOME rather than a guessed root directory.
Sender identity fingerprints require the declared image bytes to be readable. A failed read reports the setting, image path and filesystem cause instead of hashing a pathname substitute and potentially accepting a stale cached identity.
If a branded native first effect/text send cannot apply its required identity, the upstream refusal retains the actual helper or local declaration cause. It is not reported as an unrelated missing worker, and that native message is not dispatched after the refusal.
A missing sender share-cache file means there is no prior cached identity. Other filesystem failures or malformed cache values refuse with the path and actual cause rather than silently loading an empty or partial cache.
Sender share-cache directory creation, serialization, writing and publication failures retain their operation, applicable path and underlying cause. An identity may already have been shared before persistence fails; the refusal does not claim that this helper action was rolled back.
MOST_SENDER_IDENTITY_SHARE_CACHE_PATH refuses a supplied empty or non-Unicode declaration. When absent, the cache uses its standard location below HOME; that location and explicit ~/ expansion require readable, nonempty HOME rather than a root-directory substitute. Explicit paths without ~/ do not require HOME.
MOST_SENDER_IDENTITY_EMAIL remains optional in the sender fingerprint and may be absent or empty. A non-Unicode declaration refuses by name with its read cause instead of silently becoming an empty component.
The service guards identity state reads, cache loading, configuration/sharing, publication and the actual branded native send, including attachments, across relay backend instances. Direct identity configuration and sharing diagnostics use that guard too. Every branded send configures its requested identity even if its chat fingerprint was already shared: another chat may have changed the Mac's single personal identity. The cache only decides whether another recipient share is needed. This is not cross-process locking. A missing helper refuses before dispatch; native branded-send errors retain the cause and uncertain delivery state, without AppleScript fallback or inference from unrelated chat.db rows.
Native first-contact and existing-chat send failures retain the helper cause and explicitly unconfirmed delivery. Most neither accepts unrelated global chat.db activity as confirmation nor replays the uncertain operation through AppleScript. AppleScript remains available when no native helper was selected. An unrecognized effect on a native existing-chat send refuses before dispatch instead of silently sending plain text.
A declared sender avatar is required, not best effort. The native helper reports image read/staging failures, unavailable avatar class or initializer, allocation failures and initializer errors instead of silently creating a name-only card. The relay requires avatar_configured true before caching the declared image as applied.
Nickname configuration acknowledges the controller's readback, not merely a constructed object: first and last names must match the request, a declared avatar must be present, and the sharing-state object must be readable. A mismatch reports observed state. These are local-state checks, not proof of recipient delivery or byte-identical avatar processing.
Name-only sender appearance is configured, fingerprinted, shared and cached through the same sequence without requiring a local image. A remote profile_picture_url without MOST_SENDER_PROFILE_IMAGE_PATH refuses with the required setting named rather than silently sharing an old picture.
Native sender appearance requires a nonempty caller display_name. A picture-only request cannot select an invented sender name; the native send refuses with the missing field named.
The direct configure_personal_nickname helper action requires a nonempty first_name and a string last_name, which may be empty. A supplied image_path must be a string. Missing names and wrongly typed image paths refuse before configuration rather than selecting another identity.
The loopback personal-nickname endpoint also requires both name query fields. Me-card HTTP and helper configuration require explicit given_name, family_name, nickname, organization_name and email; given name and email must be nonblank, while the other strings may be empty. Missing fields never select a brand identity. Supplied image paths must be strings, and an unreadable image refuses before either contact store is modified. tests/mac-relay/helper-runtime.mjs contains real-helper refusal journeys for these declarations and personal-nickname declarations; run them only in an isolated test macOS account because a regressed helper could incorrectly mutate its Me card.
Legacy AddressBook configuration checks the native Boolean results of property writes, image assignment, record insertion and save, carries native NSError details where available, and reads back the selected Me-card ID. Both stores receive the same loaded image bytes. Failure of the legacy operation refuses the whole action and states that the modern Contacts write already succeeded; there is no claim of an atomic rollback.
Modern Contacts lookup only treats the SDK recordDoesNotExist error as absence; other causes propagate. A failed mutable copy refuses rather than creating another contact. After save, the writer reads the exact saved identifier and checks names, organization, email list and declared image presence. Readback failures or mismatches identify the already-saved contact and observed state, not a different email match.
The dedicated-Mac qualification source tests/mac-relay/sender-identity-refusal.mjs requests real branded text and attachment first-send refusals from an isolated deployment with an invalid sender setting or disabled helper. It requires an approved existing direct chat and harmless readable fixture attachment, retains the service revision and actual cause, and compares native message/chat counts through read-only SQLite. Prerequisites and regression-send risk are documented in docs/helper-runtime.md. It must not target an operator conversation and does not qualify successful/concurrent sharing, ambiguous-send refusal or desktop behavior.
Emoji reaction glyphs retain the whole nonempty Unicode sequence. The dedicated Messages emoji column takes precedence; older schemas use the decoded reaction text without a guessed code-point ceiling. A missing glyph remains null with a metadata-only diagnostic.
- Messages.app signed in with an operator-owned Apple ID and functioning manually.
- Full Disk Access to the chat.db the host reads (`MOST_CHAT_DB_PATH`, else `~/Library/Messages/chat.db`) and Automation permission for Messages.app.
- Deliberate worker identity and capacity, plus a rollback and provider-account recovery owner.
- Start with `MOST_COMPOSITION=dev-mac` only in an operator-controlled environment; a live send is not an onboarding health check.
Remote workers#
Remote transport workers connect over an mTLS WebSocket with a dedicated bearer capability. A worker needs a stable `worker_id` and backend kind, an internal CA-issued client certificate matching that identity, protocol v2, a capacity declaration, its declared reconnect schedule, and an explicit removal and certificate-revocation procedure. Protocol v2 has no heartbeat: liveness follows the open connection. Close-frame reasons use the longest complete UTF-8 prefix within the RFC 6455 budget of 123 bytes after the two-byte close code; session diagnostics retain the full reason.
A call ends when its reply arrives; no clock of Most's ends it. Its pending reply belongs to the caller: cancellation removes the waiter, disconnect fails remaining calls, and a failed hello acknowledgement tears down the already registered backend. Neither registration nor elapsed time proves the consumer works.
A worker enters the local dispatch map only after its durable registration and live-state write both succeed. A storage refusal preserves the database cause, sends no hello acknowledgement and leaves any previously admitted backend and capacity unchanged; a rejected connection never becomes routable.
The iPhone connection owns its concurrent call tasks and drains their completions. Disconnect cancels queued work; MainActor checks cancellation before entering IMCore. Already-submitted provider work cannot be rolled back, and cancellation of only a server-side reply waiter is not a wire cancellation: protocol v2 has no cancel frame.
The iPhone worker requires reconnect_first_seconds, reconnect_factor, reconnect_max_seconds, reconnect_jitter_low and reconnect_jitter_high in config.json. Missing, nonnumeric, boolean, nonfinite and nonpositive declarations refuse by field name. A computed delay that cannot be held as a positive count of nanoseconds by Swift's task suspension is a permanent configuration error naming the schedule and observed delay. Cancelling the worker ends reconnecting instead of starting another connection. No schedule is invented.
The worker invocation is exactly wisent-worker --config <path>. Missing or empty paths, unknown options, repeated --config declarations and trailing operands refuse before reading the configuration or opening a connection. The real-binary reconnect-policy journey includes these argument refusals before validating schedule fields and the actual control-plane handshake.
The iPhone WebSocket keeps Foundation's declared request/resource policy rather than assigning undocumented zero values. It logs the observed intervals at ws.started. Send and receive failures include the failed operation, task state, NSError domain and code, cause, close code, transferred byte counts and HTTP status when available. Actual device idle lifetime and disconnect recovery still require qualification; a successful hello proves neither.
Failed sends and failed receives both preserve an observed peer close code, so whichever operation fails first retains permanent-refusal semantics. An unreadable or invalid local PKCS#12 identity also stops the worker permanently with its file-read or import cause rather than retrying unchanged credentials.
Worker affinity#
A new chat is assigned to one physical worker and that assignment is persisted; every subsequent operation resolves the same worker. When that worker is unavailable, the request fails with an explicit 503 — Most never silently fails an existing chat over to a different Apple identity.
An existing chat dispatches only when its pinned worker has both a local backend and a healthy durable row. Missing or unhealthy rows refuse as offline; a failed health read returns a storage failure instead of dispatching from local state. The HTTP response carries the classified error, while server logging receives the worker, chat and database cause under the existing diagnostic rendering policy. None of these refusals silently reassigns the chat.