Files
chanora/CHANGELOG.md
T

21 KiB

Changelog

All notable changes to Chanora will be documented in this file.

This project is expected to follow a Conventional Commits style workflow.

[Unreleased]

Added

  • Apple CoreML-backed Silero VAD is now the preferred Apple voice activity detector when the private sibling silero-coreml SwiftPM package is available; WebRTC VAD remains the runtime fallback.

Changed

  • Apple platform floors are raised to iOS 16 and macOS 13 while the CoreML VAD package is linked.

[v1.0.0-rc.1] — MVP Public release candidate

This is the first release candidate for the MVP public release per DEC-001 sequencing. The remaining work before v1.0.0 is DEC-012 legal sign-off (see docs/governance/legal-review-readiness.md) and the staged platform promotions in docs/governance/staged-release-plan.md.

Added

  • Platform keyring backing for the per-install Data Encryption Key. chanora_storage::IdentityFileStore now stores the 32-byte DEK in the OS keyring (Linux Secret Service via D-Bus, macOS Keychain, Windows Credential Manager, iOS Keychain) when one is available, and transparently falls back to the v0.4 file-fallback at <storage_dir>/identity.dek otherwise. Existing file-fallback installs are migrated into the keyring on first run when the bus is reachable; the on-disk DEK copy is removed once the keyring acknowledges the write.
  • CHANORA_DISABLE_KEYRING=1 environment override forces the file-fallback path. Used by tests and headless / CI hosts where a real keyring call would prompt the user or block on a missing D-Bus session.
  • chanora_storage::Crypto trait + IdentityFileStore::crypto() give callers an envelope encrypt / decrypt pair anchored on the per-install DEK. Used by BookmarkRepository::with_crypto.
  • Encrypted server passwords for bookmarks. New password_blob BLOB column (schema v2). When the bookmark repository is constructed with a Crypto handle, server passwords are ChaCha20-Poly1305-encrypted under the same per-install DEK as the identity. Legacy password TEXT rows are still read for backward compatibility and lifted into password_blob on the next update(). chanora_core::ChanoraSession::init_storage wires this automatically.
  • SessionEvent::SnapshotChanged now triggers on any tree mutation, not just count changes. The watchdog probe hashes channels (id + parent + order + name) and clients (id + channel
    • name); any change to those bits — including in-channel client moves and channel renames — emits the event.
  • In-app About dialog with the public product name (DEC-018), the non-affiliation statement (DEC-019), the dual-license declaration (DEC-020), and a pointer to the NOTICE file for third-party attribution. New AppBar info icon opens the dialog.
  • docs/governance/legal-review-readiness.md — the engineering-side handoff package for the DEC-012 legal / trademark / licensing review.
  • docs/governance/staged-release-plan.md — the staging schedule for DEC-002's five-platform MVP target. Linux + Android sideload promote to GA on DEC-012 sign-off; Windows / macOS / iOS gate on per-platform build host availability.

Changed

  • chanora_storage test count rises from 8 to 10: encrypted bookmark password round-trip and legacy-plaintext-bookmark upgrade scenarios.
  • chanora_core test count rises from 5 to 7: signature detects in-channel move; signature is stable under input-vector reordering.
  • NOTICE refreshed for the MVP product-code dependency set (chacha20poly1305, rand, zeroize, keyring, connectivity_plus, path_provider, freezed_annotation, the removed linux-keyutils / hound, etc.). The PoC-era enumeration is preserved upstream in git history.
  • chanora_protocol::lib now also re-exports ChannelId and ClientId for downstream signature / hashing helpers.

Fixed

  • The watchdog signature blind spot from v0.4: same-count snapshots no longer suppress SnapshotChanged.

Security

  • DEK now sits behind the OS session lock on every supported desktop platform; the v0.4 two-file weakness is closed for those installs. Android, iOS, and other platforms without keyring reach fall back to the v0.4 file model; RISK-PoC-002 remains open for them.

Notes (MVP scope honesty)

  • DEC-012 legal review has not been performed. v1.0.0-rc.1 is not the public release; it is the candidate that the review signs off on (or rejects) before v1.0.0 is tagged.
  • Crash reporting is intentionally disabled (DEC-017). Repository grep for sentry|crashlytics|bugsnag returns zero hits in the MVP product code.
  • Automatic diagnostic upload remains forbidden (DEC-016). The user-initiated export path is the only way logs leave the device.
  • Android Keystore-backed DEK and iOS-side AVAudioSession.Mode.voiceChat engagement are deferred to v1.1.
  • iOS, Windows, and macOS release binaries are not built into v1.0.0-rc.1. The release page ships Linux x86_64 + Android arm64-v8a only; the other three platforms are source-buildable and promote per staged-release-plan.md.

[v0.4.0-beta.2] — External Beta

Server password input, channel join via tap, self mute (mic + speaker), master output gain slider, SQLite bookmarks with save / connect / delete, ChaCha20-Poly1305-encrypted identity at rest with identity.dek file, Android MODE_IN_COMMUNICATION routing, default log filter trims tsproto::resend chatter, GitHub Actions CI on every push.

[v0.3.0-beta.1] — Internal Beta (polish)

A.1 cross-platform DNS, A.2 identity persistence, A.3 redacted diagnostic export, A.4 SnapshotChanged event, A.5 mobile voice- preset flag, A.6 reconnect supervisor with watchdog + UI banner, A.6.1 OS connectivity signal drives reconnect timing.

[v0.2.0-beta.1] — Internal Beta first build

  • Voice in/out wired end-to-end through the Flutter UI. Per DEC-001 this reaches the Internal Beta milestone. Build hash: see the v0.2.0-beta.1 git tag.
  • crates/chanora_audio/ promoted from scaffold to a working engine:
    • cpal-based capture (mic gain, linear resampling to 48 kHz, mono down-mix) and playback (48 kHz stereo, requested config).
    • audiopus::Encoder for Opus VoIP encoding (20 ms / 960-sample mono frames).
    • tsclientlib::audio::AudioHandler for the decode + per-client jitter buffer + mix on the playback side.
    • Push-to-talk gate: encoder is bypassed entirely when PTT is off, so no spurious silence frames leak out.
    • Graceful playback-only fallback: if the host has no usable mic (typical for headless CI / users who deny the mic permission), capture logs a warning and the engine continues with output only. AudioEngine::capture_active() exposes this for the UI.
    • Live counters: frames_sent / frames_received / ptt().
  • crates/chanora_protocol/ extended with voice channels:
    • ProtocolClient::voice_out() returns a clonable mpsc::Sender<OutPacket> for outbound frames.
    • ProtocolClient::take_voice_in() returns a one-shot mpsc::Receiver<InboundVoice> of decoded S2C / S2CWhisper packets, with the originating from_client ID extracted.
    • Re-exports the few tsproto_packets::packets types (OutAudio, OutPacket, InAudioBuf, AudioData, CodecType, Direction) that chanora_audio legitimately needs. This is the only deliberate cross-crate type re-export; per SAD-067 the audio path is performance-sensitive and a parallel type hierarchy would force a copy per 20 ms frame.
    • Connection task interleaves outbound voice (drained first per loop iteration), event pumping, and control-request handling.
  • core/chanora_core::ChanoraSession audio API:
    • start_audio(AudioEngineConfig) — starts the engine attached to the active connection. Idempotent.
    • set_ptt(bool) — toggles transmission. No-op without an engine.
    • audio_stats()(frames_sent, frames_received, ptt_active).
    • disconnect() now stops the engine before disconnecting the protocol task.
  • crates/chanora_bridge/ audio surface:
    • start_audio(), set_ptt(active), audio_stats() Dart-callable commands.
    • BridgeAudioStats { frames_sent, frames_received, ptt_active } DTO.
    • Mapped CoreError::AudioNotStarted and CoreError::Audio(_) arms in BridgeError::From<CoreError>.
  • apps/chanora_flutter/:
    • Beta UI rewrite of main.dart: "Start audio" button after connect; hold-to-talk button with pressed/released visual state; live audio-stats line below the PTT (TX … frames • RX … frames • PTT on/off).
    • ARB key set expanded with startAudioAction, pttHoldToTalk, pttTransmitting, audioStatsLine in both en and zh-Hans.
    • test/beta_e2e_test.dart exercises the full Dart → FRB → chanora_bridge → chanora_core → chanora_audio path against cn.teamspeak.app. Verifies connect, audio start, PTT toggle, disconnect.
  • flutter_rust_bridge.yaml now sets local: true so the codegen resolves the workspace member's library name correctly. Without this, the generated Dart side fell back to libUNKNOWN.so and failed to load the cdylib.

Changed

  • flutter_rust_bridge.yaml: added local: true.
  • chanora_bridge::api: BridgeError::From<CoreError> now maps CoreError::AudioNotStarted to BridgeError::InvalidCommand and CoreError::Audio(_) to BridgeError::Connection.
  • apps/chanora_flutter/test/widget_test.dart: banner-string expectations updated from "Alpha build" to "Beta build" and from "Alpha 版本" to "Beta 版本".
  • docs/governance/product-decision-register.md bumped to v0.9.7 with a Beta-milestone change-history entry. No decision rows change.
  • docs/governance/poc-results-summary.md bumped to v0.6.0 with a Beta-milestone change-history entry; RISK-PoC-005 updated to reflect Beta progress.

Notes (Beta scope honesty)

  • DSP chain (AEC / AGC / NS / HPF per DEC-007..010) is not yet implemented. AudioEffects exists as a struct but its filters are no-ops in v0.2.0-beta.1. Real DSP is queued for Beta+ work.
  • The capture resampler is a simple linear interpolator. Production quality requires a proper resampler in Beta+.
  • Identity is still ephemeral per connect; persistence via chanora_storage is queued.
  • No live event stream into Dart yet — the UI fetches snapshots and audio stats on a timer instead of subscribing to push events.
  • chanora_diagnostics is still a scaffold; no redaction wired into tracing yet.
  • Audio engine is desktop-only in this Beta. Mobile bundle of the bridge cdylib + UI verification was proven by the PoC but is not re-built into product code in this milestone.

Carry-over from Alpha (v0.1.0-alpha.1)

  • First Alpha build wires the connect → snapshot → disconnect cycle end-to-end from the Flutter UI to a live TeamSpeak-compatible server via the typed Flutter/Rust bridge. Per DEC-001 this is the Internal Alpha milestone; Audio (voice in/out) is deferred to Beta.
  • crates/chanora_protocol/ promoted from a scaffold to a working adapter. Public surface:
    • ConnectConfig, ProtocolClient, ProtocolError.
    • DTO module exposing ChannelId, ClientId, ChannelInfo, ClientInfo, ServerSnapshot — all owned primitives and Strings; no tsclientlib::* types leak (SAD-067).
    • Tokio task owns the tsclientlib::Connection; public handle communicates via mpsc requests + oneshot replies.
    • Connect waits for the initial BookEvents snapshot, then pumps events for ~2 s so the subscribed channel tree settles before the first snapshot is served.
    • Promoted from poc/tsclientlib-connect-spike.
  • core/chanora_core::ChanoraSession now drives the protocol crate with a typed connect/snapshot/is_connected/disconnect API. Enforces the DEC-006 single-connection invariant via an internal tokio::sync::Mutex<Option<ProtocolClient>>.
  • crates/chanora_bridge/ wired against flutter_rust_bridge 2.12.0 (DEC-014). Compiled as cdylib + staticlib + rlib. Exposes:
    • bridge_init() (FRB lifecycle), connect(), snapshot(), disconnect(), is_connected().
    • Typed BridgeChannel, BridgeClient, BridgeSnapshot DTOs; BridgeError with From<chanora_core::CoreError>.
    • A process-wide tokio::Runtime + ChanoraSession via OnceLock, used by every async command.
    • The crate's #![forbid(unsafe_code)] lint was lifted to #![warn(missing_docs)] only, with a doc-comment explanation that the FRB-generated glue legitimately uses unsafe at the FFI boundary; hand-written code in the crate is still expected to avoid unsafe.
  • flutter_rust_bridge.yaml at the repo root drives codegen for the bridge.
  • Generated Dart bindings under apps/chanora_flutter/lib/src/rust/{api.dart,frb_generated*.dart,lib*.dart}.
  • Generated Rust glue under crates/chanora_bridge/src/frb_generated.rs.
  • apps/chanora_flutter/lib/main.dart rewritten as the Alpha UI:
    • Form: server address + nickname, both pre-populated for convenience.
    • Connect button → calls FRB → enters connecting state → shows snapshot.
    • Snapshot view: server welcome banner (preserved verbatim per ADR-008), N channels • M online count, ordered channel list with clients indented under their channel.
    • Refresh and Disconnect actions in the app bar.
  • apps/chanora_flutter/lib/l10n/app_{en,zh}.arb expanded with the Alpha key set: homeNotProductionReadyBanner (now says "Alpha build"), fieldServerHost, fieldNickname, connectAction, disconnectAction, refreshAction, statusIdle, statusConnecting, statusConnected, statusError, channelsHeading, clientsHeading, countChannelsAndClients.
  • flutter_localizations, intl, flutter_rust_bridge, freezed_annotation added to dependencies; freezed and build_runner added to dev_dependencies.
  • apps/chanora_flutter/test/alpha_e2e_test.dart runs the full Dart → FRB → Rust → tsclientlib → network → server path against cn.teamspeak.app. Verifies the snapshot contains a non-empty server name and a non-empty channel list, that isConnected() flips true → false across the disconnect, and that a re-fetched snapshot agrees on the server name. Passes in ~2.5 s.
  • core/chanora_core/tests/alpha_smoke.rs runs the same path from the Rust side; tagged #[ignore] so cargo test --workspace doesn't hit the network by default. Run with --ignored alpha_smoke.

Changed

  • chanora_core::CoreError no longer wraps chanora_bridge::BridgeError; the relationship is the other way around (bridge maps from core). This removes a cyclic chanora_corechanora_bridge dependency introduced when the bridge crate gained chanora_core as a dep.
  • chanora_bridge lints relaxed from #![forbid(unsafe_code)] to #![warn(missing_docs)] (documented above).

LICENSE files (carry-over from earlier in this branch)

  • LICENSE-APACHE — Apache License Version 2.0 text (DEC-020).
  • LICENSE-MIT — MIT License text (DEC-020).
  • Initial repository foundation files.
  • Documentation-first project structure.
  • justfile with format, lint, test, verify-docs, and security-scan targets, completing repository-bootstrap-plan v0.1.0 §3.
  • poc/tsclientlib-connect-spike/ — PoC proving protocol feasibility via tsclientlib. Verified against cn.teamspeak.app on 2026-05-13.
  • poc/flutter_rust_bridge_hello/ — PoC proving the Flutter↔Rust command and event-stream boundary via flutter_rust_bridge 2.12.0. Verified on Linux desktop on 2026-05-13.
  • poc/secure-storage-spike/ — PoC proving platform secure storage via a typed SecretStorageRepository trait and a Linux adapter selecting between Secret Service (libsecret) and kernel keyutils. Audit checks SS-AUD-001/002/003/005/006 and SS-TC-003 verified on 2026-05-13.
  • poc/sqlite-storage-spike/ — PoC proving SRS-089's "embedded data store + migration mechanism" acceptance criteria: forward-only schema migrator tracked via PRAGMA user_version, repository pattern with BookmarkRepository / SettingsRepository traits over LocalDatabaseRepository. 11/11 tests verified on 2026-05-13.
  • poc/diagnostics-redaction-spike/ — PoC proving the diagnostic redaction policy from diagnostic-redaction-audit-report.md: typed policy + regex rules + literal known-secret registry + bundle redaction. Audit cases REDACT-TC-001..010 verified on 2026-05-13.
  • poc/audio-capture-playback-spike/ — PoC proving platform audio capture/playback via cpal. Desktop half (Linux + PipeWire) empirically verified end-to-end on 2026-05-13; mobile half closed separately by poc/audio-capture-playback-android-spike.
  • poc/audio-capture-playback-android-spike/ — PoC closing the mobile half of the audio capture/playback PoC plan entry. Rust cdylib + JNI + Kotlin Android app; cpal targets Android's Oboe backend (AAudio). Verified end-to-end on a physical Motorola Moto G Stylus 5G (2023) running Android 14 arm64-v8a on 2026-05-13: 500 ms 440 Hz sine wave driven out the device speaker (22,050 frames at 44.1 kHz) and 1 s captured from the microphone into a valid 85,292-byte RIFF/WAVE mono 16-bit PCM file pulled via adb exec-out run-as.
  • poc/README.md summarising PoC status against docs/architecture/proof-of-concept-plan.md.

Changed

  • DEC-020 license resolved. Chanora is now dual-licensed under Apache-2.0 OR MIT (recipient's choice), the standard Rust-ecosystem permissive model. Compatible with every direct dependency (tsclientlib, flutter_rust_bridge, cpal, rusqlite, keyring, etc.) and with the Flutter framework's BSD-3-Clause. LICENSE rewritten as a dual-license aggregator pointing at LICENSE-APACHE and LICENSE-MIT. NOTICE rewritten with current direct-dependency attributions. README.md §License updated.

  • docs/governance/product-decision-register.md bumped to v0.9.6: DEC-020 status promoted from Open to Accepted. §4 license row updated. §6 collapsed: there is no longer any open decision — DEC-012 legal review remains as a work item, not a pending decision. Change-history entry added.

  • docs/governance/poc-results-summary.md bumped to v0.4.0: RISK-PoC-003 closed. DEC-020 row moved from the "Still open" section into the closed table.

  • docs/governance/product-decision-register.md bumped to v0.9.5: owner confirmation on all 17 previously-Proposed decisions (DEC-001..010, 012, 015..019, 021). Sixteen were Accepted as recommended; two were modified by the owner — DEC-004 Android minimum raised from API 24 to API 28, and DEC-015 product language for MVP expanded from English-only to English + Chinese (Simplified). DEC-020 license remains Open / Deferred and is now the only public-release-gating decision outstanding. §4 renamed "Recommended" → "Accepted MVP Defaults" with MODIFIED rows annotated. §6 collapsed to the single remaining DEC-020 item. §7 dated and statused for every decision.

  • docs/governance/poc-results-summary.md bumped to v0.3.0: RISK-PoC-004 closed by the owner-confirmation pass; new RISK-PoC-006 (Android minSdk move 24 → 28 for product code) and RISK-PoC-007 (MVP language expansion to en + zh-Hans) added.

  • docs/architecture/proof-of-concept-plan.md bumped to v0.3.0 to promote the audio PoC from PARTIAL PASS to PASS after the Android mobile half was closed; all six PoC plan entries are now PASS.

  • docs/governance/product-decision-register.md bumped to v0.9.4 to promote DEC-011.1 mobile half from Deferred to Accepted (Android), keeping iOS Deferred.

  • docs/governance/poc-results-summary.md bumped to v0.2.0: audio row promoted to PASS, RISK-PoC-001 narrowed from "mobile audio" to "iOS audio only", Android toolchain added to the toolchain table.

  • poc/audio-capture-playback-spike/VERIFICATION.md updated to point at the Android spike for the mobile half.

  • poc/README.md updated to list both audio spike directories.

  • docs/architecture/proof-of-concept-plan.md bumped to v0.2.0 to record PoC outcomes (5 PASS, 1 PARTIAL) and add a Status column.

  • docs/security/secure-storage-audit-report.md bumped to v0.9.3: SS-AUD-001/002/003/005/006 status set to PoC Pass with evidence pointers; SS-TC-003 (Linux) Actual Result populated and Status set to PoC Pass; findings SS-FIND-001..003 added; non-Linux test cases marked Deferred.

  • docs/security/diagnostic-redaction-audit-report.md bumped to v0.9.3: REDACT-TC-001..010 status set to PoC Pass with evidence pointers; export bundle policy §5 populated; findings REDACT-FIND-001..003 added.

  • docs/governance/product-decision-register.md bumped to v0.9.3: owner-confirmed decisions recorded — DEC-014 Accepted (flutter_rust_bridge 2.x pinned), DEC-013.1 Accepted (rusqlite bundled), DEC-013.2 Accepted (Linux Secret Service preferred, keyutils fallback), DEC-011.1 Accepted (desktop cpal) / Deferred (mobile), DEC-022 Accepted (canonical implementation directory layout per README sketch + SAD §7.2), DEC-020 explicitly Deferred and remains a public-release blocker.

Added (governance)

  • docs/governance/poc-results-summary.md v0.1.0 — single-page reviewer-facing summary of the PoC phase, the toolchain exercised, the owner decisions taken, the audit coverage, and the open risks RISK-PoC-001..005.

Fixed

  • N/A

Security

  • N/A

Versioning note

The project implementation has not reached a public release version yet.