A C# client library (multi-targets .NET 8 and .NET 10), test harness, and command-line toolkit for RHPv2 — the JSON-over-TCP "Remote Host Protocol" used by XRouter to expose its multi-protocol packet engine to applications.
📖 Full documentation: https://rhp2lib.pages.dev/
📦 Install: dotnet add package RhpV2.Client ·
self-contained rhp CLI binaries for Linux / Windows / macOS at
GitHub Releases.
The protocol is described in PWP-0222 and PWP-0245 (Paula Dowie, G8PZT et al., June 2023).
src/RhpV2.Client/ reusable client library + in-process mock server
src/RhpV2.Tools/ `rhp` CLI (chat, mon, send, probe, serve)
tests/RhpV2.Client.Tests/ xunit suite (framing, codecs, mock-driven)
tests/RhpV2.Client.IntegrationTests/ Testcontainers suite — drives ghcr.io/packethacking/xrouter
using RhpV2.Client;
using RhpV2.Client.Protocol;
await using var rhp = await RhpClient.ConnectAsync("xrouter.local");
await rhp.AuthenticateAsync("g8pzt", "secret");
rhp.Received += (_, e) =>
Console.WriteLine($"<- {e.Message.Data}");
var handle = await rhp.OpenAsync(
ProtocolFamily.Ax25, SocketMode.Stream,
port: "1", local: "G8PZT", remote: "GB7PZT",
flags: OpenFlags.Active);
await rhp.SendOnHandleAsync(handle, "hello\r");Highlights:
- Length-prefixed (2-byte big-endian) framing, exact to spec.
- Strongly-typed messages (
AuthMessage,OpenMessage,RecvMessage, …). - Async request/reply correlation via auto-assigned
idfield. - Asynchronous notifications surfaced as events (
Received,Accepted,StatusChanged,Closed,UnknownReceived,Disconnected). - Tolerates spec quirks (
errCodeon AUTHREPLY,ConnectReplyPascalCase). - Forward-compatible: unknown message types arrive as
UnknownMessage.
rhp probe --host xrouter.local --port 9000 --user g8pzt --pass …
rhp chat --pfam ax25 --radio 1 --local G8PZT --remote GB7PZT
rhp mon --pfam ax25 --radio 1 # TRACE-mode monitor
rhp send --pfam ax25 --radio 1 --local G8PZT --remote G8PZT-1 "Hi"
rhp serve --port 9000 # local mock for dev
All commands share a common --host/--port/--user/--pass vocabulary.
The MockRhpServer (in RhpV2.Client.Testing) is shipped with the library
itself so downstream applications can write integration tests without
spinning up a real XRouter:
await using var server = new MockRhpServer();
server.Start();
await using var client =
await RhpClient.ConnectAsync("127.0.0.1", server.Endpoint.Port);
// ... drive any sequence you like; assert against server.ReceivedFrames.The same mock backs rhp serve, so the CLI doubles as a self-test driver.
dotnet build
dotnet test- Unit suite (49 tests; runs everywhere): framing, codec, polymorphic JSON dispatch, correlated request/reply, server-pushed notifications, transport teardown.
- Integration suite (36 tests; requires Docker): pulls
ghcr.io/packethacking/xroutervia Testcontainers and pins the client against the real RHP server. A two-container fixture links the nodes by AXUDP and seeds the NetRom routing table so routing works on first boot. Coverage spans: AX.25 stream connect / send / recv / close (real SABM/UA/I/RR); passive listener accepting an inbound peer connection; peer-initiated close firingClosed; TRACE-mode frame capture (frametype,srce/dest,ctrl,pid,ptcl); RAW-mode complete on-the-wire frames; DGRAM (UI frame)sendtowith binary-byte round-trip; NetRom stream connect through to the peer's command processor;pfam=inetstream GET to xrouter's HTTP server with server-initiated close;seqpkt/customsocket allocation; connect-to-unreachable lifecycle (FRACK retries → status=0 → close); BUSY flag insendReply.statuson large writes;Duplicate socketon duplicate listen; handle namespace global across connections. The fixture fails loudly if Docker isn't reachable — there's no silent skip, so a green run actually means the integration paths were exercised.