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).