Moved from wayBetter for X (distribution.rs + desktop_store.rs) so every wayBetter app gets the
Store; the app keeps its Tauri command wrappers and a `StoreConfig`.
- store::catalog (pure): `StoreConfig` (app id in the shared catalog, product, user agent, link
scheme, store URL, public keys, allowed hosts), `AllowedHost` (raw-files-only rule for a code
host; DEFAULT_HOSTS = waybetter.fyi, cdn1.crt.fyi, git.crt.fyi raw), install-link parsing,
download allow-list, theme validation, catalog format 2: every item names its `apps`; the whole
catalog is verified (Ed25519 over the exact bytes with any listed key, so keys rotate; strict
schema; ids/files unique per app) and each app gets only its own items (another app's item is
shape-checked but not held to this app's hosts); per-item sha256/size, install records, versions.
- store::install: `Store` (pending links, staged installs with TTL/bound, MIN_REVIEW before a
commit, commit writes exactly the staged bytes after re-validation via PluginStore::install,
plugin remove, records file, verified catalog + offline copy verified again when read back,
StoreView). Plugin start/stop scripts run through a function the app passes.
- store::net (`net` feature): no-redirect, no-cookie reqwest fetch with caps; stage_link,
stage_item, catalog(refresh).
- Tests: links, per-app allow-lists and schemes, themes, signatures incl. tampering and key
rotation, multi-app filtering, bad items, staging/commit/remove/records/offline copy on disk,
fetch refuses anything off the allow-list. test:rust and check:tauri build with `net`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>