agent-phone (Rust)
Idiomatic Rust port of @p-vbordei/agent-phone. Minimal sync RPC between two AI agents — Noise-XK handshake over WebSocket, DID-bound peer identity, framed binary transport, self-custody keys. Wire-format-identical with the TS reference: the C4 hex vector matches byte-for-byte.
What's in the box
- Noise-XK handshake (X25519 + ChaCha20Poly1305 + BLAKE2s + HKDF)
- DID-bound peer identity (Ed25519 → X25519 via SHA-512 + RFC 7748 clamp)
- Length-prefixed frame transport (ChaChaPoly seal/open)
- WebSocket client + server (
tokio-tungstenite) - Session lifecycle (connect, call, stream, close)
- Backpressure (credit-based streaming with auto-refresh at the half-mark)
Install
[]
= "0.1"
= { = "1", = ["full"] }
Quickstart
use HandlerOutput;
use ;
use Value;
use ;
async
# server listening on 127.0.0.1:<port>
# noise-xk handshake complete; channel authenticated
# echo result : {"hello":"agent-phone"}
# closed cleanly
How it relates
| Implementation | Status | Wire format |
|---|---|---|
@p-vbordei/agent-phone (TypeScript) |
Reference | source of truth |
agent-phone (Python) |
Port | byte-identical |
agent-phone (Rust, this repo) |
Port | byte-identical |
Conformance
The v0.1 spec defines four conformance clauses; all four pass against the TS test suite.
- C1 — Handshake DID-binding. Swap the responder's static mid-handshake → initiator aborts deterministically at message 2 (AEAD fails).
tests/e2e.rs::c1_handshake_did_binding. - C2 — Streaming backpressure. Server emits 10 000 chunks, client grants 8 at a time → outstanding chunks stay bounded; every chunk arrives in order.
c2_streaming_backpressure. - C3 — Graceful cancel. Cancel mid-stream → server stops within one frame, session stays open for further RPCs.
c3_graceful_cancel. - C4 — Frame determinism. Canonical envelope bytes match the TS hex vector byte-for-byte.
tests/conformance.rs::c4_frame_determinismreadsvectors/c4.json, which mirrors the TS suite.
# 21 passed
Architecture
Module map, dependency choices (hand-rolled Noise XK on chacha20poly1305 + blake2 + curve25519-dalek), Ed25519→X25519 derivation gotcha, the accept_hdr_async quirk for capturing ?caller=<did>, byte-determinism invariants, and testing strategy: see docs/architecture.md.
Development
Contributions welcome — see CONTRIBUTING.md. Any change to noise.rs, frame.rs, or envelope.rs must keep the C4 hex vector passing; that's the wire-format contract.
License
Apache-2.0 — see LICENSE.