Defines 5 tasks (FC-CRATE-1/2/3, WZP-CRATE-1/2) to make both projects' crates importable by each other: featherChat side: - FC-CRATE-1: Make warzone-protocol standalone (replace workspace deps) - FC-CRATE-2: Add CallSignal variant using wzp-proto types - FC-CRATE-3: Extract warzone-identity micro-crate (optional) WZP side (after FC-CRATE-1): - WZP-CRATE-1: Replace identity mirror with real warzone-protocol dep - WZP-CRATE-2: Verify wzp-proto works as git dep from featherChat Priority: FC-CRATE-1 first (30 min, unblocks everything). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.3 KiB
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-protocoluses workspace dependency inheritance (Cargo.tomlhased25519-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, andFingerprinttype inwzp-crypto/src/identity.rs— duplicate code that can drift. - featherChat will need
wzp_proto::SignalMessagefor theWireMessage::CallSignalvariant — 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:
# 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:
[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:
# 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
[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
#[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<u8>, // Opaque JSON bytes
},
}
Alternative (simpler): Always use Vec<u8> for the signal field and let the consumer deserialize. This avoids the feature flag complexity:
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:
# 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
# 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:
//! 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:
# 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
- FC-CRATE-1 — Make warzone-protocol standalone (30 min, unblocks everything)
- WZP-CRATE-2 — Verify wzp-proto works as git dep (30 min)
- FC-CRATE-2 — Add CallSignal with opaque signal_json field (1-2 hours)
- WZP-CRATE-1 — Replace identity mirror with real dep (1 hour)
- FC-CRATE-3 — Extract warzone-identity micro-crate (optional, half day)
After steps 1-4, both projects share types directly:
- WZP imports
warzone-protocolfor identity/seed/fingerprint - featherChat imports
wzp-proto(via git) forSignalMessagetypes - 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