# Building Chanora v0.2.0-beta.1 on iOS via macOS host

**Document type:** Release / Platform Build Instructions
**Version:** 0.1.0
**Status:** Draft
**Language:** English
**Product:** Chanora
**Repo path:** `docs/release/ios-build.md`

---

## 1. Purpose

This document gives exact step-by-step instructions for building the
Chanora `v0.2.0-beta.1` Internal Beta on an Apple macOS host. It
exists because the development host is Linux x86_64; the entire
iOS toolchain (Xcode, `xcrun`, `codesign`, `xcodebuild`, the
`iPhoneOS` SDK) is macOS-only under Apple licence and cannot be
cross-compiled from Linux. Even projects like `xcbuild` or
`cctools-port` cannot produce a signed `.ipa` that an iOS device's
`installd` will accept.

After following this doc you will have:

- `Runner.app` — the iOS app bundle.
- `libchanora_bridge.a` and/or an XCFramework containing both
  device (arm64) and simulator (arm64+x86_64) slices of the Rust
  cdylib.
- A `Runner.ipa` archive suitable for ad-hoc / TestFlight / App
  Store distribution depending on the provisioning profile you sign
  with.

The doc assumes you have:

- A macOS host (physical Mac or rental — see §3 for options).
- An Apple Developer account ($99/year). A free Apple ID can build
  for personal devices via Xcode automatic signing, but profiles
  expire in 7 days; the paid account is strongly recommended.
- Permission to enrol a test iPhone or iPad in the developer team.
- The Chanora source code (this repository), reachable from the
  Mac (git remote, scp, or AirDrop).

---

## 2. Toolchain versions pinned for this build

| Tool | Version | Why |
|---|---|---|
| macOS | 14 Sonoma or 15 Sequoia | Xcode 26 requires macOS 14+ at minimum |
| Xcode | **26 or newer** (DEC-021) | Required to upload to App Store Connect on or after 2026-04-28. Beta builds for internal devices also pin to 26+ for parity. |
| iOS SDK | **26 or newer** (DEC-021) | Bundled with Xcode 26+. |
| iOS deployment target | **iOS 13** (DEC-003) | Matches Flutter 3.41.9's baseline support. The Podfile and Runner.xcodeproj must set this. |
| Command Line Tools | Latest stable (`xcode-select --install` or via Xcode) | Provides `xcrun`, `xcodebuild`, `codesign`. |
| Flutter SDK | 3.41.9 stable | Same pin as Linux/Android/Windows builds. |
| Dart SDK | 3.11.5 | Bundled with Flutter. |
| Rust toolchain | stable 1.95 (or newer) | Workspace `rust-version = "1.95"`. |
| Rust targets | `aarch64-apple-ios`, `aarch64-apple-ios-sim`, `x86_64-apple-ios` | Device + Apple-silicon simulator + Intel-Mac simulator. |
| `cbindgen` (optional) | 0.27+ | Only if you wire a C header for the bridge; FRB does not require it. |
| `cargo-lipo` (legacy) | not required | `cargo-lipo` was deprecated for multi-arch iOS in favor of `cargo build --target <triple>` + `xcodebuild -create-xcframework`. |
| CocoaPods | 1.16+ | Required by Flutter iOS builds (Podfile in `apps/chanora_flutter/ios/`). |
| Opus library | bundled via `audiopus_sys` build script with iOS-specific CMake toolchain | Apple does not ship libopus; we cross-compile it per Rust target. |
| Apple Developer ID + certificate | one-time | Required to sign the .ipa. Free Apple ID OK for personal-device install (7-day profile). |

> **Why not Xcode 15 or 16?** Apple's App Store Connect upload gate
> (DEC-021) requires the iOS 26 SDK on or after 2026-04-28. Even for
> internal Beta where you don't upload, we pin Xcode 26+ so the same
> archive can be re-signed for TestFlight without re-building.

---

## 3. macOS host options

### 3.1 Owned Apple hardware (cheapest long-term)

| Hardware | Min spec | Notes |
|---|---|---|
| Mac mini (M2, 2023+) | 16 GiB RAM, 256 GiB SSD | Best price/perf; ~USD $799 new |
| MacBook Air / Pro (M2+) | 16 GiB RAM | Portable; ~USD $1099+ new |
| Used Intel Mac (2018+) | 16 GiB RAM | Will need macOS 14+; check Apple's compatibility list |

Apple's licence forbids running macOS on non-Apple hardware
("Hackintoshes") for any commercial purpose. Don't.

### 3.2 macOS rental (no hardware purchase)

| Provider | Cost | Notes |
|---|---|---|
| MacStadium | USD ~$79+/month for an M-series Mac mini | Dedicated host; supports Xcode + signing |
| MacinCloud | USD ~$30+/month "Pay-As-You-Go" or "Dedicated Server" plans | Shared options exist; choose Dedicated for full Xcode access |
| Scaleway Apple silicon (EU) | EUR ~0.13/hour for Mac mini M2 | Hourly billing; cheap for short builds |
| AWS EC2 Mac (`mac1.metal`, `mac2.metal`) | USD ~$1.08/hour with a 24h minimum allocation | Most expensive; use only if you need AWS integration |

> Apple's licence permits cloud hosts that comply with their terms.
> All of the above explicitly meet that requirement.

### 3.3 Borrowed Mac

Find a friend or coworker with a Mac that can run Xcode 26. Install
your dev certificate to their Keychain locally for the build session
only, then remove it. This is the cheapest option if you have access.

---

## 4. Install the toolchain (one-time per Mac)

All commands assume a normal user account; only the Xcode install
needs an Administrator password.

### 4.1 Xcode + Command Line Tools

Either:

```bash
# Easiest: install Xcode from the Mac App Store.
# (Or download the .xip from https://developer.apple.com/download/all/)
open "https://apps.apple.com/app/xcode/id497799835"

# Then accept the licence and install Command Line Tools.
sudo xcodebuild -license accept
xcode-select --install   # idempotent; skip if already done

# Verify.
xcodebuild -version       # expect Xcode 26.x
xcrun --version
xcrun simctl list runtimes | head
```

If you already have Xcode 16 or 17 installed, **add** Xcode 26
side-by-side (don't replace) and switch via `sudo xcode-select -s
/Applications/Xcode-26.app`.

### 4.2 Homebrew + Rust + Flutter + CocoaPods

```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Rust (managed by rustup, not brew)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
source "$HOME/.cargo/env"

# Add the three iOS targets.
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios

# Flutter — pinned to 3.41.9 stable to match the Linux / Android
# builds.
brew install --cask flutter
flutter --version | head -3
flutter precache --ios

# CocoaPods.
brew install cocoapods
pod --version  # expect 1.16 or newer
```

> If your Mac is Apple-silicon and Homebrew installs to
> `/opt/homebrew`, make sure that's on `PATH` for new shells:
> `eval "$(/opt/homebrew/bin/brew shellenv)"`.

### 4.3 Opus

`audiopus_sys` ships an Opus source tree and tries to build it via
CMake. On iOS the cmake-crate's generated build sometimes confuses
the iOS toolchain. We use the same pre-built libopus strategy that
the Android build uses:

```bash
# Get the audiopus_sys-bundled libopus source after a first cargo
# fetch (it gets unpacked into the registry).
cd ~/chanora
cargo fetch
OPUS_SRC=$(find ~/.cargo/registry/src -path '*audiopus_sys-*/opus' -type d | head -1)
mkdir -p /tmp/opus-ios
cp -R "$OPUS_SRC"/* /tmp/opus-ios/

# Helper: build libopus.a for one iOS arch.
build_opus_ios() {
    local ARCH=$1                  # arm64 | x86_64 | arm64-simulator
    local SDK=$2                   # iphoneos | iphonesimulator
    local OUT=/tmp/opus-ios-${ARCH}
    rm -rf /tmp/opus-ios/build && mkdir /tmp/opus-ios/build
    cd /tmp/opus-ios/build
    cmake .. \
        -DCMAKE_SYSTEM_NAME=iOS \
        -DCMAKE_OSX_SYSROOT=$(xcrun --sdk "$SDK" --show-sdk-path) \
        -DCMAKE_OSX_ARCHITECTURES="${ARCH%-simulator}" \
        -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
        -DBUILD_SHARED_LIBS=OFF \
        -DOPUS_BUILD_PROGRAMS=OFF \
        -DOPUS_BUILD_TESTING=OFF \
        -DCMAKE_INSTALL_PREFIX="$OUT" \
        -G "Unix Makefiles"
    make -j8 install
    # Wrap so LIBOPUS_LIB_DIR appends /lib correctly.
    mkdir -p "$OUT-wrap/lib"
    cp "$OUT/lib/libopus.a" "$OUT-wrap/lib/"
    cd /tmp/opus-ios
}

build_opus_ios arm64 iphoneos                # device
build_opus_ios arm64-simulator iphonesimulator   # Apple-silicon Mac sim
build_opus_ios x86_64 iphonesimulator        # Intel Mac sim
```

You should end up with three wrap dirs each containing `lib/libopus.a`:

```
/tmp/opus-ios-arm64-wrap/lib/libopus.a
/tmp/opus-ios-arm64-simulator-wrap/lib/libopus.a
/tmp/opus-ios-x86_64-wrap/lib/libopus.a
```

### 4.4 Cargo target linker config

iOS targets need explicit linker config to find the SDK. Create
`~/.cargo/config.toml`:

```toml
[target.aarch64-apple-ios]
rustflags = ["-C", "link-arg=-fembed-bitcode"]

[target.aarch64-apple-ios-sim]
rustflags = ["-C", "link-arg=-fembed-bitcode"]

[target.x86_64-apple-ios]
rustflags = ["-C", "link-arg=-fembed-bitcode"]
```

Rust's built-in iOS targets already drive the right linker via
`xcrun`, so usually no further config is needed. If you hit a
"-platform_version not supported" error, ensure Xcode CLI tools are
fresh: `sudo xcode-select --reset`.

### 4.5 (Optional) `flutter_rust_bridge_codegen`

Only needed if you modify `crates/chanora_bridge/src/api.rs` on the
Mac. The bindings shipped in the repo are platform-agnostic Dart +
Rust source and do **not** need regenerating per platform.

```bash
cargo install flutter_rust_bridge_codegen --version "=2.12.0"
```

---

## 5. Get the source onto the Mac

Same two options as the Windows doc:

### Option A — git remote

```bash
# On Linux dev host: push to a private remote.
cd /home/milkice/chanora
git remote add origin git@github.com:<your-user>/chanora.git
git push --all
git push --tags
```

```bash
# On the Mac:
cd ~
git clone https://github.com/<your-user>/chanora.git
cd chanora
git checkout v0.2.0-beta.1
```

### Option B — zip / AirDrop

```bash
# On Linux:
git -C /home/milkice/chanora archive --format=zip \
    --output=/tmp/chanora-v0.2.0-beta.1.zip v0.2.0-beta.1

# Transfer the zip to the Mac (AirDrop, scp, USB), then:
cd ~
unzip /Volumes/.../chanora-v0.2.0-beta.1.zip -d chanora
cd chanora
```

---

## 6. Add the iOS platform to the Flutter app

The product Flutter app was scaffolded with `--platforms=linux,android`,
so the `ios/` folder does not yet exist. Add it once on the Mac:

```bash
cd ~/chanora/apps/chanora_flutter
flutter create --platforms=ios .
```

This creates `apps/chanora_flutter/ios/` containing:

```
ios/
  Runner/
    AppDelegate.swift
    Info.plist
    Runner-Bridging-Header.h
    Assets.xcassets/
  Runner.xcodeproj/
  Runner.xcworkspace/
  Podfile
  Flutter/
```

> **Important:** Do **not** commit this `ios/` folder back to the
> repo from the Mac unless you also do the corresponding edits the
> source-of-truth host uses (icons, bundle ID, signing config).
> For Beta it is fine to keep it Mac-only.

### 6.1 Set the iOS deployment target

Edit `apps/chanora_flutter/ios/Podfile`'s first line to:

```ruby
platform :ios, '13.0'   # DEC-003
```

And in Xcode (`open Runner.xcworkspace`):

- Project → Runner → General → Minimum Deployments → iOS 13.0
- Project → Runner → Signing & Capabilities → set your Team

### 6.2 Add iOS-required entitlements + Info.plist keys

Chanora's audio engine needs the microphone. Edit
`apps/chanora_flutter/ios/Runner/Info.plist` to include:

```xml
<key>NSMicrophoneUsageDescription</key>
<string>Chanora uses the microphone for voice chat with TeamSpeak-compatible servers.</string>

<!-- Background-mode audio so playback continues when the screen
     locks. Optional for Beta; required for a real release. -->
<key>UIBackgroundModes</key>
<array>
  <string>audio</string>
</array>

<!-- Allow plain HTTP only for development. Remove before release.
     The protocol layer itself uses UDP, not HTTP, but Flutter's hot
     reload talks HTTP. -->
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
```

The bundle identifier should be set to a reverse-DNS string under
your developer team, e.g. `app.chanora.chanora_flutter`. Update it
in Xcode → Runner → General → Identity → Bundle Identifier.

---

## 7. Build the Rust libraries for iOS

We build three flavors and combine them into an XCFramework that
Xcode can link against from both device and simulator slices.

```bash
cd ~/chanora
export LIBOPUS_STATIC=1
export LIBOPUS_NO_PKG=1

build_bridge_for_ios() {
    local TARGET=$1            # aarch64-apple-ios | aarch64-apple-ios-sim | x86_64-apple-ios
    local OPUS_DIR=$2          # /tmp/opus-ios-*-wrap
    export LIBOPUS_LIB_DIR="$OPUS_DIR"

    # Wipe audiopus_sys build cache so LIBOPUS_LIB_DIR is re-read.
    rm -rf target/$TARGET/release/build/audiopus_sys-*

    cargo build --release --target "$TARGET" -p chanora_bridge
}

build_bridge_for_ios aarch64-apple-ios           /tmp/opus-ios-arm64-wrap
build_bridge_for_ios aarch64-apple-ios-sim       /tmp/opus-ios-arm64-simulator-wrap
build_bridge_for_ios x86_64-apple-ios            /tmp/opus-ios-x86_64-wrap
```

Expected static-lib outputs (the `[lib]` section in
`crates/chanora_bridge/Cargo.toml` already declares `staticlib`):

```
target/aarch64-apple-ios/release/libchanora_bridge.a       (~25 MiB, device)
target/aarch64-apple-ios-sim/release/libchanora_bridge.a   (~25 MiB, M-series simulator)
target/x86_64-apple-ios/release/libchanora_bridge.a        (~25 MiB, Intel simulator)
```

### 7.1 Build the simulator universal binary with `lipo`

The simulator side needs both arches in one `.a` for Xcode to pick
the host architecture automatically:

```bash
mkdir -p target/universal-ios-sim
lipo -create \
    target/aarch64-apple-ios-sim/release/libchanora_bridge.a \
    target/x86_64-apple-ios/release/libchanora_bridge.a \
    -output target/universal-ios-sim/libchanora_bridge.a
file target/universal-ios-sim/libchanora_bridge.a
```

### 7.2 Build the XCFramework

```bash
rm -rf target/ChanoraBridge.xcframework
xcodebuild -create-xcframework \
    -library target/aarch64-apple-ios/release/libchanora_bridge.a \
    -library target/universal-ios-sim/libchanora_bridge.a \
    -output target/ChanoraBridge.xcframework
```

Result:

```
target/ChanoraBridge.xcframework/
  Info.plist
  ios-arm64/libchanora_bridge.a
  ios-arm64_x86_64-simulator/libchanora_bridge.a
```

### 7.3 Link the XCFramework from the Flutter iOS app

Open `apps/chanora_flutter/ios/Runner.xcworkspace` in Xcode.

1. Drag `target/ChanoraBridge.xcframework` into the Runner project
   (Embed: "Do Not Embed" — it's a static lib).
2. Project → Runner → Build Phases → Link Binary with Libraries →
   ensure `ChanoraBridge.xcframework` is listed.
3. The FRB Dart loader expects to find the symbols in the main app
   binary on iOS (static linkage). The generated
   `apps/chanora_flutter/lib/src/rust/frb_generated.io.dart` should
   look up via `DynamicLibrary.process()` on iOS — this is the
   default for FRB v2 when the Rust crate type is `staticlib`.

If the dart-side loader expects a separate dylib, regenerate
bindings with the iOS target hint:

```bash
flutter_rust_bridge_codegen generate
```

(The repo's bindings already target this style; no change usually
needed.)

---

## 8. Regenerate the FRB bindings (optional)

Skip unless you've modified `crates/chanora_bridge/src/api.rs` on
the Mac. The bindings are platform-agnostic Dart + Rust source.

```bash
cd ~/chanora
flutter_rust_bridge_codegen generate
```

Takes ~5–10 minutes on first run (cargo expand).

---

## 9. Build the iOS app via Flutter

```bash
cd ~/chanora/apps/chanora_flutter
flutter pub get
cd ios && pod install && cd ..
flutter build ios --release --no-codesign
```

`flutter build ios --no-codesign` produces an unsigned `Runner.app`.
For signed `.ipa` for TestFlight or device install, use:

```bash
flutter build ipa --release --export-method development
# or: --export-method ad-hoc | enterprise | app-store
```

This requires:

- A configured Team and Bundle Identifier (set in Xcode in §6.1).
- A matching provisioning profile in your Keychain (Xcode's
  Automatic Signing usually handles this if you've signed into
  your Apple ID in Xcode → Settings → Accounts).

Expected output locations:

| Variant | Path |
|---|---|
| Unsigned app bundle | `apps/chanora_flutter/build/ios/iphoneos/Runner.app/` |
| Signed `.ipa` | `apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa` |

---

## 10. Install on a physical iPhone/iPad

### 10.1 Wired install (USB or USB-C)

```bash
# List paired devices.
xcrun devicectl list devices

# Install the ipa.
xcrun devicectl device install app --device <UDID> \
    apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa

# Launch it.
xcrun devicectl device process launch \
    --device <UDID> --terminate-existing \
    app.chanora.chanora_flutter
```

### 10.2 TestFlight (wireless to many testers)

```bash
xcrun altool --upload-app \
    --type ios \
    --file apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa \
    --username "<your-apple-id>" \
    --password "<app-specific-password>"
```

Then in App Store Connect:

1. Wait for processing (10–30 minutes).
2. Add the build to a TestFlight test group.
3. Invite testers by Apple ID email.

Testers install via the TestFlight app on their device.

---

## 11. Smoke test on the iPhone/iPad

Open the installed Chanora app:

1. **Banner**: "Beta build — voice in/out wired; not production
   ready."
2. **Form**: pre-populated with `cn.teamspeak.app` + `ChanoraBeta`.
3. **Connect**.

> **Known limitation (same as Android):** the protocol layer's
> `hickory-resolver` cannot resolve hostnames on iOS by default. If
> Connect times out, enter the literal IP. For `cn.teamspeak.app`
> that is currently `175.178.125.23`. A proper fix wires the iOS
> system resolver into hickory; Beta+ work.

4. Once connected, the channel tree appears.
5. **Start audio** → grant microphone permission when prompted.
6. **Hold to talk** → frame counter increments under PTT.

If audio fails to start, check:

- Microphone permission granted in Settings → Chanora → Microphone.
- Device not in silent mode (output side only).
- iOS Audio Session category — by default Flutter uses
  `AVAudioSessionCategoryPlayback`. Voice transmission needs
  `PlayAndRecord` with `voiceChat` mode. **Beta+ work.**

---

## 12. Package the release artefact

`flutter build ipa` already produces a single `.ipa`. Stamp it with
the build identifier:

```bash
cd ~/chanora/apps/chanora_flutter
cp build/ios/ipa/chanora_flutter.ipa \
   "$HOME/chanora-v0.2.0-beta.1-ios.ipa"
shasum -a 256 "$HOME/chanora-v0.2.0-beta.1-ios.ipa"
```

Typical size: 60–90 MiB (includes Flutter engine + Dart AOT +
Rust static lib + bundled libopus + iOS resources).

---

## 13. Download to your local host

If on a rental Mac:

```bash
# From the Mac, copy back to Linux dev host.
scp "$HOME/chanora-v0.2.0-beta.1-ios.ipa" \
    user@your-linux-host:/home/milkice/
```

Or pull from the rental's web file manager. Keep the `.ipa`
re-installable indefinitely if the provisioning profile is
ad-hoc/enterprise; for development profiles it expires in 7 days.

---

## 14. Final artefact paths (what you actually produce)

On the Mac, after a successful build:

| Artefact | Path |
|---|---|
| Rust device static lib | `~/chanora/target/aarch64-apple-ios/release/libchanora_bridge.a` |
| Rust Apple-silicon sim lib | `~/chanora/target/aarch64-apple-ios-sim/release/libchanora_bridge.a` |
| Rust Intel sim lib | `~/chanora/target/x86_64-apple-ios/release/libchanora_bridge.a` |
| Lipo'd simulator lib | `~/chanora/target/universal-ios-sim/libchanora_bridge.a` |
| XCFramework | `~/chanora/target/ChanoraBridge.xcframework/` |
| Unsigned app bundle | `~/chanora/apps/chanora_flutter/build/ios/iphoneos/Runner.app/` |
| Signed `.ipa` | `~/chanora/apps/chanora_flutter/build/ios/ipa/chanora_flutter.ipa` |
| Renamed release `.ipa` | `~/chanora-v0.2.0-beta.1-ios.ipa` |

---

## 15. After the build — deallocate (rental hosts)

| Provider | Deallocate command |
|---|---|
| MacStadium | Suspend / shut down via control panel |
| MacinCloud | "Stop" the session in account dashboard |
| Scaleway | `scw instance server stop <id>` |
| AWS EC2 Mac | Stop the host; release the dedicated host if at end of 24h minimum |

Always verify the billing dashboard shows the instance as stopped,
not just shut down at the OS level.

---

## 16. Known issues and caveats

| Issue | Workaround |
|---|---|
| `audiopus_sys` cmake-build fails with "no member named '__int128_t'" | Re-export `LIBOPUS_LIB_DIR` to a pre-built `.a` directory (§4.3); never let cargo's cmake-build attempt to compile Opus on iOS. |
| `hickory-resolver` cannot resolve hostnames on iOS | Use literal IP. Fix: wire `dns-via-system` resolver into `chanora_protocol`. Beta+ work. |
| Microphone permission prompt never appears | The `NSMicrophoneUsageDescription` is missing from Info.plist. See §6.2. |
| Audio plays but no transmission | iOS AVAudioSession category likely set to `Playback` not `PlayAndRecord`. Configure the session in Swift before Flutter loads, or via a small plugin. Beta+ work. |
| Code signing fails with "No signing certificate" | Open Xcode → Settings → Accounts → add your Apple ID, then close and reopen `Runner.xcworkspace` to refresh. |
| `flutter build ipa` complains about missing entitlements | Make sure the provisioning profile in Apple Developer console includes the App ID with microphone capability enabled. |
| TestFlight upload rejected: "Invalid bundle. Apple silicon support" | Build with `flutter build ipa --release` (not `--debug`); debug AOT is JIT-only and not allowed in TestFlight. |
| App crashes on launch with `dyld: Library not loaded: libchanora_bridge.dylib` | The bridge is built as `staticlib`, not `cdylib`, on iOS — FRB's loader uses `DynamicLibrary.process()`. If you accidentally built a `cdylib`, the loader path won't match. Stick to staticlib. |
| First `flutter build ios` takes > 30 minutes | Normal — first Pod install + CocoaPods cache. |

---

## 17. Reproducibility note

Like the Windows build, the iOS build is **not bit-reproducible**:

- `xcodebuild` embeds a creation timestamp in the `.ipa`.
- `audiopus_sys` picks the cmake-discovered Xcode toolchain version.
- Dart AOT compilation embeds a Dart kernel hash.
- Code-signing inherently makes every signed `.ipa` unique.

If true reproducibility is required for a future External Beta or
MVP Public release, that is Beta+ work; the build above is
sufficient for an Internal Beta milestone per DEC-001.

---

## 18. Change History

| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial iOS build instructions for Chanora `v0.2.0-beta.1`. Targets a macOS 14+ host running Xcode 26+ (DEC-021), Rust 1.95 stable with `aarch64-apple-ios` + `aarch64-apple-ios-sim` + `x86_64-apple-ios` targets, Flutter 3.41.9 stable, `flutter_rust_bridge` 2.12.0. Produces `Runner.app` + `chanora_flutter.ipa` via `flutter build ipa --release`. iOS deployment target pinned to 13.0 (DEC-003). Surface caveat: DEC-011.1 iOS audio remains Deferred — `cpal`'s iOS backend has not been empirically verified, and AVAudioSession category configuration is required for voice transmission. |
