chore: prepare v0.3.0 release
bench-advisory / bench-advisory (push) Failing after 7m7s
ci / cargo check + cargo test (push) Successful in 10m56s
ci / cargo deny (licenses + advisories + bans + sources) (push) Successful in 53s
ci / cargo about (license inventory) (push) Successful in 10m46s
ci / flutter license inventory (push) Successful in 2m13s
ci / flutter analyze (push) Successful in 36s
ci / flutter iOS unsigned release build (push) Has been cancelled
bench-advisory / bench-advisory (push) Failing after 7m7s
ci / cargo check + cargo test (push) Successful in 10m56s
ci / cargo deny (licenses + advisories + bans + sources) (push) Successful in 53s
ci / cargo about (license inventory) (push) Successful in 10m46s
ci / flutter license inventory (push) Successful in 2m13s
ci / flutter analyze (push) Successful in 36s
ci / flutter iOS unsigned release build (push) Has been cancelled
- Rewrite CHANGELOG.md with v0.3.0 entry - Bump version to 0.3.0+100 - Fix split-per-abi Android APK builds
This commit is contained in:
+107
-409
@@ -2,435 +2,133 @@
|
|||||||
|
|
||||||
All notable changes to Chanora will be documented in this file.
|
All notable changes to Chanora will be documented in this file.
|
||||||
|
|
||||||
This project is expected to follow a Conventional Commits style workflow.
|
This project follows a Conventional Commits style workflow.
|
||||||
|
|
||||||
## [Unreleased]
|
## [v0.3.0] — Cross-platform voice client baseline
|
||||||
|
|
||||||
|
The v0.3.0 milestone transitions Chanora from an internal-beta voice
|
||||||
|
prototype to a cross-platform baseline client with event-driven UI,
|
||||||
|
per-user audio controls, non-self client info parity, and CI-hardened
|
||||||
|
Android / iOS / macOS / Linux builds.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Apple CoreML-backed Silero VAD is now the preferred Apple voice
|
- **Event-driven UI** replaces timer-based snapshot polling. Protocol
|
||||||
activity detector when the private sibling `silero-coreml` SwiftPM
|
deltas (client join/leave/move/update, channel add/remove/update)
|
||||||
package is available; WebRTC VAD remains the runtime fallback.
|
flow through a typed `ProtocolDelta` enum and update the Flutter UI
|
||||||
|
in real time. Channel switching is instant.
|
||||||
|
- **Per-user volume controls.** Each client in the snapshot gets an
|
||||||
|
independent volume slider persisted in the bridge layer. Avatar
|
||||||
|
badges show muted/deafened state. Volume adjustments take effect
|
||||||
|
immediately on the audio mix.
|
||||||
|
- **Non-self client info parity with Qint.** The Info tab now populates
|
||||||
|
connection metadata (name, description, created, last connected,
|
||||||
|
connections, transfer, ping deviation) for other clients via an
|
||||||
|
explicit `ClientProfileRefreshPlan` that fetches `connectioninfo`
|
||||||
|
and recent text messages with a bounded timeout.
|
||||||
|
- **Ping deviation in client profiles.** `ping_deviation_milliseconds`
|
||||||
|
propagated from protocol DTO through bridge API to Dart, with a
|
||||||
|
conditional l10n row in the client info sheet (en + zh).
|
||||||
|
- **Apple CoreML Silero VAD** as the preferred voice activity detector
|
||||||
|
on iOS / macOS when the private `silero-coreml` SwiftPM submodule is
|
||||||
|
available. WebRTC VAD remains the runtime fallback.
|
||||||
|
- **TeamSpeak address resolver** (`chanora_resolver`) for DNS SRV
|
||||||
|
lookups and `ts3server://` URI handling.
|
||||||
|
- **Per-ABI Android APK splitting.** `flutter build apk
|
||||||
|
--split-per-abi` produces separate arm64-v8a / armeabi-v7a / x86_64
|
||||||
|
APKs instead of a single fat APK.
|
||||||
|
- **Cross-platform CI** (GitHub Actions): cargo check + test, cargo
|
||||||
|
deny, cargo about license inventory, flutter analyze, flutter test,
|
||||||
|
flutter iOS unsigned release build. All gates must pass before merge.
|
||||||
|
- **Diagnostic recording.** Bridge events, audio interruptions, and
|
||||||
|
Rust-level log entries are captured into an in-memory ring buffer
|
||||||
|
accessible from the diagnostics page.
|
||||||
|
- **Shared app snackbar styling** for consistent error/info feedback.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Apple platform floors are raised to iOS 16 and macOS 13 while the
|
- **iOS / macOS audio lifecycle hardened.** Voice unit restart-in-place,
|
||||||
CoreML VAD package is linked.
|
serialized lifecycle events, WebRTC VAD on iOS, unblocked connect-time
|
||||||
|
audio startup.
|
||||||
## [v1.0.0-rc.1] — MVP Public release candidate
|
- **Linux native audio path promoted** with ONNX Runtime bundled for
|
||||||
|
VAD. Desktop voice I/O works on PipeWire / PulseAudio.
|
||||||
This is the first release candidate for the MVP public release per
|
- **Android audio routing** uses `MODE_IN_COMMUNICATION`, proper
|
||||||
DEC-001 sequencing. The remaining work before `v1.0.0` is DEC-012
|
startup permission flow, and system back-button integration.
|
||||||
legal sign-off (see `docs/governance/legal-review-readiness.md`) and
|
- **`SnapshotChanged` event removed.** Replaced by the typed delta
|
||||||
the staged platform promotions in
|
stream. The dead variant was removed end-to-end (protocol, bridge,
|
||||||
`docs/governance/staged-release-plan.md`.
|
Flutter).
|
||||||
|
- **Prefetch crate renamed** from the PoC-era name to
|
||||||
### Added
|
`chanora_prefetch`. All docs, specs, and code updated.
|
||||||
|
- **Build number bumped to 76.**
|
||||||
- **Platform keyring backing for the per-install Data Encryption Key.**
|
- **Flutter bridge regenerated** for `flutter_rust_bridge` 2.12.0.
|
||||||
`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
|
### Fixed
|
||||||
|
|
||||||
- The watchdog signature blind spot from v0.4: same-count
|
- **`clamp` panic on Info tab open.** `adapter.rs` cast `i64::MIN` to
|
||||||
snapshots no longer suppress `SnapshotChanged`.
|
`u128` before clamping, producing `min > max`. Replaced with
|
||||||
|
`.min(i64::MAX as u128) as i64`.
|
||||||
|
- **Non-self client profiles empty.** Added `request_client_db_info()`
|
||||||
|
with graceful fallback for missing fields.
|
||||||
|
- **Server query clients visible in UI.** Delta joins now filter
|
||||||
|
`is_server_query` clients from the channel tree.
|
||||||
|
- **Speaking indicators lost after reconnect.** Event forwarders are
|
||||||
|
reattached after the connection task restarts.
|
||||||
|
- **Channel double-tap join.** 1-second cooldown prevents rapid
|
||||||
|
re-join attempts.
|
||||||
|
- **`getconnectioninfo` errors silently discarded.** Now logged at
|
||||||
|
warning level instead.
|
||||||
|
- **iOS keyboard lag.** Multiple focus/tap handling fixes for iOS 26
|
||||||
|
`TextField` stale-focus regression.
|
||||||
|
- **Storage test isolation.** Temp directories no longer collide across
|
||||||
|
parallel test runs.
|
||||||
|
- **CI submodule fetch.** iOS build job uses best-effort `git submodule
|
||||||
|
update` with `|| true` fallback for the private `silero-coreml`
|
||||||
|
package.
|
||||||
|
- **Stale license inventory.** Regenerated `docs/security/license-
|
||||||
|
inventory.md` to match current `Cargo.lock`.
|
||||||
|
|
||||||
### Security
|
### Notes
|
||||||
|
|
||||||
- DEK now sits behind the OS session lock on every supported
|
- The `silero-coreml` submodule references a private repository
|
||||||
desktop platform; the v0.4 two-file weakness is closed for those
|
(`chanoraapp/silero-coreml`). CI builds skip the iOS unsigned build
|
||||||
installs. Android, iOS, and other platforms without keyring
|
when the submodule is unavailable.
|
||||||
reach fall back to the v0.4 file model; `RISK-PoC-002` remains
|
- Default base branch is `main` (previously `product/scaffold-v0`).
|
||||||
open for them.
|
- DSP chain (AEC / AGC / NS / HPF) is not yet production-tuned.
|
||||||
|
`AudioEffects` struct exists but filters are placeholder-grade.
|
||||||
### Notes (MVP scope honesty)
|
- Desktop push-to-talk is capability-based; global PTT requires
|
||||||
|
OS-specific permissions and is not yet wired end-to-end in the UI.
|
||||||
- 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
|
## [v0.2.0-beta.1] — Internal Beta first build
|
||||||
|
|
||||||
|
|
||||||
- **Voice in/out wired end-to-end through the Flutter UI.** Per DEC-001
|
- **Voice in/out wired end-to-end through the Flutter UI.** Per DEC-001
|
||||||
this reaches the Internal Beta milestone. Build hash: see the
|
this reaches the Internal Beta milestone.
|
||||||
`v0.2.0-beta.1` git tag.
|
- `crates/chanora_audio/` promoted from scaffold to working engine:
|
||||||
- `crates/chanora_audio/` promoted from scaffold to a working engine:
|
cpal capture (mic gain, linear resampling to 48 kHz, mono down-mix)
|
||||||
- cpal-based capture (mic gain, linear resampling to 48 kHz, mono
|
and playback (48 kHz stereo). Opus VoIP encoding (20 ms frames).
|
||||||
down-mix) and playback (48 kHz stereo, requested config).
|
Push-to-talk gate. Live counters.
|
||||||
- `audiopus::Encoder` for Opus VoIP encoding (20 ms / 960-sample
|
- `crates/chanora_protocol/` extended with voice channels.
|
||||||
mono frames).
|
- `core/chanora_core::ChanoraSession` audio API.
|
||||||
- `tsclientlib::audio::AudioHandler` for the decode + per-client
|
- `crates/chanora_bridge/` audio surface for Dart.
|
||||||
jitter buffer + mix on the playback side.
|
- Beta UI rewrite with connect, PTT, and audio stats.
|
||||||
- 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
|
### Notes
|
||||||
|
|
||||||
- `flutter_rust_bridge.yaml`: added `local: true`.
|
- DSP chain not yet implemented (AEC / AGC / NS / HPF deferred).
|
||||||
- `chanora_bridge::api`: `BridgeError::From<CoreError>` now maps
|
- Capture resampler is linear interpolation (production resampler
|
||||||
`CoreError::AudioNotStarted` to `BridgeError::InvalidCommand` and
|
deferred).
|
||||||
`CoreError::Audio(_)` to `BridgeError::Connection`.
|
- Identity is ephemeral per connect; persistence deferred.
|
||||||
- `apps/chanora_flutter/test/widget_test.dart`: banner-string
|
- No live event stream — UI polls on timer.
|
||||||
expectations updated from "Alpha build" to "Beta build" and from
|
- `chanora_diagnostics` still scaffold.
|
||||||
"Alpha 版本" to "Beta 版本".
|
- Audio engine is desktop-only in this build.
|
||||||
- `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)
|
## [v0.1.0-alpha.1] — Internal Alpha
|
||||||
|
|
||||||
- DSP chain (AEC / AGC / NS / HPF per DEC-007..010) is **not yet
|
- First Alpha build wires connect → snapshot → disconnect end-to-end
|
||||||
implemented**. `AudioEffects` exists as a struct but its filters
|
from the Flutter UI to a live TeamSpeak-compatible server via the
|
||||||
are no-ops in v0.2.0-beta.1. Real DSP is queued for Beta+ work.
|
typed Flutter/Rust bridge.
|
||||||
- The capture resampler is a simple linear interpolator. Production
|
- `crates/chanora_protocol/` promoted from scaffold to working adapter.
|
||||||
quality requires a proper resampler in Beta+.
|
- `crates/chanora_bridge/` wired against `flutter_rust_bridge` 2.12.0.
|
||||||
- Identity is still ephemeral per connect; persistence via
|
- Alpha UI: server address form, connect button, snapshot view.
|
||||||
`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
|
|
||||||
`String`s; 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_core` ↔ `chanora_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
|
## Versioning note
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,10 @@ val releaseStorePassword = resolveSigningProp("CHANORA_RELEASE_STORE_PASSWORD")
|
|||||||
val releaseKeyAlias = resolveSigningProp("CHANORA_RELEASE_KEY_ALIAS")
|
val releaseKeyAlias = resolveSigningProp("CHANORA_RELEASE_KEY_ALIAS")
|
||||||
val releaseKeyPassword = resolveSigningProp("CHANORA_RELEASE_KEY_PASSWORD")
|
val releaseKeyPassword = resolveSigningProp("CHANORA_RELEASE_KEY_PASSWORD")
|
||||||
val hasReleaseSigning = listOf(releaseStoreFile, releaseStorePassword, releaseKeyAlias, releaseKeyPassword).all { !it.isNullOrBlank() }
|
val hasReleaseSigning = listOf(releaseStoreFile, releaseStorePassword, releaseKeyAlias, releaseKeyPassword).all { !it.isNullOrBlank() }
|
||||||
val splitPerAbiRequested = gradle.startParameter.taskNames.any { taskName ->
|
val splitPerAbiRequested = project.hasProperty("split-per-abi") ||
|
||||||
taskName.contains("split-per-abi", ignoreCase = true)
|
gradle.startParameter.taskNames.any { taskName ->
|
||||||
}
|
taskName.contains("split-per-abi", ignoreCase = true)
|
||||||
|
}
|
||||||
val configuredAbiSet = listOf("arm64-v8a", "x86_64")
|
val configuredAbiSet = listOf("arm64-v8a", "x86_64")
|
||||||
val flutterTargetPlatformArg = gradle.startParameter.projectProperties["target-platform"]
|
val flutterTargetPlatformArg = gradle.startParameter.projectProperties["target-platform"]
|
||||||
?.split(',')
|
?.split(',')
|
||||||
@@ -86,16 +87,22 @@ android {
|
|||||||
// and x86_64. Other ABIs (x86, mips, …) shall not be packaged.
|
// and x86_64. Other ABIs (x86, mips, …) shall not be packaged.
|
||||||
// Per-ABI delivery is handled by the AAB bundle splits below
|
// Per-ABI delivery is handled by the AAB bundle splits below
|
||||||
// (SDD-109 item 2) rather than a fat APK.
|
// (SDD-109 item 2) rather than a fat APK.
|
||||||
ndk {
|
// When Flutter --split-per-abi is active, skip ndk.abiFilters to avoid
|
||||||
// DEC-032 RESOLVED (2026-05-18): canonical two-ABI set
|
// conflicting with the Gradle splits.abi mechanism that Flutter injects.
|
||||||
// (arm64-v8a + x86_64) restored after the audiopus_sys
|
// The per-ABI filtering is still handled by the jniLibs excludes below
|
||||||
// ANDROID_ABI propagation gap was closed by the workspace
|
// and the Rust cargo-ndk per-ABI build tasks.
|
||||||
// [patch.crates-io] override pinning `cmake` to the fork
|
if (!splitPerAbiRequested) {
|
||||||
// carrying cmake-rs PR #257 (forwards ANDROID_ABI /
|
ndk {
|
||||||
// ANDROID_PLATFORM as -D variables to the child cmake
|
// DEC-032 RESOLVED (2026-05-18): canonical two-ABI set
|
||||||
// invocation). See Cargo.toml [patch.crates-io] block and
|
// (arm64-v8a + x86_64) restored after the audiopus_sys
|
||||||
// docs/governance/product-decision-register.md DEC-032.
|
// ANDROID_ABI propagation gap was closed by the workspace
|
||||||
abiFilters += effectivePackagingAbis
|
// [patch.crates-io] override pinning `cmake` to the fork
|
||||||
|
// carrying cmake-rs PR #257 (forwards ANDROID_ABI /
|
||||||
|
// ANDROID_PLATFORM as -D variables to the child cmake
|
||||||
|
// invocation). See Cargo.toml [patch.crates-io] block and
|
||||||
|
// docs/governance/product-decision-register.md DEC-032.
|
||||||
|
abiFilters += effectivePackagingAbis
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.2.0-beta.1+76
|
version: 0.3.0+100
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.11.5
|
sdk: ^3.11.5
|
||||||
|
|||||||
Reference in New Issue
Block a user