docs(p0): compact MVP spec for Android Oboe focus

This commit is contained in:
Edison Jwa
2026-05-20 14:52:33 +09:00
parent 8c253f1d4d
commit 7d6d56e330
63 changed files with 8000 additions and 46507 deletions
-664
View File
@@ -1,664 +0,0 @@
# 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 ~510 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 (1030 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: 6090 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. |
-83
View File
@@ -1,83 +0,0 @@
# Rel Platform Release Policy
**Document type:** Release / Platform Release Policy
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Repo path:** `docs/release/platform-release-policy.md` ---
## 1. Purpose
This document separates runtime deployment targets from app-store upload build-SDK gates.
## 2. iOS / iPadOS Policy
| Policy item | Decision |
|---|---|
| Runtime deployment target | iOS 13 or later unless Flutter, plugin, audio, or product constraints require raising it. |
| App Store Connect upload build-SDK gate | For upload on or after 2026-04-28, use Xcode 26 or later and the iOS 26 / iPadOS 26 SDK or later, unless Apple publishes a newer applicable requirement before upload. |
| Internal Alpha / local development | May use development toolchains suitable for internal testing if no App Store Connect upload is performed. |
| TestFlight / App Store upload | Must satisfy the active Apple App Store Connect upload requirement. |
## 3. Android Policy
| Policy item | Decision |
|---|---|
| Runtime minimum | Android API 24 or later unless Flutter, plugin, audio, or product constraints require raising it. |
| Store upload target API | Target the Google Play-required API level on the upload date. |
| Internal Alpha / sideload testing | May use internal build configuration suitable for internal testing, but release readiness must state whether Google Play rules apply. |
## 4. Release Readiness Rule
The Go/No-Go record shall record:
- runtime minimum versions;
- store upload build/toolchain gates;
- build number;
- commit SHA;
- tag;
- artifact hashes;
- platform-specific release status;
- whether the release is internal-only or store-uploaded.
## 5. Traceability
| Policy | Traceability |
|---|---|
| iOS runtime minimum | SysRS-286 -> SysDes-133 -> SRS-185 -> SAD-061 -> SDD-071 |
| Apple App Store Connect upload SDK gate | SysRS-287 -> SysDes-134 -> SRS-186 -> SAD-062 -> SDD-072 |
| Android runtime and target API policy | SysRS-288/289 -> SysDes-135 -> SRS-187/188 -> SAD-063 -> SDD-073 |
## 6. Change History
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Initial platform release policy separating runtime targets from store upload build-SDK gates. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Desktop Push-to-Talk Release Policy Addendum (Baseline Candidate 0.9.3)
Per SysDes-148 the release readiness record shall carry, for every desktop release artefact, the following per-platform evidence:
| Field | Source | Acceptance |
|---|---|---|
| Detected `PttCapabilityLevel` | Live runtime measurement on the verification host. | Must equal the level the release notes claim. |
| Active backend identifier | `DesktopPttBackend::backend_id()` reported by the live measurement. | Must equal the backend the release notes claim. |
| Focused fallback exercised | Manual verification step. | Must record "yes" with a re-measurement of the same backend identifier after the user revokes the relevant permission / blocks the relevant input path. |
| Capability badge UI | Screenshot or platform-test trace. | Must match the runtime capability. |
Release notes shall not claim Global PTT support on a platform when the live measurement returned `L0Focused`. Release notes shall not claim mouse-side-button support on a platform when the live measurement did not include a side-button binding (per DEC-026 the Linux portal may not expose side-button bindings on every session).
Per DEC-025 the only officially-tested Linux environment for the first public release is GNOME on Wayland. Other Linux environments are supported at the `L0Focused` capability only; the release notes shall not claim Global PTT support outside the tested compositor.
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT release-policy evidence requirements: per-platform capability level + backend identifier + fallback-exercised + capability badge UI screenshot must be recorded before release notes may claim Global PTT support. |
-51
View File
@@ -1,51 +0,0 @@
# Release Artifact Policy
**Document type:** Release / Artifact Policy
**Version:** 0.1.0
**Status:** Draft
**Language:** English
**Product:** Chanora
**Repo path:** `docs/release/release-artifact-policy.md`
---
## 1. Purpose
This document defines the expected release artifact metadata before a release is approved.
## 2. Required Artifact Metadata
| Field | Required |
|---|---|
| Release version | Yes |
| Build number | Yes |
| Git commit SHA | Yes |
| Git tag | Yes |
| Build timestamp | Yes |
| Build environment | Yes |
| Platform | Yes |
| Artifact filename | Yes |
| SHA-256 hash | Yes |
| Signing status | Required for public/store release |
| Notarization status | Required for macOS public release |
| Store upload status | Required for store release |
## 3. Artifact Types
| Platform | Expected artifact |
|---|---|
| Windows | Installer or packaged app artifact |
| macOS | Signed/notarized app bundle or installer |
| Linux | AppImage, Flatpak, deb, rpm, or selected package |
| Android | AAB/APK |
| iOS / iPadOS | App Store Connect/TestFlight upload artifact |
## 4. Rule
Release artifacts shall not be treated as approved until the Release Readiness / Go-No-Go record is completed.
## 5. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial release artifact policy. |
@@ -1,234 +0,0 @@
# CHANORA_REL_Release_Readiness_Go_NoGo_Record_v0.9.2.2.1
**Document type:** Release Readiness Checklist / Go-No-Go Record
**Version:** 0.9.2
**Status:** Baseline Candidate
**Language:** English
**Product:** Chanora
**Repo path:** `docs/release/release-readiness-go-nogo-record.md` ---
## 1. Purpose
This document records the auditable release readiness decision for a Chanora release.
Design documents alone do not authorize release. A release requires an explicit readiness decision based on scope, build identity, requirement completion, verification evidence, security review, platform readiness, legal/privacy readiness, known risks, and approval.
## 2. Release Identity
| Field | Value |
|---|---|
| Release name | TBD by Product Owner |
| Release type | TBD: Internal Alpha / External Beta / MVP Public / Store Release |
| Release version | TBD |
| Release candidate ID | TBD |
| Release date target | TBD |
| Release owner | TBD |
| Product owner | TBD |
| Engineering owner | TBD |
| QA / verification owner | TBD |
| Security reviewer | TBD |
| Legal / compliance reviewer | TBD |
## 3. Build Identity
| Field | Value |
|---|---|
| Git repository | TBD |
| Git branch | TBD |
| Git commit SHA | TBD |
| Git tag | TBD |
| iOS App Store Connect upload SDK gate | Xcode 26+ and iOS 26 / iPadOS 26 SDK+ for upload on or after 2026-04-28 |
| Platform release policy included | Yes |
| Build number | TBD |
| CI pipeline ID | TBD |
| Build timestamp | TBD |
| Windows artifact | TBD |
| macOS artifact | TBD |
| Linux artifact | TBD |
| Android artifact | TBD |
| iOS artifact | TBD |
| Artifact hash method | SHA-256 unless otherwise specified |
| Artifact hashes | TBD |
## 4. Scope Readiness
| Question | Answer | Evidence | Owner |
|---|---|---|---|
| Is this release scope defined? | TBD | Release scope statement | Product Owner |
| Is this release Internal Alpha, External Beta, MVP Public, or Store Release? | TBD | Release scope statement | Product Owner |
| Are included features listed? | TBD | Release notes / scope list | Product Owner |
| Are excluded/deferred features listed? | TBD | Deferred requirements list | Product Owner |
| Are target platforms listed? | TBD | Platform readiness table | Engineering Owner |
| Are known limitations documented? | TBD | Known issue register | Product Owner / QA |
## 5. Requirements Readiness
| Requirement group | Status | Evidence | Deferred items / waiver |
|---|---|---|---|
| P0 / MVP connection requirements | TBD | SRS/SWE.6 evidence | TBD |
| P0 / MVP channel and state requirements | TBD | SRS/SWE.6 evidence | TBD |
| P0 / MVP voice requirements | TBD | SRS/SWE.6 evidence | TBD |
| Audio processing requirements | TBD | SWE.4/SWE.5/SWE.6 evidence | TBD |
| Storage and secure storage requirements | TBD | SWE.4/SWE.5/SWE.6 evidence | TBD |
| Diagnostics and redaction requirements | TBD | SWE.4/SWE.5/SWE.6 evidence | TBD |
| Material 3 / UI requirements | TBD | SWE.6 evidence | TBD |
| Accessibility requirements | TBD | SWE.6 evidence | TBD |
| Platform behavior requirements | TBD | SYS.4/SWE.6 evidence | TBD |
| i18n / Unicode requirements | TBD | SWE.4/SWE.5/SWE.6 evidence | TBD |
| Traceability requirements | TBD | Validation report | TBD |
## 6. Verification Readiness
| Verification layer | Required evidence | Status | Failed items | Waivers |
|---|---|---|---|---|
| SWE.4 Unit Verification | Unit verification summary report | TBD | TBD | TBD |
| SWE.5 Software Integration Verification | Integration verification summary report | TBD | TBD | TBD |
| SWE.6 Software Verification | Software verification summary report | TBD | TBD | TBD |
| SYS.4 System Integration Verification | System integration verification summary report | TBD | TBD | TBD |
| Regression Verification | Regression report | TBD | TBD | TBD |
| Manual exploratory test | Test notes | TBD | TBD | TBD |
## 7. Security Readiness
| Question | Required answer | Status | Evidence |
|---|---|---|---|
| Is secure storage verified for supported platforms? | Yes / waived | TBD | Security test report |
| Are secrets excluded from plaintext logs? | Yes / waived | TBD | Redaction test result |
| Is diagnostic export redaction verified? | Yes / waived | TBD | Diagnostics audit |
| Is dependency/license scan completed? | Yes / waived | TBD | Dependency scan report |
| Are high/critical dependency issues resolved or waived? | Yes / waived | TBD | Security waiver record |
| Are user-facing errors safe and non-sensitive? | Yes / waived | TBD | Review record |
| Are platform permissions justified? | Yes / waived | TBD | Permission review |
## 8. Platform Readiness
| Platform | Release status | Build artifact | Verification status | Known blockers | Owner |
|---|---|---|---|---|---|
| Windows | TBD: Go / Conditional Go / No-Go / Not in scope | TBD | TBD | TBD | TBD |
| macOS | TBD: Go / Conditional Go / No-Go / Not in scope | TBD | TBD | TBD | TBD |
| Linux | TBD: Go / Conditional Go / No-Go / Not in scope | TBD | TBD | TBD | TBD |
| Android | TBD: Go / Conditional Go / No-Go / Not in scope | TBD | TBD | TBD | TBD |
| iOS | TBD: Go / Conditional Go / No-Go / Not in scope | TBD | TBD, including Apple App Store SDK gate | TBD | TBD |
## 9. Legal, OSS, and Privacy Readiness
| Question | Required answer | Status | Evidence | Owner |
|---|---|---|---|---|
| Is the app clearly identified as unofficial and not affiliated with TeamSpeak? | Yes | TBD | App copy / legal notice | Legal |
| Is the TeamSpeak trademark/non-affiliation wording reviewed? | Yes | TBD | Legal review record | Legal |
| Is OSS license review completed? | Yes | TBD | OSS notice / license report | Legal / Engineering |
| Are Rust, Flutter, tsclientlib, and platform dependencies included in OSS review? | Yes | TBD | OSS license report | Legal / Engineering |
| Is privacy policy completed for the release scope? | Yes | TBD | Privacy policy URL/file | Legal |
| Are diagnostics/logging disclosures complete? | Yes | TBD | Privacy policy / in-app notice | Legal / Product |
| Are App Store / Play Store metadata requirements complete if applicable? | Yes / N/A | TBD | Store metadata review | Product / Legal |
## 10. Known Issues and Waivers
| Issue ID | Description | Severity | Impact | Waiver? | Waiver owner | Expiry / follow-up |
|---|---|---|---|---|---|---|
| TBD | TBD | TBD | TBD | TBD | TBD | TBD |
## 10A. Required Security, Privacy, and Legal Evidence
| Evidence document | Required status before External Beta / Public release |
|---|---|
| `CHANORA_SEC_Threat_Model_v0.9.2.2.1.md` | Reviewed; v1.0 approved before public release |
| `CHANORA_SEC_Secure_Storage_Audit_Report_v0.9.2.2.1.md` | Completed for release-scope platforms |
| `CHANORA_SEC_Diagnostic_Redaction_Audit_Report_v0.9.2.2.1.md` | Completed and approved |
| `CHANORA_SEC_Dependency_And_Supply_Chain_Report_v0.9.2.2.1.md` | Completed with no unapproved critical/high risk |
| `CHANORA_PRIV_Privacy_Policy_v0.9.2.2.1.md` | Legal/privacy reviewed; v1.0 approved before public release |
| `CHANORA_LEGAL_Trademark_And_Attribution_Review_v0.9.2.2.1.md` | Legal reviewed; v1.0 approved before public release |
## 10B. Key Product Decision Gate
| Decision | Required status before Go |
|---|---|
| Release type | Confirmed |
| Release platform scope | Confirmed |
| Minimum iOS version | Confirmed |
| Apple App Store SDK gate | Confirmed and release-inspected |
| Minimum Android version | Confirmed |
| Android target SDK policy | Confirmed |
| Multiple active connections in MVP | Confirmed |
| AEC/AGC/NS/HPF default states | Confirmed |
| Audio processing implementation path | Confirmed |
| Legal/trademark/licensing review requirement | Confirmed |
| Local database choice | Confirmed |
| Flutter/Rust bridge choice | Confirmed |
| Diagnostics upload policy | Confirmed |
| Crash reporting policy | Confirmed |
| Product license model | Confirmed or explicitly not required for release scope |
## 11. Release Decision
| Decision | Meaning |
|---|---|
| Go | Release is approved for the stated scope and platforms. |
| Conditional Go | Release is approved only if listed conditions are satisfied. |
| No-Go | Release is not approved. |
**Decision:** TBD: Go / Conditional Go / No-Go
## 12. Conditional Go Conditions
| Condition ID | Condition | Owner | Due date | Evidence required |
|---|---|---|---|---|
| TBD | TBD | TBD | TBD | TBD |
## 13. Approval
| Role | Name | Decision | Date | Evidence / Signature |
|---|---|---|---|---|
| Product Owner | TBD | TBD | TBD | TBD |
| Engineering Owner | TBD | TBD | TBD | TBD |
| QA / Verification Owner | TBD | TBD | TBD | TBD |
| Security Reviewer | TBD | TBD | TBD | TBD |
| Legal / Compliance Reviewer | TBD | TBD | TBD | TBD |
| Release Manager | TBD | TBD | TBD | TBD |
## 14. Change History
| Version | Date | Description |
|---|---|---|
| 0.9.0 | 2026-05-14 | Initial release readiness and Go/No-Go record template. |
## Baseline Candidate 0.9.1 Update
| Version | Date | Description |
|---|---|---|
| 0.9.1 | 2026-05-14 | Updated baseline after product decision closure: Apple App Store SDK gate uses Xcode 26+ and iOS 26 / iPadOS 26 SDK+ since 2026-04-28, platform baselines and decision traceability propagated across the document set. |
## Baseline Candidate 0.9.2 Update
| Version | Date | Description |
|---|---|---|
| 0.9.2 | 2026-05-14 | Corrected Apple App Store Connect upload gate to 2026-04-28 and checked full-package naming, references, and coverage. |
## Desktop Push-to-Talk Release Readiness Addendum (Baseline Candidate 0.9.3)
The release readiness checklist for every desktop release artefact gains the following items per SysDes-148, SysRS-298, and DEC-023 / DEC-024 / DEC-025 / DEC-026 / DEC-027 / DEC-028.
| Item | Owner | Evidence required | Status |
|---|---|---|---|
| RR-PTT-001 Windows Global PTT verified on a Windows reference host. | Windows Platform Owner | Live measurement of `PttCapabilityLevel` + `backend_id` returned at runtime. Backend identifier shall be `raw-input` (preferred) or `low-level-hook` (fallback) for Global. | Open |
| RR-PTT-002 macOS Global PTT verified with permission granted on a macOS reference host. | macOS Platform Owner | Live measurement + `permission_state = Granted` reported through the Event-Tap backend; UI capability badge screenshot. | Open |
| RR-PTT-003 macOS Focused PTT fallback verified with permission denied. | macOS Platform Owner | Live measurement of `PttCapabilityLevel::L0Focused` after revoking Input Monitoring; UI capability badge screenshot showing the fallback notice. | Open |
| RR-PTT-004 Linux Global PTT verified on GNOME-on-Wayland. | Linux Platform Owner | Live measurement returning `gnome-wayland-portal` backend identifier from a live GNOME-on-Wayland host; portal binding dialog screenshot. | **Implemented** (live `CreateSession` + `BindShortcuts` + signal subscription landed in code; awaiting live trace from a GNOME-on-Wayland reference host before the cell can be marked Done). |
| RR-PTT-005 Linux Focused fallback verified on a non-tested compositor (any of: X11, sway, KDE) | Linux Platform Owner | Live measurement of `L0Focused` on at least one non-tested compositor; release notes do not claim Global support on the untested environment. | Open |
| RR-PTT-006 Diagnostic export carries no key data. | Privacy Reviewer | Inspection of a user-initiated diagnostic export captured while PTT is bound to a real key; export shall contain `capability_level`, `backend_id`, `bound_input_class` and shall not contain a recognisable key code. | Open |
| RR-PTT-007 Missed-key-up watchdog timeout demonstrated. | Audio Owner | Test trace showing `transmit_active` clearing after the configured 30 s ceiling when the watchdog forces a release. | **Done (v1.0.0-rc.4)** — covered by `chanora_audio::ptt::tests::watchdog_clears_transmit_after_timeout` (and the negative `watchdog_does_not_clear_on_normal_release`). Live platform trace still required per RR-PTT-001..005. |
| RR-PTT-008 Capability badge matches runtime capability on every supported platform. | UX Owner | UI screenshot or platform-test trace. | Open |
A release decision shall be **No-Go** for any platform whose RR-PTT items are not all closed.
| Version | Date | Description |
|---|---|---|
| 0.9.3 | 2026-05-15 | Added desktop PTT release-readiness items RR-PTT-001 through RR-PTT-008 covering Windows / macOS / Linux Global verification, permission-denied fallback verification, diagnostic-export privacy inspection, missed-key-up watchdog test, and capability-badge UI verification. |
| 0.9.4 | 2026-05-15 | RR-PTT-007 (missed-key-up watchdog) flipped to Done — the cross-platform `chanora_audio::ptt::MissedKeyUpWatchdog` ships in v1.0.0-rc.4 with two passing unit tests. Live per-platform traces (RR-PTT-001..005, RR-PTT-008) remain required for the live verification phase but are no longer blocked on engineering. |
| 0.9.5 | 2026-05-15 | RR-PTT-004 status flipped to Implemented (live trace from a GNOME-on-Wayland reference host pending). The Linux backend now runs the full portal `CreateSession` + `BindShortcuts` + `Activated` / `Deactivated` flow on a dedicated tokio task per backend instance. |
-514
View File
@@ -1,514 +0,0 @@
# Building Chanora v0.2.0-beta.1 on Windows via Azure VM
**Document type:** Release / Platform Build Instructions
**Version:** 0.1.0
**Status:** Draft
**Language:** English
**Product:** Chanora
**Repo path:** `docs/release/windows-build.md`
---
## 1. Purpose
This document gives exact step-by-step instructions for building the
Chanora `v0.2.0-beta.1` Internal Beta on a Windows host, using an
Azure Virtual Machine. It exists because the development host is
Linux; `flutter build windows` requires a Windows host with Visual
Studio 2022 and cannot be cross-compiled.
After following this doc you will have:
- `chanora_flutter.exe` — the Windows desktop binary.
- `chanora_bridge.dll` — the Rust cdylib loaded by the .exe via FFI.
- A complete release bundle directory the app needs at runtime.
The doc assumes you have:
- An Azure subscription with permission to create VMs.
- An RDP client (Windows: built-in; macOS: Microsoft Remote Desktop;
Linux: `remmina` or `rdesktop`).
- The Chanora source code locally (this repository).
---
## 2. Toolchain versions pinned for this build
| Tool | Version | Why |
|---|---|---|
| Windows Server | 2022 Datacenter | LTS, plenty of Azure images, ships PowerShell 5.1 + Server Core APIs |
| Visual Studio 2022 | Build Tools (Community works too) | Required by `flutter build windows`; provides MSVC, Windows SDK, CMake, link.exe |
| Visual Studio workload | "Desktop development with C++" | Provides MSVC v143, Windows 10/11 SDK, CMake tools, C++ ATL |
| Flutter SDK | 3.41.9 stable | Matches the Linux/Beta build; bridge codegen is version-sensitive |
| Dart SDK | 3.11.5 | Comes with Flutter 3.41.9 |
| Rust toolchain | stable 1.95 (or newer) | Workspace `rust-version = "1.95"` |
| Rust target | `x86_64-pc-windows-msvc` | Native Windows ABI; produces MSVC-compatible .dll |
| `cargo-ndk` | N/A | Not needed for Windows; only Android needs it |
| Opus library | bundled via `audiopus_sys` build script + cmake | On Windows there is no system libopus by default; we build it from source |
| Git for Windows | latest | Convenient `git` + the bundled Bash for running `flutter` if needed |
| 7-Zip (optional) | latest | Convenient for unpacking the source bundle if you uploaded it as zip |
---
## 3. Azure VM provisioning
### 3.1 Recommended SKU
| Setting | Value | Rationale |
|---|---|---|
| Image | `Windows Server 2022 Datacenter — x64 Gen2` | Most common LTS Windows image |
| Size | `Standard_D4s_v5` (4 vCPU, 16 GiB) or larger | The Cargo cold build pulls hundreds of crates; 4 cores keeps the build under 30 minutes |
| OS disk | Premium SSD 128 GiB | Default 128 GiB is enough for VS 2022 (~10 GiB), Flutter SDK (~2 GiB), Rust toolchain (~1 GiB), source + build artefacts (~15 GiB) |
| Auth | Password (you'll RDP in) | Sufficient for a short-lived build VM |
| Inbound port rules | Allow **RDP (3389)** only from your IP | Lock RDP to your IP via NSG; never open to 0.0.0.0/0 |
| Networking | New VNet + public IP | Standard for an ad-hoc build host |
| Auto-shutdown | Enable, 19:00 local time | Stops you forgetting and burning hours |
### 3.2 Estimated cost
`Standard_D4s_v5` in West US 2 is about **USD $0.20/hour** for the
compute plus ~USD $0.01/hour for the disk. A first cold build
(provision → install everything → compile → archive) is typically
23 hours. Total: under **USD $1** per build session if you
deallocate after.
> **Cost gotcha:** *Stopped (deallocated)* VMs do not bill for
> compute; *Stopped* (in the OS shutdown sense, but not deallocated
> in Azure) still bills compute. Always check the Azure portal shows
> the VM as **Stopped (deallocated)**.
### 3.3 Provisioning checklist
1. Azure Portal → **Create a resource****Windows Server 2022 Datacenter (x64 Gen2)**.
2. Choose region near you.
3. Size: `Standard_D4s_v5`.
4. Administrator account: pick a username (e.g. `chanora`) + a strong
password.
5. Inbound ports: RDP only, **restricted to your public IP**.
6. Disks: keep Premium SSD 128 GiB.
7. Networking: defaults; ensure auto-assign public IP.
8. Management → Auto-shutdown: **On**, time = end of your work day,
notification = email.
9. Review + create.
### 3.4 First RDP login
1. Get the VM's public IP from the Azure portal.
2. RDP in as `chanora` / your password.
3. (Optional) Open Server Manager → Local Server → IE Enhanced
Security Configuration → turn **Off** for Administrators so the
downloads in §4 don't get blocked.
---
## 4. Install the toolchain (one-time per VM)
Run all of these in an **elevated PowerShell** (right-click PowerShell → Run as Administrator).
### 4.1 Visual Studio 2022 Build Tools + workload
```powershell
# Download the VS 2022 Build Tools bootstrapper
Invoke-WebRequest -Uri 'https://aka.ms/vs/17/release/vs_buildtools.exe' `
-OutFile "$env:TEMP\vs_buildtools.exe"
# Install with the C++ desktop workload (this is what Flutter requires).
# --quiet suppresses UI; --wait blocks until done; takes 10-15 minutes.
& "$env:TEMP\vs_buildtools.exe" --quiet --wait --norestart `
--add Microsoft.VisualStudio.Workload.VCTools `
--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
--add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
--add Microsoft.VisualStudio.Component.VC.CMake.Project `
--add Microsoft.VisualStudio.Component.VC.ATL `
--includeRecommended
```
When this completes, **reboot the VM** (Visual Studio Build Tools
needs it for environment variables).
### 4.2 Git for Windows
```powershell
winget install --id Git.Git --silent --accept-source-agreements --accept-package-agreements
```
### 4.3 Rust (stable, with the MSVC target)
```powershell
# Download rustup-init for Windows.
Invoke-WebRequest -Uri 'https://win.rustup.rs/x86_64' `
-OutFile "$env:TEMP\rustup-init.exe"
# Install the default toolchain (stable-x86_64-pc-windows-msvc).
& "$env:TEMP\rustup-init.exe" -y --default-toolchain stable --profile minimal
# Open a NEW PowerShell so PATH picks up cargo.
# Then verify:
cargo --version
rustc --version
```
Expected output: `cargo 1.95.0` (or newer), `rustc 1.95.0`. The
default target on Windows is `x86_64-pc-windows-msvc` — this is what
we want.
### 4.4 Flutter SDK (pinned to 3.41.9 stable)
```powershell
# Download Flutter SDK
$flutter = 'https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.41.9-stable.zip'
Invoke-WebRequest -Uri $flutter -OutFile "$env:TEMP\flutter.zip"
Expand-Archive "$env:TEMP\flutter.zip" -DestinationPath 'C:\'
# Add to PATH for the current user (persists across sessions).
[Environment]::SetEnvironmentVariable(
'Path',
[Environment]::GetEnvironmentVariable('Path', 'User') + ';C:\flutter\bin',
'User'
)
# Open a NEW PowerShell so PATH applies, then:
flutter --version
flutter doctor
```
`flutter doctor` should show green on:
- ✓ Flutter (Channel stable, 3.41.9, on Microsoft Windows …)
- ✓ Windows Version (Windows … 10.0.20348 or later)
- ✓ Visual Studio - develop Windows apps (Visual Studio Build Tools 2022 …)
Yellow on Android Studio / Chrome is fine — we're only building
Windows.
### 4.5 `flutter_rust_bridge_codegen` and Dart deps
```powershell
cargo install flutter_rust_bridge_codegen --version "=2.12.0"
```
Takes ~5 minutes (compiles `flutter_rust_bridge_codegen` from source).
### 4.6 Opus build dependency
The `audiopus` Rust crate wraps the C Opus library. On Windows there
is no system Opus, so the `audiopus_sys` build script will attempt to
build Opus from source via CMake (which the VS workload already
provided). No extra action is required — but verify CMake is on
PATH:
```powershell
cmake --version
```
Expected: `cmake version 3.x` (any 3.x ≥ 3.18 is fine).
If CMake is not found, install it explicitly:
```powershell
winget install --id Kitware.CMake --silent
```
---
## 5. Get the source onto the VM
Two options. Pick whichever is easier for you.
### Option A — Push the repo to a temporary git remote
On the Linux host where the repo lives:
```bash
# E.g. create a private GitHub repo and push:
git remote add origin https://github.com/<your-user>/chanora.git
git push --all
git push --tags
```
Then on the Windows VM:
```powershell
cd C:\
git clone https://github.com/<your-user>/chanora.git
cd chanora
git checkout v0.2.0-beta.1
```
### Option B — Upload the source as a zip via RDP clipboard
On Linux:
```bash
cd /home/milkice
git -C chanora archive --format=zip --output=/tmp/chanora-v0.2.0-beta.1.zip v0.2.0-beta.1
```
Copy `/tmp/chanora-v0.2.0-beta.1.zip` into the RDP session
(clipboard or shared drive), unzip into `C:\chanora\`.
Either way, you end up with the working tree at `C:\chanora\`,
checked out at the `v0.2.0-beta.1` tag.
---
## 6. Add the Windows platform to the Flutter app
The product Flutter app was created with `--platforms=linux,android`,
so the `windows/` platform folder does not yet exist. Add it once on
the build VM:
```powershell
cd C:\chanora\apps\chanora_flutter
flutter create --platforms=windows .
```
This creates `apps/chanora_flutter/windows/` containing:
```
windows/
CMakeLists.txt
flutter/
CMakeLists.txt
generated_plugin_registrant.cc
...
runner/
main.cpp
Runner.rc
chanora_flutter.exe.manifest
...
```
> **Important:** Do **not** commit this `windows/` folder back to the
> repo from the VM unless you also do the corresponding edits the
> source-of-truth host uses (icon, manifest, etc.). For Beta it is
> fine to keep it VM-only.
---
## 7. Build the Rust cdylib for Windows
```powershell
cd C:\chanora
cargo build --release -p chanora_bridge
```
This builds the workspace, including:
- The whole `tsclientlib` git dependency tree (slow first time —
expect ~510 minutes for the first compile of `audiopus_sys` +
`tsproto` etc.).
- `chanora_bridge` as a `cdylib` + `staticlib` + `rlib`.
Expected output location:
```
C:\chanora\target\release\chanora_bridge.dll
```
(Note: no `lib` prefix on Windows MSVC.)
Verify:
```powershell
Get-Item C:\chanora\target\release\chanora_bridge.dll | Format-List Name, Length, LastWriteTime
```
The DLL is typically ~1618 MiB.
---
## 8. Regenerate the FRB bindings (optional — only if you changed the bridge API)
The Linux build already produced `lib/src/rust/*.dart` and
`crates/chanora_bridge/src/frb_generated.rs`. The bindings are
platform-agnostic Dart + Rust source, so **they do not need
regenerating on Windows**. Skip §8 unless you modified
`crates/chanora_bridge/src/api.rs` after `v0.2.0-beta.1`.
If you do need to regenerate:
```powershell
cd C:\chanora
flutter_rust_bridge_codegen generate
```
Takes ~510 minutes the first time on Windows.
---
## 9. Build the Flutter Windows binary
```powershell
cd C:\chanora\apps\chanora_flutter
flutter pub get
flutter build windows --release
```
`flutter build windows`:
1. Invokes CMake on `apps/chanora_flutter/windows/`.
2. Builds `chanora_flutter.exe` via MSVC.
3. Bundles the Flutter engine DLL (`flutter_windows.dll`), ICU data,
Dart-AOT-compiled `app.so` (delivered as `data\app.so`), the
Flutter `data\` directory, and platform-specific resources.
Build time: ~35 minutes on `Standard_D4s_v5` once dependencies are
cached.
Expected output location:
```
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\
```
Contents:
| File | Size (approx) | Notes |
|---|---|---|
| `chanora_flutter.exe` | ~10 MiB | The launcher .exe |
| `flutter_windows.dll` | ~17 MiB | Flutter engine |
| `data\icudtl.dat` | ~10 MiB | Unicode tables |
| `data\app.so` | varies | AOT-compiled Dart code |
| `data\flutter_assets\` | varies | Images, fonts, ARB-derived .json |
---
## 10. Add the Rust DLL to the bundle
The Flutter app loads `chanora_bridge.dll` at runtime via `dart:ffi`.
Drop the DLL next to the .exe (the same directory):
```powershell
$src = 'C:\chanora\target\release\chanora_bridge.dll'
$dst = 'C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\'
Copy-Item $src $dst -Force
```
Verify everything is in place:
```powershell
Get-ChildItem $dst -Recurse | Select-Object FullName, Length |
Format-Table -AutoSize
```
You should see:
```
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_flutter.exe
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_bridge.dll
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\flutter_windows.dll
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\app.so
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\icudtl.dat
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\flutter_assets\...
```
---
## 11. Smoke test on the VM
Double-click `chanora_flutter.exe` (or `& '...\chanora_flutter.exe'`
in PowerShell). The Beta UI should appear:
- Banner: "Beta build — voice in/out wired; not production ready."
- Form: `cn.teamspeak.app` + `ChanoraBeta` pre-populated.
- Connect button.
Press **Connect** → after a few seconds you should see the channel
tree of `cn.teamspeak.app` (Vigorous Pro). Press **Start audio**
PTT button appears. Hold it to transmit (a Windows mic permission
prompt may appear).
> **Network gotcha:** Some Azure regions block UDP 9987 outbound by
> default. If Connect times out, check the VM's NSG (outbound rules)
> and the regional firewall.
---
## 12. Package the release artefact
```powershell
$src = 'C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release'
$out = 'C:\chanora-v0.2.0-beta.1-windows-x64.zip'
Compress-Archive -Path "$src\*" -DestinationPath $out -Force
Get-Item $out | Format-List Name, Length
```
Expected size: ~5080 MiB.
---
## 13. Download to your local host
From your local machine (Linux/macOS/Windows):
```bash
# Use Azure CLI to copy via SAS, or just RDP the file out.
# Simplest: in the RDP session enable clipboard file transfer
# (mstsc /control or Remmina's "share local folder"), then drag the
# zip out.
```
Or use Azure Blob Storage if you want versioned hosting.
---
## 14. Final artefact paths (what you actually produce)
Inside the VM, after a successful build:
| Artefact | Path on the VM |
|---|---|
| Bridge DLL | `C:\chanora\target\release\chanora_bridge.dll` |
| Flutter EXE | `C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_flutter.exe` |
| Complete runtime bundle | `C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\` |
| Release zip | `C:\chanora-v0.2.0-beta.1-windows-x64.zip` |
---
## 15. After the build — deallocate the VM
```powershell
# In the Azure CLI on your local host, OR via the portal:
az vm deallocate --resource-group <rg> --name <vm-name>
```
Verify the portal says **Stopped (deallocated)**.
---
## 16. Known issues and caveats
| Issue | Workaround |
|---|---|
| `audiopus_sys` build fails with "cmake not found" | Re-run §4.6's `winget install Kitware.CMake` and open a new PowerShell. |
| `flutter build windows` complains about Long Paths | `Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Value 1`, then reboot. |
| Microsoft Defender quarantines `chanora_bridge.dll` | Add `C:\chanora\target\release\` to Defender exclusions before the cargo build. |
| The build VM cannot reach `cn.teamspeak.app` on UDP 9987 | Confirm Azure regional firewall allows outbound UDP. The Linux dev host's verification doesn't translate to Azure VMs automatically. |
| First `cargo build` takes > 30 minutes | Normal. Subsequent builds reuse `target/` and are ~1 minute. |
| `flutter pub get` fails behind a corporate proxy | Set `HTTPS_PROXY` and `HTTP_PROXY` environment variables before running. |
| The `windows/` folder created by `flutter create` is missing icons | Replace `windows\runner\resources\app_icon.ico` with a Chanora icon. The default Flutter icon ships otherwise. Cosmetic only for Beta. |
---
## 17. Reproducibility note
This build process is **not bit-reproducible** on Windows in the
current state:
- The `audiopus_sys` build script picks the CMake-discovered MSVC
toolchain, which embeds its version into the DLL.
- Dart AOT compilation embeds a Dart kernel hash.
- Windows resource compilation embeds a build timestamp.
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 Windows build instructions for Chanora `v0.2.0-beta.1`. Targets Azure VM `Standard_D4s_v5` running Windows Server 2022, Visual Studio 2022 Build Tools with the C++ desktop workload, Rust 1.95 stable (`x86_64-pc-windows-msvc`), Flutter 3.41.9 stable, `flutter_rust_bridge` 2.12.0. Produces `chanora_flutter.exe` + `chanora_bridge.dll` packaged into `chanora-v0.2.0-beta.1-windows-x64.zip`. |