diff --git a/README.md b/README.md index a8c55e88..45bf6184 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,13 @@ agents against developer workspaces. It provides a macOS-focused Electron UI for provider CLIs and SDK-backed workflows while keeping execution, history, and workspace state on the user's machine. -> **iOS companion status:** TaskWraith for iPhone/iPad is pending -> TestFlight/App Store review. Until then, the iOS companion is provisionally -> available from this repository for testers who can sign and provision the app -> with their own Apple Developer team. +> **iOS companion status:** TaskWraith for iPhone/iPad is in **TestFlight beta**. +> It is a **Mac companion** — it pairs with TaskWraith on macOS over an +> end-to-end-encrypted connection to monitor runs, approve actions, and reply +> from the phone; it is not a standalone AI app. Testers can also build it from +> this repository with their own Apple Developer team. Push notifications are +> opt-in after pairing and require APNs credentials on the Mac (see +> `ios/TaskWraithApp/README.md`). diff --git a/ios/TaskWraithApp/AppStorePrivacyNotes.md b/ios/TaskWraithApp/AppStorePrivacyNotes.md index 349a8837..e35e46fb 100644 --- a/ios/TaskWraithApp/AppStorePrivacyNotes.md +++ b/ios/TaskWraithApp/AppStorePrivacyNotes.md @@ -18,20 +18,28 @@ Mac/provider runtime. ## Remote notifications -Release/TestFlight builds do not request APNs registration by default. The -current desktop APNs sender is a local-admin/development feature that requires -an Apple APNs auth key for the companion bundle; that is not a reviewable -consumer App Store design. Keep release registration disabled until one of -these is true: - -- A developer-controlled push relay owns the APNs key and sends metadata-only - wake pushes. -- Distribution is explicitly scoped to local-admin/internal builds with - documented APNs key management outside App Store review. - -Debug builds can opt into local APNs registration for development. Release -builds can only opt in by compiling with `TASKWRAITH_ENABLE_APNS_REGISTRATION` -after the release owner accepts the distribution model. +Release/TestFlight builds request notification permission **after a successful +pairing** (not at cold launch). The user can deny; the app still works without +push (open the app to reconnect / refresh). When granted, the APNs device token +travels over the paired encrypted bridge to the user's own Mac and is stored +there — there is no developer-operated push backend. + +**Push delivery depends on the user's Mac having APNs credentials.** The Mac +sends wake pushes only when an Apple APNs auth key (`.p8`) for the companion +bundle is configured via environment (`TASKWRAITH_APNS_KEY_PATH`, +`TASKWRAITH_APNS_KEY_ID`, `TASKWRAITH_APNS_TEAM_ID`, `TASKWRAITH_APNS_BUNDLE_ID`). +Without those, the Mac uses a no-op pusher: pairing and manual reconnect work, +but **no notifications are delivered**. The relay carries pairing/transport only +— it does not send push. + +APNs payloads carry routing metadata only (pair identifier, coarse reason, +thread/run identifiers, timestamps) — never prompts, commands, diffs, file +paths, filenames, workspace names, model output, or user messages. + +App Store Connect: declare push notifications; the data is the device token + +routing metadata, kept within the user's own device + Mac (no developer +backend). For a consumer launch, do **not** feature push as a hero capability +unless the Mac ships with push pre-configured — present it as optional/advanced. ## Export compliance diff --git a/ios/TaskWraithApp/README.md b/ios/TaskWraithApp/README.md index d8e4f8d6..12c0934d 100644 --- a/ios/TaskWraithApp/README.md +++ b/ios/TaskWraithApp/README.md @@ -4,8 +4,10 @@ SwiftUI app that pairs with TaskWraith on the Mac over the `taskwraith-e2ee-v1` relay transport and renders the remote task feed (approvals, questions, running agents) with action controls — all end-to-end encrypted. -This is the thin UI shell. The substance lives in the `TaskWraithKit` Swift -package next door: +The app target is a thin wrapper; the substance lives in the `TaskWraithKit` +Swift package next door (the companion itself is feature-rich — pairing, +approvals/questions, global + side chats, ensemble roster, diff/file views, +token streaming, composer shells, APNs actions): - **`TaskWraithKit`** — the CryptoKit port of `src/shared/e2ee` + the `RelayTransportClient` and Codable domain models. Validated byte-for-byte @@ -59,9 +61,10 @@ cleartext `ws://` to a LAN/Tailscale relay. Checklist: 4. Run, tap **Scan QR code**, point it at the ghost QR on the Mac, compare the 6-digit codes, confirm on the Mac. Done. -> ⚠️ The ATS `NSAllowsArbitraryLoads` exception is for development only — -> switch the relay to `wss://` (TLS) and remove the exception before any -> TestFlight build, alongside the crypto review noted below. +> ATS is already scoped to `NSAllowsLocalNetworking` only (no global +> `NSAllowsArbitraryLoads`). LAN relays use cleartext `ws://` on the local +> network; off-LAN/remote use requires a `wss://` (TLS) relay — the Tailscale +> serve path documented for desktop 1.5+. ## Pairing locally @@ -74,11 +77,6 @@ cleartext `ws://` to a LAN/Tailscale relay. Checklist: 4. In the app, paste the pairing-code JSON and tap **Pair**. Compare the 6-digit code, then tap **Pair** on the Mac. The task feed appears. -## Not yet wired (intentional scaffold gaps) - -- **Incremental run-event rendering** — the feed renders the projection snapshot; - live `bridge.runEvent` streaming into a transcript view is a follow-up. - ## TestFlight / App Store archive path The generated Xcode project has a shared `TaskWraith` scheme and a Release @@ -93,23 +91,42 @@ TASKWRAITH_APPLE_TEAM_ID=ABCDE12345 ./scripts/archive-testflight.sh Before upload: -1. Confirm the archived entitlement prints `aps-environment = production`. +1. Confirm the **exported IPA** entitlements show `aps-environment = production` + and `get-task-allow = false` (the script prints these). The archive-stage + entitlements may read `development` / `get-task-allow = true` under automatic + signing — that is expected; the export is what ships. 2. Complete the App Store Connect export-compliance questionnaire. This project sets `ITSAppUsesNonExemptEncryption=false`: the app's CryptoKit E2EE uses standard algorithms that qualify for the export-compliance exemption. 3. Read `AppStorePrivacyNotes.md` and make the App Store privacy answers match the selected distribution model. -Remote notifications are intentionally disabled by default in Release builds. -The current Mac-side APNs sender is a local-admin/development feature that -requires an APNs auth key for the companion bundle; that is not suitable for a -consumer TestFlight/App Store review flow. Re-enable Release APNs registration -only after a developer-controlled push service exists, or for an explicitly -internal/local-admin distribution by compiling with -`TASKWRAITH_ENABLE_APNS_REGISTRATION`. +## Push notifications (post-pairing opt-in; delivery needs Mac credentials) + +Release/TestFlight builds request notification permission **after a successful +pairing**, and register the APNs token to the user's paired Mac. The app works +fine if the user denies (open to reconnect/refresh). + +Delivery requires the **Mac** to have APNs credentials configured — set these in +the Mac's environment before `npm run dev` / the packaged app launch: + +```sh +TASKWRAITH_APNS_KEY_PATH=~/.appstoreconnect/private_keys/AuthKey_XXXXXXXXXX.p8 +TASKWRAITH_APNS_KEY_ID=XXXXXXXXXX # 10-char Key ID +TASKWRAITH_APNS_TEAM_ID=8CZML8FK2D # 10-char Team ID +TASKWRAITH_APNS_BUNDLE_ID=com.taskwraith.companion +``` + +Without these the Mac uses a no-op pusher (pairing + manual reconnect still +work; no pushes delivered). The relay does not send push. For a consumer App +Store listing, don't market push as a hero feature unless the Mac ships with +push pre-configured — see `AppStorePrivacyNotes.md`. ## Security -The E2EE core is security-sensitive. Recommend an independent crypto review of -`TaskWraithKit` (and the shared `src/shared/e2ee`) before a public App Store -submission. +The E2EE core is security-sensitive. An independent crypto review of +`TaskWraithKit` (and the shared `src/shared/e2ee`) was completed 2026-06; +CRITICAL/HIGH findings were fixed and the results are tracked in +`docs/security/e2ee-review-findings.md` (one residual MED — silent identity +regeneration if the Keychain/`safeStorage` is unavailable — is documented there +and accepted for the companion-beta scope).