feat(mvp): v1.0.0-rc.1 — keyring-backed DEK, encrypted bookmarks, MVP release-gate docs

Closes the v0.4 dual-file weakness in identity-at-rest and turns the
release into an MVP public release candidate. 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`.
No decision rows in `product-decision-register.md` change; the
register's change-history advances to 0.9.8.

`chanora_storage`
-----------------

* New public `Crypto` trait + `IdentityFileStore::crypto()` give
  callers an encrypt / decrypt pair anchored on the per-install
  32-byte DEK without exposing the key material.
* `IdentityFileStore` keyring-first DEK retrieval (Linux Secret
  Service via D-Bus, macOS Keychain, Windows Credential Manager,
  iOS Keychain via the `keyring` crate). Pre-existing
  `identity.dek` files are opportunistically migrated into the
  keyring on first run; the on-disk DEK copy is removed once the
  keyring acknowledges. `CHANORA_DISABLE_KEYRING=1` forces the
  file-fallback path for tests and headless / CI hosts where a
  real keyring call would prompt the user or block on a missing
  D-Bus session.
* `BookmarkRepository::with_crypto(dir, crypto)` encrypts the
  server password into a new `password_blob` BLOB column under
  the same per-install DEK. Schema v2 migration is idempotent —
  legacy v0.4 rows with a plain `password TEXT` are read
  transparently and lifted into `password_blob` on the next
  `update()`. `BookmarkRepository::new` (no crypto) is preserved
  for tests and as a documented fallback when the DEK is
  unreachable.
* Storage tests rise from 8 to 10: encrypted bookmark password
  round-trip + legacy-plaintext-bookmark upgrade.

`chanora_core`
--------------

* `ChanoraSession::init_storage(dir)` wires the bookmark
  repository with crypto by default. On any crypto-derivation
  failure it falls back to the plain-password repository and
  logs the gap — better than hard-failing init.
* `supervisor_loop` now tracks a 64-bit `snapshot_signature` over
  channels (id + parent + order + name) and clients (id + channel
  + name) instead of the old `(channel_count, client_count)`
  tuple. Any in-channel client move, channel rename, or reorder
  now fires `SessionEvent::SnapshotChanged`. The signature sorts
  by id before hashing so it's stable under input-vector
  reordering.
* Two new unit tests cover the signature behaviour; new
  `tests/mvp_storage.rs` integration test drives
  `ChanoraSession::init_storage` end-to-end and verifies the
  bookmark `password_blob` does not contain the plaintext.
* Re-export `ChannelId` + `ClientId` from `chanora_protocol` so
  downstream callers and tests can construct DTOs directly.

Flutter
-------

* New About dialog (info icon in the AppBar) surfaces DEC-018
  (public name "Chanora"), DEC-019 (non-affiliation statement),
  and DEC-020 (Apache-2.0 OR MIT dual license). New ARB keys in
  `app_en.arb` and `app_zh.arb`: `aboutAction`, `aboutVersion`,
  `aboutNonAffiliation`, `aboutLicenseHeading`, `aboutLicenseBody`,
  `aboutThirdPartyHeading`, `aboutThirdPartyBody`.
* `pubspec.yaml` version bumps to `1.0.0-rc.1+5`.

Governance
----------

* `docs/governance/legal-review-readiness.md` — DEC-012 handoff
  package. Enumerates trademark / non-affiliation / license-text
  / third-party-attribution / `tsclientlib`-posture / crypto-
  export / data-handling items the legal reviewer must confirm,
  and lists the concrete engineering deliverables they block on
  (`cargo about generate`, `cargo deny check licenses`,
  Flutter `LicenseRegistry` dump).
* `docs/governance/staged-release-plan.md` — DEC-002 channel
  schedule. Linux + Android sideload promote to GA on DEC-012
  sign-off; Play Store / Windows / macOS / iOS gate on per-
  platform signed-build availability. Rollback policy included.
* `product-decision-register.md` change-history advances to
  0.9.8 with a single entry summarising v0.3, v0.4, and v1.0-rc.1
  progress against DEC-001. No decision rows mutate.

Build + ops
-----------

* `NOTICE` refreshed for the MVP product-code dependency set:
  adds `chacha20poly1305`, `rand`, `zeroize`, `base64`,
  `keyring`, `connectivity_plus`, `path_provider`,
  `freezed_annotation`; drops PoC-only entries.
* `CHANGELOG.md` restructured: explicit version sections for
  v0.3.0-beta.1, v0.4.0-beta.2, v1.0.0-rc.1. Previous "Unreleased"
  contents migrated into their respective milestone sections.
* `.github/workflows/ci.yml` exports `CHANORA_DISABLE_KEYRING=1`
  for the cargo-test job — CI runners have no D-Bus session and
  the keyring crate would otherwise block.
* `run-chanora.sh` reads `CHANORA_BUNDLE_FLAVOUR` (default
  `release`) and self-copies the latest cdylib into the bundle's
  `lib/` if missing.

Verification
------------

* `cargo test --workspace` with `CHANORA_DISABLE_KEYRING=1`: all
  green (49 unit tests across the workspace; up from 36 at
  v0.4.0-beta.2).
* `cargo test -p chanora_core --release -- --ignored alpha_smoke`
  passes against the live `cn.teamspeak.app` (DNS → connect →
  snapshot → disconnect in ~2.5 s).
* `flutter analyze`: clean.
* `cargo build -p chanora_bridge --release` + `flutter build
  linux --release` produce a working Linux x86_64 bundle.

No Android live test in this commit per the user's note that the
physical device was removed; the Android arm64-v8a build path is
mechanically identical to v0.4.0-beta.2.
This commit is contained in:
EdisonJwa
2026-05-15 02:24:42 +08:00
parent 780fd7eca2
commit 50768a8f48
21 changed files with 1471 additions and 115 deletions
+191
View File
@@ -0,0 +1,191 @@
# Legal review readiness — DEC-012 sign-off checklist
| Version | Date | Status |
|---|---|---|
| 0.1.0 | 2026-05-15 | Initial draft alongside v1.0.0-rc.1 |
## Purpose
DEC-012 in `product-decision-register.md` records the legal /
trademark / licensing review as the **only** outstanding gate before
the MVP public release per DEC-001 sequencing. That decision was
accepted as a *release gate* on 2026-05-14; the actual review work
has not been performed.
This document is the engineering-side handoff package for that
review. It enumerates exactly what the reviewer needs to confirm,
points to the artefacts in this repository that answer each item,
and lists the work the reviewer must perform that the engineering
side cannot.
Engineering does **not** make legal decisions. Items marked
"engineering: done" mean the underlying technical artefact is in
place; the corresponding legal confirmation is still required.
## Scope
Reviewer is expected to confirm or correct each of the following
before `v1.0.0-rc.1` is promoted to `v1.0.0` and any release
artefact is published publicly or to a store.
### 1. Trademark — "Chanora"
* **DEC-018** accepted "Chanora" as the public product name.
* Engineering: name appears in `Cargo.toml`, `pubspec.yaml`, the
About dialog, the AppBar title via the `appTitle` localisation
key, and every commit message.
* **Reviewer action**:
- Trademark registrability check in target jurisdictions (CN, US,
EU at minimum, per DEC-002 target platforms' user base).
- Confirm no conflicting registration in voice-communication
software / mobile-app categories.
- Issue go / no-go ruling. A no-go ruling triggers a rename which
invalidates `v1.0.0-rc.1` and forces a new RC.
### 2. Non-affiliation statement — TeamSpeak
* **DEC-019** accepted the working wording:
> Chanora is independent and is not affiliated with, endorsed by,
> sponsored by, or officially associated with TeamSpeak.
* Engineering: that exact sentence ships in:
- `NOTICE` (top of file).
- `README.md` `## License and trademark` section.
- The in-app About dialog (English: `aboutNonAffiliation` ARB key;
Chinese Simplified: `aboutNonAffiliation` in `app_zh.arb`,
translated by an engineer — translation should be reviewed for
legal precision).
* **Reviewer action**:
- Confirm the English wording is sufficient under target-market
consumer-protection and unfair-competition statutes.
- Confirm the Chinese-Simplified translation does not weaken the
statement.
- Confirm there is no remaining text anywhere in the product that
could imply affiliation (search hints: "TeamSpeak", "official",
"endorsed").
### 3. Trademark usage — "TeamSpeak"
The product documentation and UI strings reference "TeamSpeak" in
several places where we describe interoperability (e.g.
"TeamSpeak-compatible servers"). This is nominative use.
* **Reviewer action**:
- Confirm each occurrence of "TeamSpeak" in user-facing strings,
documentation, and store metadata is permissible nominative
use under target-jurisdiction trademark law.
- Recommend a `™` or `®` symbol convention if required.
### 4. License posture — Chanora's own code
* **DEC-020** accepted dual-license **Apache-2.0 OR MIT**.
* Engineering: the texts ship as `LICENSE-APACHE` and `LICENSE-MIT`
at the repository root; the aggregator `LICENSE` references both.
Cargo-level package manifests carry `license.workspace = true`
pointing to `Apache-2.0 OR MIT` in the workspace `Cargo.toml`.
* **Reviewer action**:
- Confirm the dual-license declaration is consistent with all
contributor agreements (none in place yet — see open items).
- Confirm `LICENSE` file contents satisfy each app store's source-
code-availability and inbound-license requirements.
### 5. Third-party license posture — direct dependencies
* `NOTICE` enumerates the direct dependency list as of v1.0.0-rc.1.
* Each direct dependency is permissively licensed
(`MIT`, `Apache-2.0`, `MIT OR Apache-2.0`, `BSD-3-Clause`).
No GPL / LGPL / AGPL surfaces in the direct set.
* **Reviewer action**:
- Confirm the `NOTICE` enumeration matches what the build tooling
actually links (the audit must be repeated against a `cargo
about generate --workspace` output and a Flutter
`LicenseRegistry` dump as of the release build).
- Confirm each direct dependency's attribution obligations are
satisfied (Apache-2.0 requires a copy of the license text, the
NOTICE entry, and a list of changes in any modified copies).
- Confirm no copyleft transitive dependency creeps in via
`tsclientlib` or `cpal`. The most likely failure mode is a
crypto / DSP subdep with LGPL coverage; `cargo deny` config
should refuse those.
### 6. `tsclientlib` posture specifically
The project pins `tsclientlib` to a specific commit
(`04aa249` on `https://github.com/ReSpeak/tsclientlib`). The crate is
upstream-licensed `MIT OR Apache-2.0`. It implements the
TeamSpeak 3 protocol from publicly observed behaviour, not from
TeamSpeak proprietary sources.
* **Reviewer action**:
- Confirm linking against `tsclientlib` does not by itself create
a derivative-work obligation under TeamSpeak's own licenses or
EULAs.
- Confirm using `tsclientlib` to talk to third-party
TeamSpeak-protocol servers does not create a trademark or
contract-tort exposure.
### 7. Crypto + secure-storage compliance
* `chacha20poly1305` (Apache-2.0 OR MIT) provides envelope
encryption for the identity at rest and bookmark passwords.
* `keyring` (Apache-2.0 OR MIT) hits the platform Secret Service /
Keychain / Credential Manager for the DEK.
* No symmetric or asymmetric primitive other than the above is
introduced by Chanora's own code; `tsclientlib` carries its own
protocol-level crypto.
* **Reviewer action**:
- Confirm export-control posture for the resulting binary
(cryptography category determination, ECCN, any EAR self-
classification needed for store distribution).
- Confirm any privacy-statement updates required by jurisdictions
that treat persistent device identifiers as personal data.
### 8. Data handling — DEC-016 / DEC-017
* **DEC-016** No automatic diagnostic upload. The `export_diagnostics`
bridge function is invoked only on user action and the redacted
output is local-only (Clipboard or share-sheet).
* **DEC-017** Crash reporting disabled. Repository grep for
`sentry|crashlytics|bugsnag` returns zero hits as of v1.0.0-rc.1.
* Engineering: diagnostics redaction is enforced at write-time by
the in-bridge `RedactingLogLayer`; tests
`chanora_diagnostics::tests::*` cover the policy.
* **Reviewer action**:
- Confirm privacy policy text aligns: no telemetry, no automatic
upload, no crash reporting in MVP.
- Confirm app-store privacy-label entries are consistent.
### 9. Store-listing copy
Out of scope for engineering; reviewer drafts and validates per
DEC-002 staged platform list:
- Google Play Store (Android arm64-v8a)
- Apple App Store (iOS, MVP gate)
- Microsoft Store / direct (Windows)
- Mac App Store / direct (macOS)
- Linux (direct distribution; no store)
## Open engineering work blocking sign-off
These are concrete items that engineering must close before the
reviewer's work can complete. They do **not** require legal input
themselves — they are listed here so the reviewer's scope is clear.
1. Produce a `cargo about generate --workspace` output checked into
`docs/security/`.
2. Produce a Flutter `LicenseRegistry` dump for the release build
checked into the same path.
3. Wire `cargo deny check licenses` into CI with a deny-list of
GPL / LGPL / AGPL / commercial source licenses.
4. Confirm iOS and macOS build artefacts can ship (DEC-002 staged
release allows deferring these; today neither has a live build).
## Out-of-scope
The following are *not* part of DEC-012 and have their own owners
and decisions:
* Cryptographic primitive selection (`chacha20poly1305`, key sizes,
KDF choice) — Security Architect; closed by DEC-013.2.
* Codec choice (Opus) — Software Architect.
* TLS / connection security — falls inside `tsclientlib`.
@@ -196,3 +196,4 @@ release but is not an open decision:
| Version | Date | Description |
|---|---|---|
| 0.9.7 | 2026-05-14 | DEC-001 release-sequence progress recorded: Internal Alpha (`v0.1.0-alpha.1`, commit 3bb038c) completed on 2026-05-14; **Internal Beta first build (`v0.2.0-beta.1`)** reached the same day. Beta milestone adds voice in/out: `crates/chanora_audio` promoted from scaffold to a cpal-based capture + playback engine with `audiopus` Opus encoding and tsclientlib `AudioHandler` for decode + jitter buffer + mix; `crates/chanora_protocol` extended to forward inbound voice packets and accept outbound `OutPacket`s via mpsc channels; `core/chanora_core::ChanoraSession` exposes `start_audio`, `set_ptt`, and `audio_stats`; `crates/chanora_bridge` adds matching DTOs; the Flutter UI gains a "Start audio" action and a hold-to-talk PTT button with live frame counters. Verified end-to-end against `cn.teamspeak.app`; capture runs in graceful playback-only mode on hosts with no usable microphone (e.g. the PipeWire `auto_null` source on the verification host). No decision rows change; this entry documents progress against DEC-001 only. |
| 0.9.8 | 2026-05-15 | DEC-001 release-sequence progress recorded for the polished Internal Beta and the External Beta milestones, plus the first MVP-public release candidate. **`v0.3.0-beta.1`** ("Internal Beta polish") added the supervisor + reconnect-with-watchdog path (A.6), OS-connectivity-aware backoff (A.6.1), persistent identity at rest as a plain 0600 file (A.2), the redacted in-memory log sink + user-initiated diagnostic export per DEC-016 (A.3), the `SnapshotChanged` lifecycle event for UI auto-refresh (A.4), and the `mobile_voice_preset` config-surface plumb-through (A.5). **`v0.4.0-beta.2`** ("External Beta") added the server-password input, channel join via tap, self mute (input + output), master output gain, SQLite-backed bookmark list, ChaCha20-Poly1305 encryption of the identity at rest with the DEK in a separate `identity.dek` file, Android `AudioManager.setMode(MODE_IN_COMMUNICATION)` routing engagement via JNI, and the `.github/workflows/ci.yml` pipeline. **`v1.0.0-rc.1`** ("MVP Public release candidate") closes the v0.4 DEK-on-disk weakness on every keyring-reachable platform: `chanora_storage::IdentityFileStore` now stores the DEK in the OS keyring (Linux Secret Service via D-Bus / macOS Keychain / Windows Credential Manager / iOS Keychain via the `keyring` crate) and migrates pre-existing file-fallback installs into the keyring opportunistically; bookmark server passwords are ChaCha20-Poly1305-encrypted under the same per-install DEK and the legacy plain `password TEXT` column is upgraded into a new `password_blob BLOB` column on the next `update()`; `SessionEvent::SnapshotChanged` now fires on any tree mutation (the in-channel-move blind spot from A.4 is closed); the in-app About dialog surfaces DEC-018 / DEC-019 / DEC-020. New `docs/governance/legal-review-readiness.md` carries the DEC-012 handoff package (trademark check, non-affiliation wording, third-party license posture, `cargo about` deliverables, `cargo deny` lifelines); new `docs/governance/staged-release-plan.md` enumerates the DEC-002 platform staging (Linux + Android sideload GA on DEC-012 sign-off; Windows, macOS, iOS gate on per-platform signed-build availability). No decision rows change; DEC-012 remains the sole outstanding release gate. |
+111
View File
@@ -0,0 +1,111 @@
# Staged MVP release plan — DEC-002 channels
| Version | Date | Status |
|---|---|---|
| 0.1.0 | 2026-05-15 | Initial draft alongside v1.0.0-rc.1 |
## Purpose
DEC-002 in `product-decision-register.md` accepts a five-platform
MVP target (Windows, macOS, Linux, Android, iOS) and explicitly
permits a **staged release** in which not all platforms ship on the
same day. This document records the staging plan for the MVP public
release.
## Channel definitions
* **GA — Linux desktop**: x86_64 GNU/Linux. Direct distribution
via the GitHub Releases page (release tarball + AppImage when
the bundle is added). No app store.
* **GA — Android**: arm64-v8a APK. Direct distribution from the
Releases page during early MVP. Play Store submission deferred
until DEC-012 legal sign-off includes Play-specific consumer-
protection review (`docs/governance/legal-review-readiness.md`
§9). Other Android ABIs (armeabi-v7a, x86_64) are deferred to
v1.1.
* **Beta — Windows**: x86_64 MSI / portable zip. The build steps
are documented in `docs/release/windows-build.md`; a live
artefact has not yet been produced because the development
toolchain ran on Linux. Public bits land once a Windows builder
produces a signed artefact.
* **Beta — macOS**: Apple Silicon (`aarch64-apple-darwin`) + Intel
(`x86_64-apple-darwin`) universal `.app`. Same gating as Windows:
build documentation only at v1.0.0-rc.1; an Apple-side builder
must produce a notarised artefact.
* **Beta — iOS**: arm64 IPA. Same gating; in addition the
`mobile_voice_preset` AAudio engagement on Android is documented
as a routing hint, and the iOS equivalent
(`AVAudioSession.Mode.voiceChat`) is not yet wired.
## Promotion schedule
Each row triggers when the listed prerequisite is satisfied. There
is no calendar.
| Order | Channel | Prerequisite |
|---|---|---|
| 1 | Linux GA | DEC-012 legal sign-off; `v1.0.0` tag cut from `v1.0.0-rc.N` after all RC reviews close. |
| 2 | Android GA (sideload) | Same prerequisite; APK signed with the project key; Releases page updated. |
| 3 | Android Play Store | DEC-012 §9 Play-listing review complete; signing key migrated to Play App Signing if not already there. |
| 4 | Windows Beta | Live x86_64 Windows builder produces a signed MSI; smoke test on a Windows 11 host that the build host can verify. |
| 5 | macOS Beta | Live `aarch64-apple-darwin` build with code-signing certificate and notarisation; smoke test. |
| 6 | iOS Beta | TestFlight build cycle; per-device entitlement review; mic permission flow validated. |
| 7 | Windows / macOS / iOS GA | Each platform promoted from Beta after its first round of public-Beta feedback is triaged. |
## Per-platform readiness as of v1.0.0-rc.1
### Linux x86_64
* Cargo build: green (`cargo check --workspace`, `cargo test
--workspace` with `CHANORA_DISABLE_KEYRING=1`).
* Flutter build: `flutter build linux --release` documented; live
bundle exists at `apps/chanora_flutter/build/linux/x64/`.
* Audio: cpal-on-PipeWire / ALSA verified through Beta lifecycle.
* Secure storage: Secret Service via D-Bus when a session is
available; file-fallback when not.
* Status: **GA-ready** pending DEC-012.
### Android arm64-v8a
* Cargo + cargo-ndk build: green; the APK pipeline produced
`app-release.apk` for v0.4.0-beta.2.
* Audio: cpal-on-AAudio with `AudioManager.MODE_IN_COMMUNICATION`
engaged via JNI on engine start.
* Secure storage: keyring crate falls back to file on Android in
v1.0.0-rc.1 (no Android Keystore wiring yet). The identity and
bookmark passwords remain ChaCha20-Poly1305-encrypted under the
file-DEK in this configuration.
* Status: **GA-ready for sideload** pending DEC-012; Play Store
promotion is a separate gate.
### Windows / macOS / iOS
* Build docs exist (`docs/release/windows-build.md`,
`docs/release/ios-build.md`); macOS does not have a dedicated
doc yet because the build is a straightforward
`flutter build macos` once Apple-side signing is configured.
* Status: **Beta-track**. The RC tag still ships for these
platforms in source form (anyone with the appropriate toolchain
can build), but no signed binary is included.
## Rollback policy
Each platform's Releases-page artefact carries the exact `v1.0.0-rc.N`
or `v1.0.0` tag. If a critical regression surfaces in a channel:
1. Mark the affected Releases asset "deprecated — do not download" in
the GitHub UI within 24h.
2. Cut a `v1.0.x` patch from the `release-1.0` branch (created
when `v1.0.0` is tagged).
3. Re-promote per the schedule above; no platform fast-tracks the
schedule, no platform skips a channel.
## Out-of-scope for MVP
* iOS hardware AEC engagement (`AVAudioSession.Mode.voiceChat`).
* Android `setInputPreset(VOICE_COMMUNICATION)` (RISK-AUDIO-MOBILE-001).
* Android Keystore-backed DEK (the file-fallback path is exercised in
the current build).
* Multi-server connection (DEC-006: explicitly out of MVP).
* Crash reporting (DEC-017: explicitly disabled).
* Automatic diagnostic upload (DEC-016: user-initiated only).