Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

<table>
<tr>
Expand Down
36 changes: 22 additions & 14 deletions ios/TaskWraithApp/AppStorePrivacyNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
59 changes: 38 additions & 21 deletions ios/TaskWraithApp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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).
Loading