Native apps (iOS / Android)
HAPI offers native iOS and Android apps for controlling sessions on a hub you run. Agents execute on your CLI/Runner machines; the hub stores the session history. The apps connect directly to that hub and share the same conversations as the Web/PWA.
The native apps are paid downloads. The Web/PWA is free, works in any phone browser and includes features the apps do not have yet; see Features available through the web.
Install
| Platform | Device requirement | Download |
|---|---|---|
| iOS / iPadOS | iOS 17 or newer | App Store |
| Android | Android 8.0 (API 26) or newer | Google Play |
Store builds include native push through the encrypted push relay; no Firebase or Apple developer setup is needed on your hub. See Notifications.
Build from source (developers)
The app source is in the repository. Development builds follow the iOS README (Xcode 27.1 or newer, shared Hapi scheme; device builds require signing) or the Android README (JDK 17+, Android SDK, Gradle wrapper). Self-built apps need their own push configuration; see Notifications.
Pair with your hub
Start your hub, or use an existing HTTPS hub endpoint:
bashhapi hub --relayOpen the native app and choose a pairing method:
- Scan QR: use the in-app scanner with either QR code printed by the hub. Web Settings → Companion pairing also shows the companion QR.
- Open a pairing link:
hapicompanion://bindopens the app's pairing flow. Confirm the hub before pairing. - Enter manually: supply the hub URL and access token printed by the hub. On iOS, enter just the domain/IP and optional port; the separate protocol menu defaults to HTTPS. You can also paste a full address, or paste a pairing link into either field to fill both values before tapping Pair. Use the hub address, not the web frontend address
app.hapi.run.
The app checks reachability and protocol compatibility, then authenticates and stores credentials for that hub. A namespaced token such as
your-token:teamopens that namespace's sessions.
Use an HTTPS hub origin, for example https://hub.example.com. Both apps identify hubs by origin; path prefixes are not retained. Your phone must be able to reach the hub. localhost on a physical phone refers to the phone. The network relay, an HTTPS reverse proxy, or Tailscale Serve can provide an endpoint; see Deployment.
Android rejects HTTP URLs in manual entry, QR codes, deep links and saved hub state, including debug builds. iOS manual entry defaults to HTTPS and accepts HTTP only when explicitly selected or supplied in a full URL/pairing link. It warns about unencrypted HTTP and never automatically downgrades HTTPS. HTTP connection success still depends on system network policy. The iOS project declares no ATS exceptions, so HTTP input acceptance does not guarantee a working connection. Prefer HTTPS on both platforms.
Camera pairing is optional. Use manual entry on an iOS Simulator or any device without a usable scanner.
Sessions and everyday use
Start an agent on your computer with hapi and choose an installed agent, or run a specific agent command. The session appears in the app.
To create sessions from your phone, start a Runner on the machine that will execute the agent:
hapi runner startIn the app, select New Session, choose an online machine, directory and available agent, then create the session. Model and permission controls depend on the agent and session capabilities. Configured workspace roots constrain directory browsing and session creation.
| Capability | Current native behavior |
|---|---|
| Chat and permissions | Streaming messages, history, tool inspection, approvals and question answering on both platforms. |
| Composer | Text, photos/camera/files, drafts, queued-message actions, / command suggestions and steering when supported by the session. |
| Android incoming shares | Share files, photos, text or links to HAPI, choose a session and confirm sending from its composer. The picker can switch paired hubs and explicitly restore stopped sessions. |
| Session controls | Both support pin, rename, stopping a turn and sending to resume an inactive session. Active sessions can be archived; inactive sessions can be reopened explicitly or deleted. |
| Files and Git | Open Session files from the chat menu to browse/search files, inspect Git status and read diffs. |
| Scratchlist | On iOS, tap the tray beside the composer attachment button to enter Save draft mode; Android uses the chat menu. Text and attachments sync within the session. |
| Dictation | Record audio, transcribe through a configured hub provider, then edit the inserted text before sending. |
| Usage and storage | Available only to the hub owner (default namespace). |
| Display | English/Simplified Chinese, theme preferences, system text scaling and machine filtering on the session list. |
Share to an Android session
In another app, choose Share → HAPI, then select a session. Use the hub menu to choose another paired hub, or search the session list. A stopped session asks for restoration before files can upload. HAPI adds the files and any accompanying text to the session composer, preserving its existing draft; review the content and tap Send.
Files use the existing attachment limits and image compression policy. Failed files must be retried or removed before continuing. Before import, pending content stays in private local storage for up to 24 hours, including while pairing or recovering from process recreation. After import, ordinary Android attachment behavior applies: leaving the chat discards unsent attachments.
Session-list parity
Android and iOS use the shared base session-list order: global-pinned sessions, project-pinned sessions, active sessions, pending-request priority among active sessions, then updatedAt descending. Ties preserve their incoming order. The Web sidebar may apply additional directory grouping and client-side search ranking; it also exposes text, date-range and unread-only session-list filters. Both native apps offer machine filtering and relevance-ranked text search. Android adds a date range; iOS adds an unread-only lens and Mark All as Read. Android and iOS README files describe their platform-specific filter presentation and session-row differences.
Selecting an attachment starts its upload after preparation, before you send the message or save the Scratchlist entry. Removing it requests cleanup of unused uploads on a best-effort basis. See the Privacy Policy for storage and provider data flows.
On iOS, Scratchlist opens as a compact drawer above the same input field. Save draft keeps the content without sending it; closing the drawer with × leaves unsaved input intact. The drawer shows one recent draft, or just its header while typing. Take draft keeps the saved entry and restores its text and attachments. If the input is nonempty, choose Append to input or Save input, then take draft. Add to send queue in the draft's menu is an explicit send and removes the draft only after acceptance. A failed removal can be retried without sending again. Tap the drawer header for the full list and pull down to search text or filenames. The same menu offers edit, copy and delete; editing an original draft commits text and attachments together on Save. Cancelling an edit leaves the original unchanged.
Dictation uses the first configured provider that supports standard transcription. The microphone stays hidden until provider discovery succeeds; grant microphone permission when first using it. Configure providers on the hub or through Web Settings → Voice. Native dictation inserts text and does not send it automatically. See Voice input and assistant.
Features available through the web
Use the web app for the remote terminal, Work Graph, realtime dictation and voice assistant, Markdown export, and the skills picker. These have no native UI. On iOS, the chat's … menu forks the current conversation, rewinds or forks at a user message with a native history point, and exports the session as JSON; Android has no fork, rewind or export UI. Session history actions also depend on the agent's capabilities. Both apps display existing scheduled messages. On iOS, the composer's clock schedules a text-only send up to 7 days ahead (relative presets or a specific time); Android has no form to create scheduled sends. Android's full-text file export is a reader action, separate from exporting an entire session.
Multiple hubs and sign-out
The home screen's hub menu lets you add or switch hubs, open Settings, and sign out. You can keep several hubs paired, with one active at a time; session lists and drafts belong to their hub. Signing out removes that hub's stored credentials and attempts to unregister its push token.
Both apps refresh authentication automatically. Temporary network or server failures retain paired credentials. A rejected access token or a freshly issued token that is rejected again requires re-pairing with valid credentials.
Notifications
Allow system notifications after pairing. Native notifications support permission approval/denial and replies; a notification for the foreground chat is suppressed locally because the conversation is already visible.
| App build | Push requirements |
|---|---|
| Google Play build | A current hub uses the encrypted push relay by default when no private FCM credentials are configured. Google Play services and FCM connectivity are required. |
| App Store build | A current hub uses the encrypted push relay by default. No Apple developer credentials are needed on the user's hub. |
| Private Android Firebase project | Bundle that project's client configuration and configure matching Firebase service-account credentials on the hub for direct FCM. |
| Android without Firebase configuration | Session features work; FCM push is unavailable. |
| Self-signed iOS build | Configure direct APNs or a self-hosted push relay with credentials matching the app's developer account, bundle ID and APNs environment. Follow the iOS README's signing instructions. |
The official push relay carries encrypted notification content and routing metadata. It is separate from the network tunnel enabled by hapi hub --relay, so it also works with hubs reached through another HTTPS setup. Private direct FCM delivery uses an unencrypted notification data payload; direct APNs remains encrypted. See Notifications and the push contract for configuration.
Push registration covers every paired hub. On iOS, a notification tap finds the hub that owns the session (active hub first) and switches to it before opening the chat. Android opens the session against the active hub; if it cannot be found, switch to its owning hub. Background notification actions on both platforms try paired hubs to resolve the session.
Troubleshooting
| Symptom | Check |
|---|---|
| Pairing cannot reach the hub | Use the HTTPS hub origin, verify phone network access and certificate trust, and check the hub's listening address or reverse proxy. |
| Protocol mismatch | Update the app from the App Store / Google Play and update the hub; both apps require the hub protocol version to equal their supported version. |
| Re-pairing requested | Use the current access token, including the intended namespace suffix. |
| No machines for New Session | Start the Runner, verify its connection and namespace, and check agent availability on that machine. |
| Microphone missing | Configure a standard transcription provider and ensure the hub is reachable. |
| Notifications missing | Check OS permission, the build's Firebase/APNs configuration, provider connectivity and hub push settings. |
For development and conformance checks, use the platform READMEs and the client contract. Protocol fixture conformance does not imply identical native and web UI features.