mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-24 09:39:35 +00:00
371 lines
17 KiB
Markdown
371 lines
17 KiB
Markdown
# OpenClaw iOS (Super Alpha)
|
|
|
|
This iOS app is super-alpha and internal-use only. The first public App Store release targets iPhone and connects to an OpenClaw Gateway as a `role: node`.
|
|
|
|
## Distribution Status
|
|
|
|
- Public distribution: App Store Connect app created; production signing is configured through the App Store release Fastlane path.
|
|
- Internal TestFlight distribution: uses the same App Store distribution archive uploaded to App Store Connect.
|
|
- Local/manual deploy from source via Xcode remains the default development path.
|
|
|
|
## Super-Alpha Disclaimer
|
|
|
|
- Breaking changes are expected.
|
|
- UI and onboarding flows can change without migration guarantees.
|
|
- Foreground use is the only reliable mode right now.
|
|
- Treat this build as sensitive while permissions and background behavior are still being hardened.
|
|
|
|
## Exact Xcode Manual Deploy Flow
|
|
|
|
1. Prereqs:
|
|
- Xcode 16+
|
|
- `pnpm`
|
|
- `xcodegen`
|
|
- Apple Development signing set up in Xcode
|
|
2. From repo root:
|
|
|
|
```bash
|
|
pnpm install
|
|
./scripts/ios-configure-signing.sh
|
|
cd apps/ios
|
|
xcodegen generate
|
|
open OpenClaw.xcodeproj
|
|
```
|
|
|
|
3. In Xcode:
|
|
- Scheme: `OpenClaw`
|
|
- Destination: connected iPhone (recommended for real behavior)
|
|
- Build configuration: `Debug`
|
|
- Run (`Product` -> `Run`)
|
|
4. If signing fails on a personal team:
|
|
- Use unique local bundle IDs via `apps/ios/LocalSigning.xcconfig`.
|
|
- Start from `apps/ios/LocalSigning.xcconfig.example`.
|
|
|
|
Shortcut command (same flow + open project):
|
|
|
|
```bash
|
|
pnpm ios:open
|
|
```
|
|
|
|
## App Store Release Flow
|
|
|
|
Prereqs:
|
|
|
|
- Xcode 16+
|
|
- `pnpm`
|
|
- `xcodegen`
|
|
- `fastlane`
|
|
- Apple account signed into Xcode for the canonical OpenClaw team (`FWJYW4S8P8`)
|
|
- Fastlane Apple Developer Portal session for the canonical OpenClaw team when creating bundle IDs or enabling services
|
|
- Release-owner access to the encrypted signing repo password (`MATCH_PASSWORD`)
|
|
- App Store Connect app already created for `ai.openclawfoundation.app`
|
|
- App Store Connect API key set up in Keychain via `scripts/ios-app-store-connect-keychain-setup.sh` when auto-resolving a build number or uploading to App Store Connect
|
|
|
|
Release behavior:
|
|
|
|
- Local development uses the canonical `ai.openclawfoundation.app*` bundle IDs when the OpenClaw team is available, and unique `ai.openclawfoundation.app.test.*` bundle IDs only for non-canonical fallback teams.
|
|
- App Store release uses canonical `ai.openclawfoundation.app*` bundle IDs through a temporary generated xcconfig in `apps/ios/build/AppStoreRelease.xcconfig`.
|
|
- App Store release uses manual `Apple Distribution` signing with profile names pinned in `apps/ios/Config/AppStoreSigning.json`.
|
|
- Fastlane owns one-time Developer Portal setup, encrypted `match` signing sync to the repo/branch pinned in `apps/ios/Config/AppStoreSigning.json`, and release handling.
|
|
- App Store release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, `OpenClawPushAPNsEnvironment=production`, and a production `aps-environment` entitlement.
|
|
- `pnpm ios:release:upload` generates App Store screenshots and uploads release notes before archiving and uploading the IPA.
|
|
- `pnpm ios:release` remains a compatibility alias for `pnpm ios:release:upload`; prefer the explicit upload command in new release docs and automation.
|
|
- App Review submission is manual in App Store Connect. The release lane uploads a build and metadata, but does not submit for review.
|
|
- The release flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
|
|
- `apps/ios/version.json` is the pinned iOS release version source.
|
|
- `apps/ios/CHANGELOG.md` is the iOS-only changelog and release-note source.
|
|
- The pinned iOS version must use CalVer like `2026.4.10`.
|
|
- That pinned value becomes:
|
|
- `CFBundleShortVersionString = 2026.4.10`
|
|
- `CFBundleVersion = next App Store Connect build number for 2026.4.10`
|
|
- Changing the root gateway version does not change the iOS app version until you explicitly pin from the gateway.
|
|
- See `apps/ios/VERSIONING.md` for the full workflow.
|
|
|
|
Relay behavior for App Store builds:
|
|
|
|
- Release builds default to `https://ios-push-relay.openclaw.ai`.
|
|
- Optional custom relay override: `OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com`
|
|
This must be a plain `https://host[:port][/path]` base URL without whitespace, query params, fragments, or xcconfig metacharacters.
|
|
|
|
Signing setup commands:
|
|
|
|
```bash
|
|
pnpm ios:release:signing:plan
|
|
pnpm ios:release:signing:check
|
|
pnpm ios:release:signing:setup
|
|
MATCH_PASSWORD=... pnpm ios:release:signing:sync:push
|
|
MATCH_PASSWORD=... pnpm ios:release:signing:sync:pull
|
|
```
|
|
|
|
Release-owner secrets:
|
|
|
|
- App Store Connect API auth uses Keychain for private key material plus non-secret `apps/ios/fastlane/.env` variables.
|
|
- The encrypted signing repo password lives outside this repo in the release-owner vault and is exposed locally as `MATCH_PASSWORD`.
|
|
- Apple Distribution private keys, certificates, provisioning profiles, and decrypted signing sync output stay under `apps/ios/build/` or Keychain and are gitignored.
|
|
- Rotating release signing means refreshing Fastlane `match` assets and pushing a fresh encrypted sync state.
|
|
|
|
Prepare the generated release xcconfig/project without archiving:
|
|
|
|
```bash
|
|
pnpm ios:release:prepare -- --build-number 7
|
|
```
|
|
|
|
Archive without upload:
|
|
|
|
```bash
|
|
pnpm ios:release:archive
|
|
```
|
|
|
|
Archive and upload to App Store Connect:
|
|
|
|
```bash
|
|
pnpm ios:release:upload
|
|
```
|
|
|
|
If you need to force a specific build number:
|
|
|
|
```bash
|
|
pnpm ios:release:upload -- --build-number 7
|
|
```
|
|
|
|
### Maintainer Quick Release Checklist
|
|
|
|
Use this when a clone is missing local iOS release setup and you want the shortest path to an App Store Connect upload.
|
|
|
|
1. Confirm Fastlane auth is set up:
|
|
|
|
```bash
|
|
cd apps/ios
|
|
fastlane ios auth_check
|
|
```
|
|
|
|
2. If auth is missing, bootstrap it once on this Mac:
|
|
|
|
```bash
|
|
scripts/ios-app-store-connect-keychain-setup.sh \
|
|
--key-path /absolute/path/to/AuthKey_XXXXXXXXXX.p8 \
|
|
--issuer-id YOUR_ISSUER_ID \
|
|
--write-env
|
|
```
|
|
|
|
This should create `apps/ios/fastlane/.env` with non-secret App Store Connect variables while the private key stays in Keychain.
|
|
|
|
3. Confirm the App Store Connect app and Apple Developer identifiers/capabilities exist for:
|
|
- `ai.openclawfoundation.app`
|
|
- `ai.openclawfoundation.app.share`
|
|
- `ai.openclawfoundation.app.activitywidget`
|
|
- `ai.openclawfoundation.app.watchkitapp`
|
|
- `ai.openclawfoundation.app.watchkitapp.extension`
|
|
|
|
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.
|
|
|
|
4. Optional: set a custom official relay URL for the build. If unset, the release flow uses `https://ios-push-relay.openclaw.ai`.
|
|
|
|
```bash
|
|
export OPENCLAW_PUSH_RELAY_BASE_URL=https://relay.example.com
|
|
```
|
|
|
|
5. If you are starting a brand-new production release train, pin iOS to the current gateway version first:
|
|
|
|
```bash
|
|
pnpm ios:version:pin -- --from-gateway
|
|
```
|
|
|
|
6. Upload the build:
|
|
|
|
```bash
|
|
pnpm ios:release:upload
|
|
```
|
|
|
|
7. Expected behavior:
|
|
- Fastlane reads `apps/ios/version.json`
|
|
- verifies synced iOS versioning artifacts
|
|
- resolves the next App Store Connect build number for that short version
|
|
- generates deterministic App Store screenshots
|
|
- uploads release notes and screenshots to the editable App Store version
|
|
- generates `apps/ios/build/AppStoreRelease.xcconfig`
|
|
- archives `OpenClaw`
|
|
- uploads the IPA to App Store Connect for TestFlight/App Review use
|
|
- leaves App Review submission for a maintainer to complete manually
|
|
|
|
8. Expected outputs after a successful run:
|
|
- `apps/ios/build/app-store/OpenClaw-<version>.ipa`
|
|
- `apps/ios/build/app-store/OpenClaw-<version>.app.dSYM.zip`
|
|
- Fastlane log line like `Uploaded iOS App Store build: version=<version> short=<short> build=<build>`
|
|
|
|
9. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
|
|
|
|
## iOS Versioning Workflow
|
|
|
|
- Pinned iOS release version: `apps/ios/version.json`
|
|
- iOS-only changelog: `apps/ios/CHANGELOG.md`
|
|
- Generated checked-in artifacts:
|
|
- `apps/ios/Config/Version.xcconfig`
|
|
- `apps/ios/fastlane/metadata/en-US/release_notes.txt`
|
|
- Useful commands:
|
|
|
|
```bash
|
|
pnpm ios:version
|
|
pnpm ios:version:check
|
|
pnpm ios:version:sync
|
|
pnpm ios:version:pin -- --from-gateway
|
|
pnpm ios:version:pin -- --version 2026.4.10
|
|
```
|
|
|
|
Recommended flow:
|
|
|
|
### TestFlight iteration on an existing train
|
|
|
|
1. Keep `apps/ios/version.json` pinned to the current train version.
|
|
2. Update `apps/ios/CHANGELOG.md`, usually under `## Unreleased` while iterating.
|
|
3. Run `pnpm ios:version:sync` after changelog changes.
|
|
4. Upload more TestFlight builds with `pnpm ios:release:upload`.
|
|
5. Let Fastlane bump only the numeric build number.
|
|
|
|
### Starting the next production release train
|
|
|
|
1. Pin iOS to the current gateway version:
|
|
|
|
```bash
|
|
pnpm ios:version:pin -- --from-gateway
|
|
```
|
|
|
|
2. Update `apps/ios/CHANGELOG.md` for the new release as needed.
|
|
3. Run `pnpm ios:version:sync`.
|
|
4. Submit the first App Store Connect build for that newly pinned version.
|
|
5. Keep iterating on that same version until the release candidate is ready.
|
|
|
|
See `apps/ios/VERSIONING.md` for the detailed spec.
|
|
|
|
## APNs Expectations For Local/Manual Builds
|
|
|
|
- The app calls `registerForRemoteNotifications()` at launch.
|
|
- `apps/ios/Sources/OpenClaw.entitlements` derives `aps-environment` from the active build configuration/signing override.
|
|
- APNs token registration to gateway happens only after gateway connection (`push.apns.register`).
|
|
- Local/manual builds default to `OpenClawPushTransport=direct`, `OpenClawPushDistribution=local`, and a development `aps-environment` entitlement.
|
|
- Your selected team/profile must support Push Notifications for the app bundle ID you are signing.
|
|
- If push capability or provisioning is wrong, APNs registration fails at runtime (check Xcode logs for `APNs registration failed`).
|
|
- The gateway host also needs direct APNs auth configured separately with `OPENCLAW_APNS_TEAM_ID`, `OPENCLAW_APNS_KEY_ID`, and either `OPENCLAW_APNS_PRIVATE_KEY_P8` or `OPENCLAW_APNS_PRIVATE_KEY_PATH`.
|
|
- Recommended gateway-host storage for the APNs `.p8` file is `~/.openclaw/credentials/apns/AuthKey_<KEYID>.p8` with restrictive permissions, then point `OPENCLAW_APNS_PRIVATE_KEY_PATH` at that file.
|
|
- `apps/ios/fastlane/.env` only covers App Store Connect / Fastlane auth; it does not provide gateway APNs credentials for local direct-push testing.
|
|
- Debug builds default to `OpenClawPushAPNsEnvironment=sandbox`; Release builds default to `production`.
|
|
|
|
## APNs Expectations For Official Builds
|
|
|
|
- Official/TestFlight builds register with the external push relay before they publish `push.apns.register` to the gateway.
|
|
- The gateway registration for relay mode contains an opaque relay handle, a registration-scoped send grant, relay origin metadata, and installation metadata instead of the raw APNs token.
|
|
- The relay registration is bound to the gateway identity fetched from `gateway.identity.get`, so another gateway cannot reuse that stored registration.
|
|
- The app persists the relay handle metadata locally so reconnects can republish the gateway registration without re-registering on every connect.
|
|
- If the relay base URL changes in a later build, the app refreshes the relay registration instead of reusing the old relay origin.
|
|
- Relay mode requires a reachable relay base URL and uses App Attest plus a StoreKit app transaction JWS during registration.
|
|
- Gateway-side relay sending is configured through `gateway.push.apns.relay.baseUrl` in `openclaw.json`. `OPENCLAW_APNS_RELAY_BASE_URL` remains a temporary env override only.
|
|
|
|
## Official Build Relay Trust Model
|
|
|
|
- `iOS -> gateway`
|
|
- The app must pair with the gateway and establish both node and operator sessions.
|
|
- The operator session is used to fetch `gateway.identity.get`.
|
|
- `iOS -> relay`
|
|
- The app registers with the relay over HTTPS using App Attest plus a StoreKit app transaction JWS.
|
|
- The relay requires the official production/TestFlight distribution path, which is why local
|
|
Xcode/dev installs cannot use the hosted relay.
|
|
- `gateway delegation`
|
|
- The app includes the gateway identity in relay registration.
|
|
- The relay returns a relay handle and registration-scoped send grant delegated to that gateway.
|
|
- `gateway -> relay`
|
|
- The gateway signs relay send requests with its own device identity.
|
|
- The relay verifies both the delegated send grant and the gateway signature before it sends to
|
|
APNs.
|
|
- `relay -> APNs`
|
|
- Production APNs credentials and raw official-build APNs tokens stay in the relay deployment,
|
|
not on the gateway.
|
|
|
|
This exists to keep the hosted relay limited to genuine OpenClaw official builds and to ensure a
|
|
gateway can only send pushes for iOS devices that paired with that gateway.
|
|
|
|
## What Works Now (Concrete)
|
|
|
|
- Pairing via QR or setup code flow (`/pair qr` or `/pair`, then `/pair approve` in Telegram).
|
|
- Gateway connection via discovery or manual host/port with TLS fingerprint trust prompt.
|
|
- Chat + Talk surfaces through the operator gateway session.
|
|
- iOS node commands in foreground: camera snap/clip, canvas present/navigate/eval/snapshot, screen record, location, contacts, calendar, reminders, photos, motion, local notifications.
|
|
- Authenticated background `node.presence.alive` beacons that update gateway last-seen metadata when the app moves between foreground and background, without treating suspended sockets as connected.
|
|
- Share extension deep-link forwarding into the connected gateway session.
|
|
|
|
## Computer Use Relationship
|
|
|
|
The iOS app is not a Codex Computer Use backend. Computer Use and `cua-driver mcp` are macOS desktop-control paths; iOS exposes device capabilities as OpenClaw node commands through the gateway. Agents can drive the iPhone canvas, camera, screen, location, voice, and other node capabilities with `node.invoke`, subject to iOS foreground/background limits.
|
|
|
|
## Location Automation Use Case (Testing)
|
|
|
|
Use this for automation signals ("I moved", "I arrived", "I left"), not as a keep-awake mechanism.
|
|
|
|
- Product intent:
|
|
- movement-aware automations driven by iOS location events
|
|
- example: arrival/exit geofence, significant movement, visit detection
|
|
- Non-goal:
|
|
- continuous GPS polling just to keep the app alive
|
|
|
|
Test path to include in QA runs:
|
|
|
|
1. Enable location permission in app:
|
|
- set `Always` permission
|
|
- verify background location capability is enabled in the build profile
|
|
2. Background the app and trigger movement:
|
|
- walk/drive enough for a significant location update, or cross a configured geofence
|
|
3. Validate gateway side effects:
|
|
- node reconnect/wake if needed
|
|
- expected location/movement event arrives at gateway
|
|
- automation trigger executes once (no duplicate storm)
|
|
4. Validate resource impact:
|
|
- no sustained high thermal state
|
|
- no excessive background battery drain over a short observation window
|
|
|
|
Pass criteria:
|
|
|
|
- movement events are delivered reliably enough for automation UX
|
|
- no location-driven reconnect spam loops
|
|
- app remains stable after repeated background/foreground transitions
|
|
|
|
## Known Issues / Limitations / Problems
|
|
|
|
- Foreground-first: iOS can suspend sockets in background; reconnect recovery is still being tuned.
|
|
- Background command limits are strict: `canvas.*`, `camera.*`, `screen.*`, and `talk.*` are blocked when backgrounded.
|
|
- Background location requires `Always` location permission.
|
|
- Pairing/auth errors intentionally pause reconnect loops until a human fixes auth/pairing state.
|
|
- Voice Wake and Talk contend for the same microphone; Talk suppresses wake capture while active.
|
|
- APNs reliability depends on local signing/provisioning/topic alignment.
|
|
- Expect rough UX edges and occasional reconnect churn during active development.
|
|
|
|
## Current In-Progress Workstream
|
|
|
|
Automatic wake/reconnect hardening:
|
|
|
|
- improve wake/resume behavior across scene transitions
|
|
- reduce dead-socket states after background -> foreground
|
|
- tighten node/operator session reconnect coordination
|
|
- reduce manual recovery steps after transient network failures
|
|
|
|
## Debugging Checklist
|
|
|
|
1. Confirm build/signing baseline:
|
|
- regenerate project (`xcodegen generate`)
|
|
- verify selected team + bundle IDs
|
|
2. In app `Settings -> Gateway`:
|
|
- confirm status text, server, and remote address
|
|
- verify whether status shows pairing/auth gating
|
|
3. If pairing is required:
|
|
- run `/pair approve` from Telegram, then reconnect
|
|
4. If discovery is flaky:
|
|
- enable `Discovery Debug Logs`
|
|
- inspect `Settings -> Gateway -> Discovery Logs`
|
|
5. If network path is unclear:
|
|
- switch to manual host/port + TLS in Gateway Advanced settings
|
|
6. In Xcode console, filter for subsystem/category signals:
|
|
- `ai.openclawfoundation.app`
|
|
- `GatewayDiag`
|
|
- `APNs registration failed`
|
|
7. Validate background expectations:
|
|
- repro in foreground first
|
|
- then test background transitions and confirm reconnect on return
|