- ios/Runner.xcodeproj/project.pbxproj: Update RunnerTests TEST_HOST
paths from Runner.app/Runner to Chanora.app/Chanora (target was
renamed in prior commit but test config still pointed at old paths,
breaking xcodebuild test).
- Cargo.toml: Move release DWARF flags from workspace [profile.release]
into Apple-only podspec CARGO_PROFILE_RELEASE_* env vars so Android,
Linux, Windows release builds stay lean (~10MB DWARF avoided).
- ios/Runner/Info.plist + macos/Runner/Info.plist: Flip
ITSAppUsesNonExemptEncryption from false to true (Chanora ships
ChaCha20-Poly1305 local storage + tsclientlib ECDH/AES-EAX voice
channel encryption, not exempt under Apple export-compliance rules).
- scripts/verify_silero_exports.sh: Make slice-aware via lipo -archs
loop + per-arch nm -arch invocation so universal macOS builds
verify every architecture slice, not just whichever slice nm picks.
- .gitignore: Drop .omo/ and .playwright-mcp/ entries (scope leak;
unrelated tooling state, not part of PR #26 archive-symbol concern).
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.
The macOS chanora_bridge.framework tree at
apps/chanora_flutter/macos/Frameworks/chanora_bridge.framework was
being tracked in git despite being a pure build artifact. Both
mechanisms in chanora_bridge.podspec rebuild the entire tree from
scratch:
* prepare_command (runs on `pod install`) — `rm -rf $FW` and
reconstructs Versions/A, the Versions/Current and Resources
symlinks, Info.plist, and copies the lipo-merged universal dylib.
* script_phase :before_compile (runs on every Xcode build) — same
rm -rf + reconstruction, gated on freshness of the cargo output.
Tracking the tree therefore added zero value and ~36 MB per binary
revision (the chanora_bridge dylib alone). The iOS counterpart at
apps/chanora_flutter/ios/Frameworks/ has been correctly ignored since
.gitignore:118-119 was added; this commit mirrors that rule for macOS.
Changes:
- git rm --cached -r the 5 tracked entries (binary, 3 symlinks,
Info.plist). Working tree is untouched, so existing local
builds keep functioning until the next `pod install` /
Xcode build refreshes them.
- Add /apps/chanora_flutter/macos/Frameworks/ to .gitignore
alongside the existing iOS entry, with a comment pointing at the
podspec mechanism so the next maintainer understands the rule.
Verified the working tree binary survives the cache untrack and
the path is now matched by .gitignore:125.
Canonical baseline lives at crates/chanora_audio/benches/baselines/x86_64-unknown-linux-gnu.json
and is updated only via the bench-baseline-update.yml workflow.
Debug and Profile had ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO which
blocked outbound TCP. All three configs now have:
- ENABLE_HARDENED_RUNTIME = YES
- ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES
This was the root cause of the 'Operation not permitted' connection
failure on macOS Sequoia.
iOS rejects loose .dylib loads (`dlopen` of any path outside the
app bundle is sandboxed), so a Flutter-Rust bridge has to ship
inside the .app as an Embed-and-Sign framework that
flutter_rust_bridge's runtime loader can dlopen via its default
`chanora_bridge.framework/chanora_bridge` lookup path.
This commit wires the bridge into the iOS build via a CocoaPods
podspec. Same role Cargokit plays for other Flutter+Rust setups,
done by hand against this repo's layout to avoid the Cargokit
vendoring footprint that was previously dropped.
apps/chanora_flutter/ios/chanora_bridge.podspec
New file. `prepare_command` invokes
`cargo build --release --target aarch64-apple-ios -p chanora_bridge`
with IPHONEOS_DEPLOYMENT_TARGET=13.0 +
CMAKE_POLICY_VERSION_MINIMUM=3.5 (satisfies audiopus_sys's
cmake invocation on modern CMake 4.x), then wraps the
produced libchanora_bridge.dylib into
chanora_bridge.framework with an Info.plist that declares
iPhoneOS / MinimumOSVersion=13.0, and rewrites LC_ID_DYLIB
to @rpath/chanora_bridge.framework/chanora_bridge.
`vendored_frameworks` exposes the result to CocoaPods, which
integrates it into Runner.xcodeproj with Embed & Sign
automatically. No Xcode UI edits required.
apps/chanora_flutter/ios/Podfile
Add `pod 'chanora_bridge', :path => '.'` to the Runner
target.
apps/chanora_flutter/ios/Podfile.lock
Generated by `pod install` after the pod was added. Pins
chanora_bridge 1.0.0 + checksum so iOS builds on other
developer machines pull the same framework version.
.gitignore
Add `/apps/chanora_flutter/ios/Frameworks/` so the
~13 MB built framework (regenerated on every pod install) is
not committed.
Verified end-to-end on the M1 Mac (coder@100.118.130.73):
pod install ok
chanora_bridge.framework generated at
apps/chanora_flutter/ios/Frameworks/chanora_bridge.framework
Install name: @rpath/chanora_bridge.framework/chanora_bridge
flutter build ios --release --no-codesign 12.7 s
-> Runner.app 29.9 MB (was 16.9 MB without the bridge)
-> Runner.app/Frameworks/chanora_bridge.framework present
alongside Flutter.framework, App.framework,
connectivity_plus.framework, objective_c.framework.
DEC-025: iOS / iPad officially in scope for P0.
Notes for owners installing on physical iOS devices:
The Personal Apple Team in Xcode requires a unique
PRODUCT_BUNDLE_IDENTIFIER (the default 'app.chanora.chanoraFlutter'
may already be claimed in the App Store registry). Set this
locally in Xcode -> Runner target -> Signing & Capabilities ->
Bundle Identifier (e.g. yourname.chanora.chanoraFlutter); do
NOT commit that change back since it is user-specific.
Applies the gen2 desktop-PTT review summary
(`gen2/chanora-desktop-ptt-review-summary-v0.9.2.md`) to our doc set
with the owner rulings PTT-OPEN-001 through PTT-OPEN-006 resolved as
accepted decisions DEC-023 through DEC-028:
* DEC-023 Windows Global PTT P0 / MVP
* DEC-024 macOS Global PTT P0 / MVP with permission UX
* DEC-025 Linux officially-tested env: GNOME on Wayland only
* DEC-026 Mouse side buttons supported (Win + macOS; Linux portal)
* DEC-027 PTT diagnostics: capability + availability only, no
raw key codes ever
* DEC-028 Missed-key-up watchdog: P0
Requirements (SysRS / SRS) and architecture (SysDes / SAD / SDD)
gain the desktop-PTT ID set the gen2 summary describes:
SysRS-296..302 -> SysDes-142..148
-> SRS-195..203
-> SAD-071..079
-> SDD-081..092
ID totals advance from 295 / 141 / 194 / 70 / 80 to 302 / 148 / 203
/ 79 / 92. The strict layered sourcing rule (`SRS -> SysDes` only,
`SAD -> SRS` only, `SDD -> SAD` only) is preserved; the
`tools/validate_docs.py` validator reports zero undefined refs and
zero direct-layer-rule violations.
New document:
* `docs/architecture/desktop-ptt-architecture.md` — capability
ladder (L0Focused, L1GlobalShortcut, L2GlobalHoldToTalk,
L3GlobalWithMouseButtons, L4DeviceAware reserved), Windows /
macOS / Linux strategies, privacy rule, audio-gate rule,
missed-key-up watchdog, release-readiness evidence requirement,
traceability summary.
Doc addenda (Baseline Candidate 0.9.3):
* `privacy/privacy-policy.md` — no raw key history, capability-
dependent Global PTT, UI reflects actual runtime capability
* `security/threat-model.md` — THREAT-PTT-001..006
* `security/diagnostic-redaction-audit-report.md` —
REDACT-PTT-001..006 banned field list enforced by `PttSanitizer`
* `release/platform-release-policy.md` — per-platform evidence
fields, no over-claim on untested Linux compositors
* `release/release-readiness-go-nogo-record.md` — RR-PTT-001..008
release-readiness items
* `verification/swe4-unit-verification-plan.md` —
SWE4-UV-035..039
* `verification/swe5-software-integration-verification-plan.md` —
SWE5-IV-015
* `verification/swe6-software-verification-plan.md` — SWE6-SV-017
* `verification/sys4-system-integration-verification-plan.md` —
SYS4-SIV-016
* `governance/traceability-matrix.md` — full PTT trace rows +
verification map
* `governance/decision-impact-assessment.md` — DEC-023..028
impact matrix
* `governance/product-decision-register.md` v0.9.9 entry
recording DEC-023..028 in the decision table and the status
table at §7
* `governance/document-index.md` — adds
`desktop-ptt-architecture.md` to the controlled set
* `architecture/proof-of-concept-plan.md` —
PoC-PTT-001..005 platform items
* `references/external-references.md` — Windows Raw Input,
macOS event-tap, Linux GlobalShortcuts portal references
* Both validation reports
(`baseline-candidate-validation-report.md`,
`repo-format-validation-report.md`) bumped to v0.9.3 with the
new ID totals (302 / 148 / 203 / 79 / 92).
README §"Desktop Push-to-Talk" added between Architecture Overview
and Repository Layout: capability levels, per-platform strategy,
privacy posture, missed-key-up watchdog.
Tooling:
* `tools/validate_docs.py` copied from the gen2 zip into the
repo tree (was previously available only inside the zip).
Reports zero undefined refs, zero direct-layer-rule violations,
English-only CJK check passes. The 35 "old package-style
filename" hits are pre-existing and identical to the gen2
baseline (they live in `path-migration-map.md` and config-ID
headers of governance docs and are intentional per the path
migration policy).
* `.gitignore` adds `/gen2/` so the externally-provided review
package does not enter the repo.
No code changes in this commit; B (the implementation split into
`transmit_active` / `capture_active`, `PttCapabilityLevel`
reporting, `PttSanitizer` diagnostics rule, and the UI capability
badge) follows in a separate commit.
Adds .gradle/, local.properties, and **/jniLibs/**/*.so to the
ignore list. The jniLibs .so files (600 KB - 950 KB per ABI) are
produced by the cargoBuildRust Gradle task wrapping cargo-ndk;
they are regenerated on every build and must not be tracked.
Required by the next commit (the Android audio spike) which
otherwise would try to track those four prebuilt libraries.
Imports the v0.9.2 documentation baseline and the bootstrap files
required by docs/governance/repository-bootstrap-plan.md v0.1.0 §3,
minus the justfile (added in the next commit).
This commit establishes the git history for the project. All previous
work lived only as filesystem state with no version control.