diff --git a/docs/WZP-FC-SHARED-CRATES.md b/docs/WZP-FC-SHARED-CRATES.md new file mode 100644 index 0000000..bd7e049 --- /dev/null +++ b/docs/WZP-FC-SHARED-CRATES.md @@ -0,0 +1,230 @@ +# Shared Crate Strategy: WZP ↔ featherChat + +**Goal:** Both projects import each other's crates directly instead of duplicating code. A change to identity derivation in featherChat automatically applies in WZP, and vice versa for call signaling types. + +--- + +## Current Problem + +- `warzone-protocol` uses workspace dependency inheritance (`Cargo.toml` has `ed25519-dalek.workspace = true`). When WZP tries to use it as a path dep, Cargo fails because it can't resolve workspace references from outside the featherChat workspace. +- WZP had to mirror featherChat's `identity.rs`, `mnemonic.rs`, and `Fingerprint` type in `wzp-crypto/src/identity.rs` — duplicate code that can drift. +- featherChat will need `wzp_proto::SignalMessage` for the `WireMessage::CallSignal` variant — another potential duplication. + +## Solution: Make Key Crates Standalone-Importable + +### What featherChat Needs to Do + +#### FC-CRATE-1: Make `warzone-protocol` standalone-publishable + +**File:** `warzone/crates/warzone-protocol/Cargo.toml` + +Replace all `workspace = true` references with explicit versions: + +```toml +# Before: +ed25519-dalek.workspace = true +x25519-dalek.workspace = true + +# After: +ed25519-dalek = { version = "2", features = ["serde", "rand_core"] } +x25519-dalek = { version = "2", features = ["serde", "static_secrets"] } +chacha20poly1305 = "0.10" +hkdf = "0.12" +sha2 = "0.10" +rand = "0.8" +bip39 = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +bincode = "1" +thiserror = "2" +hex = "0.4" +base64 = "0.22" +uuid = { version = "1", features = ["v4"] } +zeroize = { version = "1", features = ["derive"] } +chrono = { version = "0.4", features = ["serde"] } +k256 = { version = "0.13", features = ["ecdsa", "serde"] } +tiny-keccak = { version = "2", features = ["keccak"] } +``` + +**Keep workspace inheritance working too** by using the `[package]` fallback pattern: +```toml +[package] +name = "warzone-protocol" +version = "0.0.20" +edition = "2021" +# Remove version.workspace and edition.workspace — use explicit values +``` + +This way the crate still works inside the featherChat workspace AND can be imported by WZP as a path dependency. + +**Test:** From the WZP repo, this should work: +```toml +# In wzp-crypto/Cargo.toml: +warzone-protocol = { path = "../../deps/featherchat/warzone/crates/warzone-protocol" } +``` + +**Effort:** 30 minutes. Mechanical replacement, then `cargo build` to verify. + +#### FC-CRATE-2: Add `wzp-proto` as a git dependency for `CallSignal` + +**File:** `warzone/crates/warzone-protocol/Cargo.toml` + +```toml +[dependencies] +# WarzonePhone signaling types (for CallSignal WireMessage variant) +wzp-proto = { git = "ssh://git@git.manko.yoga:222/manawenuz/wz-phone.git", optional = true } + +[features] +default = [] +wzp = ["wzp-proto"] +``` + +**File:** `warzone/crates/warzone-protocol/src/message.rs` + +```rust +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum WireMessage { + // ... existing variants ... + + /// Voice/video call signaling (requires "wzp" feature). + #[cfg(feature = "wzp")] + CallSignal { + id: String, + sender_fingerprint: String, + signal: wzp_proto::SignalMessage, // Typed, not opaque bytes + }, + + /// Voice/video call signaling (without wzp feature — opaque bytes). + #[cfg(not(feature = "wzp"))] + CallSignal { + id: String, + sender_fingerprint: String, + signal: Vec, // Opaque JSON bytes + }, +} +``` + +**Alternative (simpler):** Always use `Vec` for the signal field and let the consumer deserialize. This avoids the feature flag complexity: + +```rust +CallSignal { + id: String, + sender_fingerprint: String, + signal_json: String, // JSON-serialized wzp_proto::SignalMessage +}, +``` + +featherChat server treats it as opaque. WZP client deserializes it to `SignalMessage`. + +**Effort:** 1-2 hours. + +#### FC-CRATE-3: Extract shared identity types to a micro-crate (optional, long-term) + +Create `warzone-identity` crate containing only: +- `Seed` (generation, from_bytes, from_hex, from_mnemonic, to_mnemonic) +- `IdentityKeyPair` (derive from seed) +- `PublicIdentity` (verifying key, encryption key, fingerprint) +- `Fingerprint` (SHA-256 truncated, display format) +- `hkdf_derive()` helper + +Both `warzone-protocol` and `wzp-crypto` depend on `warzone-identity` instead of each implementing their own. This is the cleanest long-term solution but requires more refactoring. + +**Crate structure:** +``` +warzone-identity/ +├── Cargo.toml (standalone, no workspace inheritance) +├── src/ +│ ├── lib.rs +│ ├── seed.rs +│ ├── identity.rs +│ ├── fingerprint.rs +│ └── mnemonic.rs +``` + +**Dependencies:** ed25519-dalek, x25519-dalek, hkdf, sha2, bip39, hex, zeroize + +Both projects import it: +```toml +# featherChat: +warzone-identity = { path = "../warzone-identity" } + +# WZP (via submodule): +warzone-identity = { path = "deps/featherchat/warzone-identity" } +``` + +**Effort:** Half a day. Extract code from warzone-protocol, update imports in both projects. + +--- + +### What WZP Needs to Do (after featherChat completes FC-CRATE-1) + +#### WZP-CRATE-1: Replace identity mirror with real dependency + +Once `warzone-protocol` is standalone-importable: + +**File:** `crates/wzp-crypto/Cargo.toml` +```toml +# Remove bip39 and hex (now comes from warzone-protocol) +# Add: +warzone-protocol = { path = "../../deps/featherchat/warzone/crates/warzone-protocol" } +``` + +**File:** `crates/wzp-crypto/src/identity.rs` +Replace the entire file with re-exports: +```rust +//! featherChat identity — re-exported from warzone-protocol. +pub use warzone_protocol::identity::{IdentityKeyPair, Seed}; +pub use warzone_protocol::types::Fingerprint; +``` + +**File:** `crates/wzp-crypto/src/handshake.rs` +Use `warzone_protocol::identity::Seed` internally instead of raw HKDF calls. + +**Effort:** 1 hour (after FC-CRATE-1 is done). + +#### WZP-CRATE-2: Make `wzp-proto` standalone-importable + +`wzp-proto` already has explicit dependency versions (not workspace-inherited for external deps). It should work as a git dependency from featherChat. Verify: + +```bash +# From a scratch project: +cargo add --git ssh://git@git.manko.yoga:222/manawenuz/wz-phone.git wzp-proto +``` + +If this fails, replace any remaining workspace references in `wzp-proto/Cargo.toml` with explicit versions. + +**Key types featherChat needs from wzp-proto:** +- `SignalMessage` (CallOffer, CallAnswer, IceCandidate, Hangup, etc.) +- `QualityProfile` (for codec negotiation) +- `HangupReason` + +**Effort:** 30 minutes to verify and fix. + +--- + +## Recommended Order + +1. **FC-CRATE-1** — Make warzone-protocol standalone (30 min, unblocks everything) +2. **WZP-CRATE-2** — Verify wzp-proto works as git dep (30 min) +3. **FC-CRATE-2** — Add CallSignal with opaque signal_json field (1-2 hours) +4. **WZP-CRATE-1** — Replace identity mirror with real dep (1 hour) +5. **FC-CRATE-3** — Extract warzone-identity micro-crate (optional, half day) + +After steps 1-4, both projects share types directly: +- WZP imports `warzone-protocol` for identity/seed/fingerprint +- featherChat imports `wzp-proto` (via git) for `SignalMessage` types +- No duplicated code, no drift risk + +--- + +## Dependency Graph After Integration + +``` +warzone-identity (shared micro-crate, optional step 5) + ↑ ↑ +warzone-protocol wzp-crypto + ↑ ↑ +warzone-server wzp-proto ← wzp-codec, wzp-fec, wzp-transport + ↑ ↑ +warzone-client wzp-client, wzp-relay, wzp-web +```