Files
wz-phone/docs/EXTENSIBILITY.md
Siavash Sameni 5425c59e7d docs: comprehensive project documentation
- 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>
2026-03-28 05:30:11 +04:00

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:

  1. Add a new variant to CodecId in crates/wzp-proto/src/codec_id.rs (uses 4-bit wire encoding, currently 5 of 16 values used)
  2. Implement AudioEncoder and AudioDecoder for your codec
  3. Register the codec in AdaptiveEncoder/AdaptiveDecoder in crates/wzp-codec/src/adaptive.rs
  4. Add a QualityProfile constant 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:

  1. Caller looks up callee via @alias, federated address, or raw fingerprint
  2. Caller sends WireMessage::CallOffer through the existing message channel
  3. Callee receives the offer and responds with WireMessage::CallAnswer
  4. 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:

  1. 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.

  2. Implementation point: crates/wzp-relay/src/handshake.rs already implements accept_handshake() which performs signature verification. To gate admission, add an authorization check after signature verification.

  3. Token-based auth: Add a token: Vec<u8> field to CallOffer containing 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:

  1. Relay discovery and route selection (not yet implemented)
  2. Per-hop FEC parameters (each link may have different loss characteristics)
  3. Cumulative latency management (each hop adds jitter buffer delay)

Video Support

The trait architecture supports video by adding:

  1. Video codec trait: Similar to AudioEncoder/AudioDecoder but for video frames
  2. Codec choices: AV1 (best compression, higher CPU), VP9 SVC (scalable, moderate CPU)
  3. Separate FEC strategy: Video frames are larger and more critical (I-frames vs P-frames need different protection levels)
  4. 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"):

  1. JNI bindings: Use jni crate or uniffi to expose CallEncoder, CallDecoder, and MediaTransport to Kotlin/Java
  2. Audio I/O: Android uses AAudio or OpenSL ES instead of cpal
  3. Build: Cross-compile with cargo ndk targeting aarch64-linux-android and armv7-linux-androideabi
  4. 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:

  1. STUN server queries to discover the client's public IP/port
  2. ICE candidate exchange via the signaling channel
  3. TURN relay fallback when direct UDP is blocked
  4. 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:

  1. Feed PathMonitor::quality() into AdaptiveQualityController::observe() as QualityReport values
  2. The controller will trigger tier transitions when conditions change
  3. Propagate the new QualityProfile to both encoder (codec switch) and FEC (ratio change)
  4. Signal the peer via SignalMessage::QualityUpdate so 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.