- ARCHITECTURE.md: protocol design, wire format, FEC, crypto, relay modes - USAGE.md: build instructions, all CLI flags, deployment examples - DESIGN.md: rationale for codec/FEC/transport/crypto choices - EXTENSIBILITY.md: trait extension points, Warzone integration, future features - PROGRESS.md: phase 1-4 timeline, test coverage, known issues - API.md: complete crate API reference for all 8 crates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.4 KiB
WarzonePhone Extension Points & Future Features
Trait-Based Architecture
The protocol is designed around trait interfaces defined in crates/wzp-proto/src/traits.rs. Any implementation that satisfies the trait contract can be plugged in without modifying other crates.
Adding a New Audio Codec
Implement AudioEncoder and AudioDecoder from wzp_proto::traits:
pub trait AudioEncoder: Send + Sync {
fn encode(&mut self, pcm: &[i16], out: &mut [u8]) -> Result<usize, CodecError>;
fn codec_id(&self) -> CodecId;
fn set_profile(&mut self, profile: QualityProfile) -> Result<(), CodecError>;
fn max_frame_bytes(&self) -> usize;
fn set_inband_fec(&mut self, _enabled: bool) {}
fn set_dtx(&mut self, _enabled: bool) {}
}
pub trait AudioDecoder: Send + Sync {
fn decode(&mut self, encoded: &[u8], pcm: &mut [i16]) -> Result<usize, CodecError>;
fn decode_lost(&mut self, pcm: &mut [i16]) -> Result<usize, CodecError>;
fn codec_id(&self) -> CodecId;
fn set_profile(&mut self, profile: QualityProfile) -> Result<(), CodecError>;
}
Steps:
- Add a new variant to
CodecIdincrates/wzp-proto/src/codec_id.rs(uses 4-bit wire encoding, currently 5 of 16 values used) - Implement
AudioEncoderandAudioDecoderfor your codec - Register the codec in
AdaptiveEncoder/AdaptiveDecoderincrates/wzp-codec/src/adaptive.rs - Add a
QualityProfileconstant for the new codec
Adding a New FEC Scheme
Implement FecEncoder and FecDecoder from wzp_proto::traits:
pub trait FecEncoder: Send + Sync {
fn add_source_symbol(&mut self, data: &[u8]) -> Result<(), FecError>;
fn generate_repair(&mut self, ratio: f32) -> Result<Vec<(u8, Vec<u8>)>, FecError>;
fn finalize_block(&mut self) -> Result<u8, FecError>;
fn current_block_id(&self) -> u8;
fn current_block_size(&self) -> usize;
}
pub trait FecDecoder: Send + Sync {
fn add_symbol(&mut self, block_id: u8, symbol_index: u8, is_repair: bool, data: &[u8]) -> Result<(), FecError>;
fn try_decode(&mut self, block_id: u8) -> Result<Option<Vec<Vec<u8>>>, FecError>;
fn expire_before(&mut self, block_id: u8);
}
For example, a Reed-Solomon implementation would maintain the same block/symbol structure but use a different coding algorithm internally. The FEC block ID and symbol index fields in MediaHeader support any scheme that fits the block/symbol model.
Adding a New Transport
Implement MediaTransport from wzp_proto::traits:
#[async_trait]
pub trait MediaTransport: Send + Sync {
async fn send_media(&self, packet: &MediaPacket) -> Result<(), TransportError>;
async fn recv_media(&self) -> Result<Option<MediaPacket>, TransportError>;
async fn send_signal(&self, msg: &SignalMessage) -> Result<(), TransportError>;
async fn recv_signal(&self) -> Result<Option<SignalMessage>, TransportError>;
fn path_quality(&self) -> PathQuality;
async fn close(&self) -> Result<(), TransportError>;
}
A raw UDP transport, a WebRTC data channel transport, or a TCP tunnel transport could all implement this trait.
Obfuscation Layer (Phase 2)
The ObfuscationLayer trait is defined in crates/wzp-proto/src/traits.rs but not yet implemented:
pub trait ObfuscationLayer: Send + Sync {
fn obfuscate(&mut self, data: &[u8], out: &mut Vec<u8>) -> Result<(), ObfuscationError>;
fn deobfuscate(&mut self, data: &[u8], out: &mut Vec<u8>) -> Result<(), ObfuscationError>;
}
Planned implementations:
- TLS-in-TLS: Wrap QUIC traffic inside a TLS connection to port 443, making it look like ordinary HTTPS
- HTTP/2 mimicry: Frame QUIC packets as HTTP/2 data frames
- Random padding: Add random-length padding to defeat traffic analysis
- Domain fronting: Use CDN infrastructure to hide the true destination
The obfuscation layer sits between the crypto layer and the transport layer in the protocol stack, wrapping encrypted packets before transmission.
FeatherChat / Warzone Messenger Integration
As described in docs/featherchat.md, WarzonePhone is designed to integrate with the existing Warzone messenger.
Shared Identity Model
Both WarzonePhone and Warzone use the same identity derivation:
- 32-byte seed (BIP39 mnemonic backup)
- HKDF with context strings:
"warzone-ed25519-identity"and"warzone-x25519-identity" - Ed25519 for signing, X25519 for encryption
- Fingerprint:
SHA-256(Ed25519_pub)[:16]
This is implemented in crates/wzp-crypto/src/handshake.rs as WarzoneKeyExchange::from_identity_seed().
Signaling via Existing WebSocket
Call initiation flows through the Warzone messenger's existing WebSocket connection:
- Caller looks up callee via
@alias, federated address, or raw fingerprint - Caller sends
WireMessage::CallOfferthrough the existing message channel - Callee receives the offer and responds with
WireMessage::CallAnswer - Both sides establish a direct QUIC connection to the relay using ephemeral keys from the signaling exchange
The SignalMessage::CallOffer and SignalMessage::CallAnswer variants in crates/wzp-proto/src/packet.rs carry the same fields needed for this flow.
Key Derivation from Existing Shared Secret
When two Warzone users already have an X3DH shared secret from their messaging session, call keys can be derived from it:
HKDF(x3dh_shared_secret, "warzone-call-session")-> 32-byte session key- Or: fresh ephemeral exchange per call (current implementation) for independent forward secrecy
Unified Addressing
The Warzone addressing system resolves user identities across multiple namespaces:
| Method | Format | Resolution |
|---|---|---|
| Local alias | @manwe |
Server resolves to fingerprint |
| Federated | @manwe.b1.example.com |
DNS TXT record -> fingerprint + endpoint |
| ENS | @manwe.eth |
Ethereum address -> fingerprint (planned) |
| Raw fingerprint | xxxx:xxxx:... |
Direct lookup |
A user calls @manwe the same way they message @manwe.
Authentication: Caller Verification Before Bridging
Currently, relays forward packets without verifying caller identity. To add authentication:
-
Relay-side handshake: The relay receives the
CallOffer, verifies the Ed25519 signature, and checks the caller's identity against an allowlist before accepting the connection. -
Implementation point:
crates/wzp-relay/src/handshake.rsalready implementsaccept_handshake()which performs signature verification. To gate admission, add an authorization check after signature verification. -
Token-based auth: Add a
token: Vec<u8>field toCallOffercontaining a relay-issued authentication token (e.g., signed by the relay operator's key).
Multi-Relay Mesh
The current two-relay chain (--remote flag) can be extended to a multi-hop mesh:
Client -> Relay A -> Relay B -> Relay C -> Destination
Each hop uses the relay pipeline (FEC decode -> jitter buffer -> FEC re-encode) to absorb loss on each link independently. This requires:
- Relay discovery and route selection (not yet implemented)
- Per-hop FEC parameters (each link may have different loss characteristics)
- Cumulative latency management (each hop adds jitter buffer delay)
Video Support
The trait architecture supports video by adding:
- Video codec trait: Similar to
AudioEncoder/AudioDecoderbut for video frames - Codec choices: AV1 (best compression, higher CPU), VP9 SVC (scalable, moderate CPU)
- Separate FEC strategy: Video frames are larger and more critical (I-frames vs P-frames need different protection levels)
- SVC (Scalable Video Coding): With VP9 SVC, the relay can drop enhancement layers without transcoding, adapting video quality to each receiver's bandwidth
Video would add new CodecId variants and a separate QualityProfile for video parameters.
Android Native Client
The workspace is designed with Android in mind (wzp-client description mentions "for Android (JNI) and Windows desktop"):
- JNI bindings: Use
jnicrate oruniffito exposeCallEncoder,CallDecoder, andMediaTransportto Kotlin/Java - Audio I/O: Android uses AAudio or OpenSL ES instead of cpal
- Build: Cross-compile with
cargo ndktargetingaarch64-linux-androidandarmv7-linux-androideabi - Permissions:
RECORD_AUDIO,INTERNET,WAKE_LOCK
STUN/TURN NAT Traversal Integration
The SignalMessage::IceCandidate variant is already defined for NAT traversal:
IceCandidate { candidate: String }
Integration would involve:
- STUN server queries to discover the client's public IP/port
- ICE candidate exchange via the signaling channel
- TURN relay fallback when direct UDP is blocked
- Integration with the existing QUIC transport (QUIC can traverse NATs via its connection migration)
Bandwidth Estimation and Adaptive Bitrate
The PathMonitor in crates/wzp-transport/src/path_monitor.rs already estimates bandwidth from observed packet rates. To close the loop:
- Feed
PathMonitor::quality()intoAdaptiveQualityController::observe()asQualityReportvalues - The controller will trigger tier transitions when conditions change
- Propagate the new
QualityProfileto both encoder (codec switch) and FEC (ratio change) - Signal the peer via
SignalMessage::QualityUpdateso both sides switch simultaneously
The framework is in place; the missing piece is the integration wiring in the client's main loop to periodically generate quality reports from path metrics.