The Apple CoreML Silero VAD backend resolves six @_cdecl Swift symbols
via dlsym(RTLD_DEFAULT) at runtime in the Rust audio crate. Local
flutter build paths preserved those symbols, but Xcode Archive (the
path used for TestFlight and App Store uploads) silently stripped them
through two independent mechanisms, causing Rust to fall back to
WebRTC VAD on every shipped build.
Both stripping mechanisms are now neutralised:
* ld dead-strip: OTHER_LDFLAGS now whitelists each of the six
chanora_silero_vad_* symbols via repeated `-Xlinker -exported_symbol`
pairs in ios/Flutter/{Release,Debug}.xcconfig and
macos/Flutter/Flutter-{Release,Debug}.xcconfig.
* install-time strip: STRIP_STYLE is set to `non-global` in the same
four xcconfigs so the post-link strip phase no longer drops exported
global text symbols from the Archive product. Cost: ~264 bytes per
binary; verified `xcrun strip` vs `xcrun strip -x` behaviour.
Self-test wired into both AppDelegates: at launch on a utility queue,
ChanoraSileroSelfTest resolves all six symbols through dlsym (the same
path the Rust runtime uses, not a direct call that would mask the bug
class) and exercises create → reset → process → destroy. Result is
logged via NSLog and surfaces in Console.app / idevicesyslog.
A post-link verify_silero_exports.sh build phase runs nm -gU on the
final Archive binary and fails the build if any of the six symbols are
missing. Empirically caught the original Archive regression that
flutter build --no-codesign did not.
CocoaPods bridge podspecs now emit a proper .dSYM via dsymutil so
TestFlight crash reports are symbolicated; Cargo.toml release profile
sets `debug = true` because dsymutil needs DWARF in the input dylib.
macOS chanora_bridge.podspec PATH inserts /opt/homebrew/opt/rustup/bin
ahead of /opt/homebrew/bin so rustup's cargo (which has the
x86_64-apple-darwin target installed) wins over the homebrew rust
formula that is aarch64-only.
iOS Podfile target renamed from `Runner` to `Chanora` to match the
Xcode target name shipped in the project (the workspace and scheme
already referenced Chanora; the Podfile mismatch produced lint
warnings during `pod install`).
ITSAppUsesNonExemptEncryption=false declared in both Info.plist files
so TestFlight and App Store Connect uploads skip the export-compliance
prompt; Chanora uses only platform-provided TLS.
.gitignore now covers Xcode archive bundles, IPA exports, dSYM
directories, the local macOS release zip, and agent/tooling state
directories so generated TestFlight artifacts no longer appear in
git status.
End-to-end verified by headless archive:
xcodebuild -workspace Runner.xcworkspace -scheme Runner \
-configuration Release -destination 'generic/platform=iOS' \
-archivePath /tmp/chanora.xcarchive archive CODE_SIGNING_ALLOWED=NO
nm -gU on the resulting .app/Chanora binary shows all six
chanora_silero_vad_* symbols present.
109 lines
4.7 KiB
TOML
109 lines
4.7 KiB
TOML
# Chanora — top-level Cargo workspace.
|
|
#
|
|
# Layout authority: DEC-022 in
|
|
# docs/governance/product-decision-register.md, which adopts the
|
|
# README's sketch as canonical:
|
|
#
|
|
# apps/chanora_flutter/ — Flutter application (separate toolchain)
|
|
# core/chanora_core/ — top-level Rust API + orchestration
|
|
# crates/chanora_protocol/ — tsclientlib isolation
|
|
# crates/chanora_resolver/ — TeamSpeak address resolution
|
|
# crates/chanora_state/ — snapshot, deltas, reducers
|
|
# crates/chanora_audio/ — capture, DSP, Opus, jitter, mixer
|
|
# crates/chanora_storage/ — bookmarks, settings, identity refs
|
|
# crates/chanora_diagnostics/ — logs, redaction, export
|
|
# crates/chanora_prefetch — server-resolution prefetch cache/policy
|
|
# crates/chanora_bridge/ — Flutter/Rust typed DTOs + glue
|
|
#
|
|
# The Flutter app is not part of this Cargo workspace; it is owned by
|
|
# Gradle / Xcode / Flutter tooling under apps/chanora_flutter/.
|
|
#
|
|
# Proof-of-concept spikes are archived separately in the private
|
|
# chanoraapp/chanora-poc repository. Promotion of PoC findings into
|
|
# product code happens crate-by-crate with explicit audit-trail commits.
|
|
|
|
[workspace]
|
|
resolver = "2"
|
|
members = [
|
|
"core/chanora_core",
|
|
"crates/chanora_protocol",
|
|
"crates/chanora_state",
|
|
"crates/chanora_audio",
|
|
"crates/chanora_storage",
|
|
"crates/chanora_diagnostics",
|
|
"crates/chanora_prefetch",
|
|
"crates/chanora_bridge",
|
|
"crates/chanora_resolver",
|
|
]
|
|
|
|
exclude = [
|
|
"apps/chanora_flutter",
|
|
]
|
|
|
|
[workspace.package]
|
|
version = "0.2.0-beta.1"
|
|
edition = "2021"
|
|
rust-version = "1.95"
|
|
authors = ["The Chanora Project Contributors"]
|
|
license = "MIT OR Apache-2.0" # DEC-020
|
|
repository = "https://github.com/anomalyco/opencode"
|
|
publish = false # not on crates.io
|
|
readme = "README.md"
|
|
|
|
# Shared dependency pinnings. Members refer to these via
|
|
# `package.dependencies.<x>.workspace = true` so versions stay aligned.
|
|
[workspace.dependencies]
|
|
thiserror = "2"
|
|
tracing = "0.1"
|
|
serde = { version = "1", features = ["derive"] }
|
|
|
|
# DEC-032 exit step: override the `cmake` crate with the fork carrying
|
|
# cmake-rs PR #257 (https://github.com/rust-lang/cmake-rs/pull/257).
|
|
# The patch forwards `ANDROID_ABI` and `ANDROID_PLATFORM` environment
|
|
# variables (set per-invocation by cargo-ndk >= 4.x and reinforced by
|
|
# our SDD-118 item 5 cleanEnv map) to the child `cmake` invocation as
|
|
# `-D` variables, so audiopus_sys's libopus CMake build picks up the
|
|
# correct per-ABI target on multi-ABI Android builds. Without it the
|
|
# child cmake falls through to the NDK toolchain file's default and
|
|
# armeabi-v7a / x86_64 fail to compile.
|
|
#
|
|
# Pinned by exact commit SHA (the `android-build` branch tip on
|
|
# pr2502/cmake-rs as of 2026-05-18) so the patch is reproducible.
|
|
# Re-evaluate and remove once PR #257 merges and a fresh `cmake`
|
|
# release lands on crates.io (current upstream is 0.7.2).
|
|
# Patch tsproto-types to left-pad P-256 coordinates that are shorter than
|
|
# 32 bytes. BigInt::to_bytes_be() strips leading zero bytes; when a
|
|
# coordinate happens to start with 0x00 (~0.8 % probability per coordinate)
|
|
# the upstream code rejects it with WrongPublicKeyLength, breaking the
|
|
# init-server handshake. The fix zero-pads to the field size instead.
|
|
# Fork: https://github.com/EdisonJwa/tsclientlib/tree/fix/p256-short-coordinate-pad
|
|
[patch."https://github.com/ReSpeak/tsclientlib.git"]
|
|
tsproto-types = { git = "https://github.com/EdisonJwa/tsclientlib.git", branch = "fix/p256-short-coordinate-pad" }
|
|
|
|
[patch.crates-io]
|
|
cmake = { git = "https://github.com/pr2502/cmake-rs", rev = "bdad5edc569d82151922c5c6c4685b1563f12aa1" }
|
|
|
|
# Emit DWARF debug info in release builds.
|
|
#
|
|
# Apple's archive validator (and App Store Connect upload) require a
|
|
# matching `.dSYM` for every embedded framework, keyed by the framework
|
|
# binary's LC_UUID. The CocoaPods bridge podspecs copy the cargo-built
|
|
# `libchanora_bridge.dylib` into `chanora_bridge.framework/chanora_bridge`
|
|
# and then run `dsymutil` on it (see apps/chanora_flutter/ios and
|
|
# apps/chanora_flutter/macos podspecs). `dsymutil` needs DWARF in the
|
|
# input binary to produce a useful dSYM with symbol records; without
|
|
# `debug = true` here we'd ship a stripped binary and an empty/unusable
|
|
# dSYM and TestFlight crash reports would be unsymbolicated.
|
|
#
|
|
# `split-debuginfo = "off"` keeps DWARF inside the dylib so install_name_tool
|
|
# rewriting and the dsymutil pass both work without chasing per-object
|
|
# `.o` files cargo would otherwise scatter under target/<triple>/release/deps.
|
|
#
|
|
# `strip = false` ensures cargo does not pre-strip the dylib before
|
|
# dsymutil sees it. We strip the in-framework binary ourselves after the
|
|
# dSYM is produced (handled in the podspec script phases).
|
|
[profile.release]
|
|
debug = true
|
|
split-debuginfo = "off"
|
|
strip = false
|