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.