Availability: The official iPhone app is available on the App Store. Local development builds can also run from source.

What it does

Settings

Open Settings in the sidebar to use the same Dashboard settings pages as the web and macOS apps. A connected operator session with operator.admin is required. The toolbar's Gateway button opens the native connection screen, including setup, paired Gateways, manual connection, and advanced connection options. The sidebar footer also provides Gateway access: Add Gateway when none are saved, a direct connection-settings button when one is saved, and a quick picker when multiple Gateways are saved. The picker keeps saved Gateways available even when they are offline and includes a management action for setup and pairing. Selection identifies the focused Gateway, not whether it is connected. Finish recording or delivering attachments and send or clear the current draft before using the quick picker. It does not move drafts to another Gateway. Approvals opens the native approval inbox and shows the pending count.

An icon beside a native sidebar session shows whether its oldest pending request is a question or an approval. It opens a compact preview and the number of additional requests of that kind. The agent and section headings summarize their sessions, including requests outside the visible recent-session list. Tap an icon, activate it with a keyboard, or use VoiceOver to read the details without switching chats. Questions stay available when navigating away from Chat. Completed, cancelled, and expired requests disappear from these previews; answer and credential drafts never appear. Gateway administration approvals open their existing Dashboard review page, while native exec and plugin approvals keep their existing actions.

The Gateway must serve Dashboard pages that support the companion iOS app. If a loaded Settings page does not report that support, a native banner asks you to update the Gateway. Open Gateway opens the native connection screen; Gateway setup and pairing remain available. The offline Device section also remains available without Dashboard support.

The This iPhone or This iPad page contains appearance, notification delivery, camera access, keep-awake behavior, and optional Health summaries. Its Permissions page shows notification, camera, microphone, speech, location, contacts, calendars, reminders, and photo access. Permission requests use native iOS prompts; Precise Location shows the system's current state and links to iOS Settings. The Dashboard Talk page contains this device's Voice Wake, Talk, Talk control, background listening, and speakerphone settings. Enabling Voice Wake, camera access, Health summaries, or Always location first asks for native confirmation.

The Gateway's Talk configuration owns provider, voice, and speech language. Saved iOS overrides no longer apply; the retired app speech-language preference is cleared at startup. The default share instruction is also retired and cleared at startup. Share messages include only an instruction explicitly supplied for that share. These changes do not remove the Gateway's talk.speechLocale option.

This iPhone and This iPad also open native Apple Watch, Diagnostics, Licenses, and About screens. When disconnected, connected without admin access, or running in demo or screenshot mode, Settings shows a native fallback: the Gateway connection controls followed by those four rows in a Device section. Connection and these device screens remain available without loading the Dashboard.

Channels (/settings/channels) and Skills (/skills) are managed in the Dashboard. Appearance, notifications, privacy, permissions, and Voice & Talk settings also use the Dashboard pages; the separate native settings screens and system-agent settings chat have been removed. Agent selection and read-only Files browsing remain native.

The sidebar opens these Gateway pages in the embedded Dashboard without web navigation chrome:

Sidebar destination Dashboard path
Activity /activity
Workboard /workboard
Skill Workshop /skills/workshop
Instances /settings/devices
Dreaming /settings/memory/dreams
Usage /usage
Automations /automations

These pages require the same connected operator.admin session as Settings. Without that access, they show the native Gateway connection guidance. Instances opens Devices, the Dashboard owner of paired nodes and connected clients.

The native New Thread agent picker shows configured names or agent IDs as soon as the roster arrives. Resolved identities update each choice without delaying selection; configured names keep precedence and the Gateway's default identity is Assistant. The catalog refreshes when the picker opens and stays bound to the selected Gateway.

Cloudflare Access sign-in

When a Gateway URL or setup code reaches a Cloudflare Access sign-in challenge, tap Connect to sign in to the Gateway website in the system browser inside Apus. Complete the Access sign-in, then tap Done. Choose Continue to authorize the app connection and finish any Access approval. The app starts its encrypted session transfer only after you choose Continue. The browser closes after the app verifies the returned session, then normal Gateway pairing continues. Existing Gateway credentials, service headers, and WARP connections continue to work without an extra prompt when the connection check already succeeds.

This browser has its own app session. Signing in with Safari does not guarantee that you are already signed in here. Website sign-in alone does not authorize the native connection. Tap Cancel to stop; use Sign in in the native Gateway settings to try again. Returning from the background never starts a browser automatically.

If Access expires or is revoked, connections pause and Gateway settings asks you to sign in again. Your Gateway pairing and device keys remain saved. Signing out of Access removes the shared grant for that host and closes connections using that Access session. The sign-out control follows the focused saved Gateway, not the manual credential editor. Access Host above the button shows the full, selectable origin for the shared session it will remove. Access status and sign-in guidance also follow the focused profile, so another profile’s sign-out message does not appear beside its valid session. Gateways using their own service headers stay connected. The browser may retain your identity-provider session; this does not sign you out of that provider. Forget Gateway removes one profile and its active work. Other profiles on the same host keep their Access session; forgetting the last profile also removes that grant. If a setup code expires while you are signing in, scan a fresh code. An invalid or used setup code is a Gateway pairing error and does not require a new Access login.

Native pairing, chat, and authenticated media use the Access session. Embedded Dashboard pages and widgets display an explanation with native Gateway settings available; browser-session support for those surfaces is separate. The share extension asks you to send from Apus for an Access-protected Gateway. Access credentials are not exported to Apple Watch or cloud workers.

Sessions

Choose All Sessions… in the sidebar to browse Active, Snoozed, or Archived sessions. Long-press an eligible session and choose Snooze: In 1 hour, In 3 hours, This evening (18:00 local, only when more than one hour away), Tomorrow (09:00 on the next calendar day), or Next week (next Monday at 09:00). On Sundays, Next week is omitted because it matches Tomorrow. Each preset shows its local wake time.

The Snoozed scope shows Wakes and the scheduled time. Long-press a snoozed row and choose Wake session to bring it back early. A session also wakes when its deadline arrives, a real inbound message arrives, or an agent run completes. Pinning or archiving clears its snooze. Protected main sessions, child/subagent sessions, and archived sessions cannot be snoozed.

Cached snoozed sessions remain browsable offline under the Snoozed scope.

Snooze only hides a session from active lists. It never stops a run, prevents messages, or disables automations. The Gateway stores the wake time, so it is shared across connected clients; an open conversation stays open.

Session colors

Long-press a session in the sidebar or Sessions screen to open its session actions, then choose Color. Select red, blue, green, yellow, purple, orange, pink, or cyan. Default clears the color.

A colored session has a narrow leading stripe in session lists and a small dot beside its title in Chat. Unset colors show neither marker. The Gateway stores color names, not hex values; the app adjusts their hues for light and dark appearances.

Reactions

Saved prompts and assistant replies show emoji reaction chips with counts and reactor names for VoiceOver. Tap a chip to toggle your reaction, or long-press a message and choose Add Reaction for the quick palette. More… accepts one typed or pasted emoji. Reactions update live while the session is open and do not create notifications. Reaction controls follow the Gateway's advertised methods, operator scopes, session cap, and current sharing role; sessions you can only view show the chips without reaction controls. Archived and catalog sessions do not offer reaction controls.

Message times and models

Completed message groups show relative time for the past week and a compact local date for older messages, alongside usage when available. VoiceOver reads the exact date, time, and time zone. Assistant replies show their recorded originating model when known; selecting another model does not change older replies. Streaming text, commentary, and tool activity do not gain these footers.

Sources in chat

Completed answers show up to eight compact Sources cards for cited pages returned by web search or web fetch during that answer's run. Tap a card to read the recorded Search snippet or Page excerpt, then choose Open source to visit the page. A card says when no recorded excerpt is available. Opening the preview does not retrieve the page again.

Source icons follow the Gateway's automatic favicon preference and use the Gateway's authenticated favicon service. A globe appears when icons are disabled or unavailable. Session links and GitHub issue or pull request links keep their existing link cards.

Diagrams in chat

Use a fenced mermaid block to display a diagram. A diagram renders when its closing fence arrives or the response finishes; an incomplete streaming fence stays readable as code. Ordinary code fences keep their usual presentation.

Tap the diagram to open a full-screen preview with pinch-to-zoom. The corner menu lets you switch between the diagram and its source, and the copy button copies the complete source. If rendering fails, the source remains available; temporary failures offer Retry diagram.

Local source builds generate the bundled renderer during pnpm ios:gen. Run pnpm install from the repository root before generating the Xcode project so the pinned renderer dependencies are available.

Requirements

Quick start (pair + connect)

On first launch the app walks through a short pairing explainer, then Gateway setup. It does not present an aggregate permissions page. Optional access is requested when you use the related feature, or from Settings -> This iPhone (This iPad on iPad) -> Permissions. A permission request presents the native iOS authorization prompt. You can change granted access later in the iOS Settings app.

  1. Start an authenticated Gateway with a route your phone can reach. Tailscale Serve is the recommended remote path:
apus gateway --port 18789 --tailscale serve

For a trusted same-LAN setup, use an authenticated gateway.bind: "lan" instead. The default loopback bind is not reachable from a phone. If the Gateway has not been configured yet, run apus onboard first so setup-code creation has a token or password auth path.

  1. Open the Control UI, select Nodes, and click Pair device on the Devices page. Full access is recommended and selected by default; choose Limited access only when you want to omit administrative Gateway controls, then click Create setup code.

  2. In the iOS app, open Settings -> Gateway, scan the QR code (or paste the setup code), and connect. Use a mobile setup code from Control UI or apus qr — not a gateway join URL from apus devices join-code.

    Paired Gateways remain in the Gateways list. The checkmark identifies the focused Gateway; use the bolt control on another row to keep its operator session connected at the same time. Switching focus does not disconnect other enabled Gateways. Only the focused Gateway receives the iPhone's capability-bearing node session, so camera, screen, location, and other device commands always have one unambiguous owner. iOS may suspend these foreground connections after the app enters the background.

  3. The official app connects automatically. If Pending approval shows a request, review its role and scopes before approving it.

    Settings → Gateway shows whether the saved operator connection has Full or Limited access. Plaintext LAN ws:// setup is automatically limited for bearer-token safety. If it is limited, configure wss:// or Tailscale Serve, scan a new full-access code from Control UI or apus qr, then reconnect to enable settings and upgrades.

The Control UI button requires an already paired session with operator.admin. As a terminal fallback, pick a discovered Gateway in the iOS app (or enable Manual Host and enter host/port), then approve the request on the Gateway host:

apus devices list
apus devices approve <requestId>

If the app retries pairing with changed auth details (role/scopes/public key), the previous pending request is superseded and a new requestId is created. Run apus devices list again before approval.

Optional: if the iOS node always connects from a tightly controlled subnet, you can opt in to first-time node auto-approval with explicit CIDRs or exact IPs:

{
  gateway: {
    nodes: {
      pairing: {
        autoApproveCidrs: ["192.168.1.0/24"],
      },
    },
  },
}

This is disabled by default. It applies only to fresh role: node pairing with no requested scopes. Operator/browser pairing and any role, scope, metadata, or public-key change still require manual approval.

  1. Verify connection:
apus nodes status
apus gateway call node.list --params "{}"

Health summaries

The iOS node can return an opt-in, read-only HealthKit aggregate for the current calendar day. iOS device consent and explicit Gateway command authorization are independent gates. See HealthKit summaries for setup, invocation, payload fields, privacy behavior, and troubleshooting.

Apple Watch voice and chat

Apus has two separate Watch voice paths:

Neither path runs a full agent or the stock Codex runtime on the Watch. The Gateway owns agent execution and tool policy; the Watch provides input, playback, and call controls.

Pair the Watch with the iPhone in Apple's Watch app, install Apus from Watch app -> My Watch -> Available Apps, then open Apus once on both devices.

Talk to Claw with the iPhone

This companion path does not need separate Watch Gateway pairing. Its Talk controls operate Talk on the paired iPhone; they do not enable a standalone Watch call.

  1. Connect the iPhone to your Gateway and select the chat you want to use.
  2. On the Watch, open Talk to Claw, then tap the voice button beside Message Apus.
  3. Use the native input sheet to dictate and submit your message. Keep Chat open on the Watch to hear the reply. The message pill also opens native input, but does not request a spoken reply.

The iPhone must remain available to relay messages. If its Gateway connection is asleep, Watch messages use the same bounded background reconnect as Watch quick replies, respecting the iPhone's auto-connect setting. Update Apus on both devices. A companion chat payload without ownership information cannot prove safe delivery, so the phone rejects it with an update-required error. A Watch app that predates that ownership check can still label a background transfer as queued. That label does not mean the phone accepted it. Check the phone's delivery warning and update both apps before sending again.

Both apps save delivery state before acknowledging it. The Watch retains the original command while waiting for the phone to accept it, and the phone saves the accepted run and its reply before sending the result back. Reopening an app or losing a result acknowledgment can therefore redeliver a saved reply without submitting the same chat again. A queued message is still not confirmation that the Gateway has processed it.

A permanent rejection, such as an expired message or retired Gateway route, does not mean the phone accepted the message. The Watch records that outcome without retrying the command. Reconnecting the Watch or requesting Refresh also retries saved result delivery without submitting another chat.

If delivery stalls, open Settings -> This iPhone -> Apple Watch -> Message Delivery on iPhone. Delivery uncertain means the phone cannot prove whether a send reached the Gateway; check the original conversation before resending. It does not automatically repeat that send. Saved messages that lack delivery context appear as Needs review. Copy their text to Chat if you still want to send it, or use Discard to delete that text. Completed cards offer Dismiss, which hides the card while preserving its original receipt for the Watch. Active deliveries offer neither action. Dismiss does not cancel a Gateway run or extend the reply's expiry.

If the Gateway accepts a message but the phone cannot save that acceptance, a later refresh or reconnect retries the local save without sending again. If the app exits before saving it, the send is treated as uncertain on reopening.

New commands and their saved app-local copies expire after 48 hours, measured from their original submission, not the most recent retry. Expiration stops automatic delivery and reply replay; it does not cancel remote work or delete the Gateway conversation. Expired app-local copies are removed on the next delivery operation or when the delivery list opens, not by a background timer. Previously saved messages marked Needs review remain on iPhone until you discard them or forget their Gateway.

The phone retains hash-only import markers after Discard or Forget so an older app's saved queue cannot restore identical deleted text on a later upgrade. If older messages conflict with saved delivery data or a forgotten Gateway, the app preserves their source and shows a recovery error. Contact support to recover those messages. Reset Onboarding deliberately erases all local client state, including the old queue and its import markers; it is not a targeted message deletion.

Only the reply belonging to the submitted turn is read aloud. Switching the Gateway or chat on iPhone retires the pending spoken reply and clears the old Watch preview. Leaving Watch Chat or backgrounding the app stops playback; a reply received while away can be read on return if its wait has not expired.

The spoken-reply wait expires after 90 seconds and shows Spoken reply timed out. Check Chat on iPhone., including after reopening the Watch app. Cancelling that wait or stopping speech does not cancel the Gateway chat run or remove a queued message. If no reply is spoken, refresh Chat or check the conversation on iPhone before resending. Long runs and interrupted return delivery can still require this manual readback. Keep the Gateway updated for reliable reply attribution when messages are collected into a later run.

The phone captures the Gateway, agent and exact session when it offers a chat or quick-reply action. Later changes to the selected chat cannot retarget a queued message, including shared main or global sessions. If that delivery context is unavailable, reconnect and refresh from iPhone rather than guessing the destination.

Forgetting a Gateway removes its phone delivery records and invalidates old commands, even if you pair the same Gateway again. A disconnected Watch cannot be remotely erased immediately; it clears a stale command when it receives the phone's rejection. The Watch's Forget direct setup action affects only its separate direct connection, not the iPhone relay.

These are app-local SQLite journals. They migrate when the apps open and do not require a Gateway database upgrade or apus doctor run.

Standalone voice

Standalone voice needs a current Gateway with Gateway-controlled WebRTC Talk, an iPhone connection with operator.admin for setup, and a secure Gateway URL the Watch can reach independently. Use the same secure endpoint and initial pairing requirements as direct Watch node setup: watchOS must trust the HTTPS certificate, and an iPhone-only or tailnet-only route is not enough when the Watch is away from the phone.

  1. Configure realtime Talk mode on the Gateway with webrtc transport and a provider/authentication combination that supports gateway-control-v1 and returns an ICE-lite answer with UDP candidates. Provider credentials stay on the Gateway.
  2. On iPhone, open Settings -> This iPhone -> Apple Watch -> Connect Apple Watch. Voice access is included in normal Watch setup; there is no separate voice enable setting.
  3. Open Apus on the Watch before the setup code expires. Open Talk on Watch and wait for Ready to talk.
  4. Tap Start, allow microphone access, and choose an agent if prompted. Keep Apus on screen until it shows Connected. Opening the voice screen alone does not start the microphone.
  5. Speak, use Mute or Unmute as needed, and tap End to finish. The screen shows the latest user and assistant transcripts; the Gateway retains the conversation history.

The one-time setup gives the Watch its own node credential and an operator credential with exactly operator.read and operator.talk. It does not grant admin access or copy the iPhone's saved Gateway token or password. Setup configuration is stored in the Watch Keychain; issued device credentials are stored in its protected native-state SQLite database, scoped to that Gateway. If setup is incomplete or expired, send it again from iPhone Settings. Watches paired with the older node-only setup need Connect Apple Watch once more to authorize voice. Existing grants are not silently expanded, and revoked access is not automatically restored. Pairing does not start a call or turn on the microphone.

Audio uses native WebRTC with Opus over UDP between the Watch and provider. The secure Gateway WebSocket carries call control and transcript events, not microphone audio. The Gateway controls provider tools and agent consultations; the Watch does not open a provider data channel or receive a permanent provider API key. Unsupported provider/authentication or transport choices fail visibly instead of switching to a different voice path. The native Watch transport requires ICE-lite; support for Gateway-controlled WebRTC alone is not sufficient.

Provider error notifications appear on the Watch without ending an otherwise active call. You can try another turn; an explicit session closure or an unrecoverable connection failure still ends the call.

Each new call uses a separate session for its selected agent. Network recovery is bounded and keeps that agent and chat session; Try Again after a terminal error starts a new call. This does not resume the iPhone's currently selected chat, and the companion chat and approval features still use the iPhone relay.

For OpenAI Gateway-controlled WebRTC calls, the Gateway schedules a 30-minute active-session lease during setup; audio activity does not renew it. When the Watch receives the session-ended event from lease expiry, it shows Call unavailable and does not retry automatically. Bring Apus to the foreground and tap Try Again to start a new call. The lease is not a guarantee of 30 minutes of usable audio, and calls may end earlier.

An established call uses background audio and is not intentionally ended merely because the display dims or the app backgrounds. Startup that backgrounds before connecting stops with a message asking you to keep Apus on screen. Navigating back, tapping End, disabling, changing or forgetting the Watch's Gateway connection, an audio interruption, or an unrecoverable failure ends the call.

Physical-Watch microphone/speaker routing, wrist-down operation, Wi-Fi/cellular handoff, battery use, and multi-hour reliability still need device validation. Simulator tests and native macOS provider-audio checks do not establish those behaviors. This is not an arbitrary always-on Gateway connection: watchOS low-level networking depends on an active audio session. UDP must be reachable; the Watch transport does not configure a TURN relay or TCP/WebSocket media fallback.

Review command approvals

An operator connection with operator.admin, or a paired operator.approvals connection explicitly targeted by the Gateway, can review pending exec requests on iPhone. The approval card shows the Gateway's sanitized command preview, warning, host context, expiry, and only the decisions offered by that request. The paired Apple Watch receives the same reviewer-safe prompt through the existing iPhone relay and offers the compact allow-once/deny decision subset. Direct Watch Gateway mode does not carry approval prompts.

Approval state is shared with the Control UI and supported chat surfaces. The first committed answer wins. iPhone and Watch fetch the Gateway's canonical terminal record after another surface resolves the request, after a remote resolved notification, and whenever a resolve acknowledgement may have been lost. Actions stay unavailable until that readback confirms whether the request remains pending.

Approval ownership is bound to the selected Gateway. Switching Gateways cannot apply an old prompt to the replacement connection. Gateways that predate the unified approval methods fall back to the shipped exec-specific methods; retained terminal state and richer cross-surface results require an updated Gateway.

Answer agent questions

Chat shows pending Gateway questions as native cards for operator connections with operator.questions (or operator.admin). Cards support single- and multi-select options, option descriptions, free-text Other answers, and an expiry countdown. Reconnects reload pending questions from the Gateway. A card locks when this device answers it, another surface answers it first, or the question expires or is cancelled.

Optional direct Apple Watch node

Direct mode gives the watch its own signed node identity and Gateway connection. Supported node commands continue to work over watch Wi-Fi or cellular while Apus is active, even when the paired iPhone is unavailable.

Requirements:

Setup:

  1. On iPhone, open Settings -> This iPhone -> Apple Watch (or Device -> Apple Watch in the offline fallback).
  2. Tap Connect Apple Watch.
  3. Open Apus on the watch before the short-lived setup code expires.
  4. Verify the separate Apple Watch row with apus nodes status.

The setup code contains a short-lived bootstrap credential for the Watch's node and limited read/Talk roles; treat it like a password until it expires. It never contains the iPhone's saved Gateway password or token. After pairing, the watch stores its own device credentials and deletes the bootstrap credential. The same setup covers the commands below and standalone voice; calls still require Start and microphone permission. Companion chat, iPhone Talk controls, approvals, and the existing watch.* notification flow remain iPhone-relay features.

A watch.notify receipt reports Watch transport delivery or queuing, not completion of the best-effort iPhone notification mirror. Cancellation is checked before starting a new Watch transfer or phone mirror; it cannot recall work already handed to WatchConnectivity. Once the phone mirror is handed off, it proceeds independently of the invoke.

Direct watchOS node commands:

Surface Commands Notes
Device device.info, device.status Watch identity, battery, thermal, storage, and network.
Notifications system.notify While the app is active; requires watch permission.

Relay-backed push for official builds

Official distributed iOS builds use an external push relay instead of publishing the raw APNs token to the Gateway. Official App Store builds from the public release lane use the hosted relay at https://ios-push-relay.openclaw.ai; this base URL is hardcoded for App Store distribution and does not read any override.

Custom relay deployments require a deliberately separate iOS build/deployment path whose relay URL matches the Gateway relay URL. The App Store release lane never accepts a custom relay URL. If you're using a custom relay build, set the matching Gateway relay URL:

{
  gateway: {
    push: {
      apns: {
        relay: {
          baseUrl: "https://relay.example.com",
        },
      },
    },
  },
}

How the flow works:

What the Gateway does not need for this path: no deployment-wide relay token, no direct APNs key for official App Store relay-backed sends.

Expected operator flow:

  1. Install the official iOS app.
  2. Optional: set gateway.push.apns.relay.baseUrl on the Gateway only when using a deliberately separate custom relay build.
  3. Pair the app to the Gateway and let it finish connecting.
  4. The app publishes push.apns.register once it has an APNs token, the operator session is connected, and relay registration succeeds.
  5. After that, push.test, reconnect wakes, and wake nudges can use the stored relay-backed registration.

Background alive beacons

When iOS wakes the app for a silent push, background refresh, or significant-location event, the app attempts a short node reconnect and then calls node.event with event: "node.presence.alive". The Gateway records this as lastSeenAtMs/lastSeenReason on the paired node/device metadata only after the authenticated node device identity is known.

The app treats a background wake as successfully recorded only when the Gateway response includes handled: true. Older Gateways may acknowledge node.event with { "ok": true }; that response is compatible but does not count as a durable last-seen update.

Background refresh wakes are requested through the system BackgroundTasks scheduler whenever the app moves to the background, after a silent push that could not be applied, and again after each refresh run; iOS decides when they actually execute. They stop if Background App Refresh is turned off for Apus in iOS Settings, leaving push and significant-location wakes.

Compatibility note:

Authentication and trust flow

The relay exists to enforce two constraints direct APNs-on-gateway cannot provide for official iOS builds:

Hop by hop:

  1. iOS app -> gateway: the app pairs with the Gateway through the normal Gateway auth flow, giving it an authenticated node session plus an authenticated operator session. The operator session calls gateway.identity.get.
  2. iOS app -> relay: the app calls the relay registration endpoints over HTTPS with App Attest proof plus a StoreKit app transaction JWS. The relay validates the bundle ID, App Attest proof, and Apple distribution proof, and requires the official/production distribution path — this is what blocks local Xcode/dev builds from using the hosted relay, since a local build cannot satisfy the official Apple distribution proof.
  3. gateway identity delegation: before relay registration, the app fetches the paired Gateway identity from gateway.identity.get and includes it in the relay registration payload. The relay returns a relay handle and a registration-scoped send grant delegated to that Gateway identity.
  4. gateway -> relay: the Gateway stores the relay handle and send grant from push.apns.register. On push.test, reconnect wakes, and wake nudges, the Gateway signs the send request with its own device identity; the relay verifies both the stored send grant and the Gateway signature against the delegated Gateway identity from registration. Another Gateway cannot reuse that stored registration, even if it somehow obtains the handle.
  5. relay -> APNs: the relay owns the production APNs credentials and the raw APNs token for the official build. The Gateway never stores the raw APNs token for relay-backed official builds; the relay sends the final push to APNs on behalf of the paired Gateway.

Why this design was created: to keep production APNs credentials out of user Gateways, avoid storing raw official-build APNs tokens on the Gateway, allow hosted relay usage only for official Apus iOS builds, and prevent one Gateway from sending wake pushes to iOS devices owned by a different Gateway.

Local/manual builds remain on direct APNs. If you are testing those builds without the relay, the Gateway still needs direct APNs credentials:

export APUS_APNS_TEAM_ID="TEAMID"
export APUS_APNS_KEY_ID="KEYID"
export APUS_APNS_PRIVATE_KEY_P8="$(cat /path/to/AuthKey_KEYID.p8)"

These are gateway-host runtime env vars, not Fastlane settings. apps/ios/fastlane/.env only stores App Store Connect auth such as APP_STORE_CONNECT_KEY_ID and APP_STORE_CONNECT_ISSUER_ID; it does not configure direct APNs delivery for local iOS builds.

Recommended gateway-host storage, consistent with other provider credentials under ~/.apus/credentials/:

mkdir -p ~/.apus/credentials/apns
chmod 700 ~/.apus/credentials/apns
mv /path/to/AuthKey_KEYID.p8 ~/.apus/credentials/apns/AuthKey_KEYID.p8
chmod 600 ~/.apus/credentials/apns/AuthKey_KEYID.p8
export APUS_APNS_PRIVATE_KEY_PATH="$HOME/.apus/credentials/apns/AuthKey_KEYID.p8"

Do not commit the .p8 file or place it under the repo checkout.

Discovery paths

Bonjour (LAN)

The iOS app browses _apus-gw._tcp on local. and, when configured, the same wide-area DNS-SD discovery domain. Same-LAN Gateways appear automatically from local.; cross-network discovery can use the configured wide-area domain without changing the beacon type.

Tailnet (cross-network)

If mDNS is blocked, use a unicast DNS-SD zone (choose a domain; example: apus.internal.) and Tailscale split DNS. See Bonjour for the CoreDNS example.

Manual host/port

Open Settings → Gateway, enable Use Manual Gateway, and enter the Gateway host + port (default 18789).

Multiple Gateways

The app keeps a registry of every Gateway it has paired with, so you can switch between them without pairing again:

Computer Use relationship

The iOS app is a mobile node surface, not a Codex Computer Use backend. Codex Computer Use and cua-driver mcp control a local macOS desktop through MCP tools; the iOS app exposes iPhone capabilities through Apus node commands such as camera.*, screen.*, location.*, and talk.*.

Agents can still operate the iOS app through Apus by invoking node commands, but those calls go through the Gateway node protocol and follow iOS foreground/background limits. Use Codex Computer Use for local desktop control and this page for iOS node capabilities.

Voice wake + talk mode

Start live voice with Siri or Shortcuts

The Start Live Voice App Shortcut opens Apus to the current chat and starts the same Talk path as the inline Talk control.

  1. Open Apus and pair and connect to your Gateway first. Live voice uses your existing Talk mode voice provider configuration; the shortcut does not configure a provider or bypass pairing.
  2. In Shortcuts → Apps → Apus, choose Start Live Voice. You can also ask Siri: "Start live voice with Apus".
  3. Allow microphone access when iOS prompts. Unlock your iPhone if asked, and keep Apus in the foreground while Talk starts. The shortcut does not bypass iOS unlock or foreground restrictions.

For quick access, save a shortcut containing Start Live Voice, then assign it under Settings → Action Button → Shortcut on a supported iPhone, or use Add to Home Screen in Shortcuts. Background voice remains subject to the same iOS limits as Talk started inside the app.

Common errors

Related docs