Release notes¶
This page mirrors the GitHub Releases feed, with a short summary per version.
Unreleased¶
Fixed (alignment with RHPTEST-documented behaviour)¶
BindAsync/BindMessage.Localcan now omit the local address — RAW sockets bind to a port only, and supplying an address returns errCode 16, so the previous always-emitted"local"made a spec-correct RAW bind impossible.localis now an optional parameter (source-compatible).MockRhpServernow pushes astatusCONNECTED notification after a successful activeopenorconnect, matching real xrouter's lifecycle — previously code awaiting Connected against the mock hung forever.MockRhpServernotificationseqnonow starts at 0 (was 2), matching the contract RHPTEST asserts.- Doc comments: errCode 17 is intentional (RHPTEST asserts it; the
same condition on DGRAM returns 7), and
listenon RAW sockets sets trace flags.
0.3.0 — client robustness¶
Released 2026-06-11.
One behaviour change worth knowing about before upgrading: sends larger
than 8100 characters now throw immediately (see MaxSendDataLength
below) instead of hanging forever against real xrouter. Everything else
is defensive hardening and test surface.
Fixed¶
- A throwing user event handler (
Received,Accepted, etc.) no longer faults the read loop — previously one misbehaving subscriber tore down the whole RHP connection and failed every in-flight request. - Undecodable frames now surface their raw text through
UnknownReceived(UnknownMessage.Raw["raw"]) instead of an empty placeholder, so consumers can log something actionable. UnknownMessageno longer throws on forward-compatible frames whoseid/seqnoaren't numeric.
Added¶
RhpClient.MaxSendDataLength(default 8100): client-side guard against xrouter's silent drop ofsend.dataabove ~8 KB (issue #7). Oversized sends now throwArgumentExceptioninstead of hanging the awaiting caller forever. Set tonullto disable.MockRhpServer.BroadcastRawAsyncfor injecting raw / malformed frames in tests.
0.2.2 — first NuGet.org publish¶
Released 2026-05-04.
Same source as 0.2.1; re-cut to exercise the full release flow now
that the NUGET_API_KEY repository secret is configured. The
RhpV2.Client package is published to https://www.nuget.org/ from
this version onward.
0.2.1 — real-xrouter integration¶
Released 2026-05-04.
This release is mostly about driving the library against real XRouter (via a Testcontainers-based integration suite) and aligning the wire format with what the live server actually emits. Two breaking changes; everything else is additive.
Breaking¶
AcceptMessage.Portis nowstring?(wasint?). Real XRouter sendsaccept.portas a JSON string, not the unquoted number the PWP-0222 example shows. The library reads either shape via aStringOrIntConverter. If you were readingAcceptMessage.Portasint?, switch tostringand parse if you need an integer.QueryStatusAsyncnow returnsTask<StatusFlags>(wasTask<StatusReplyMessage>) and accepts an optionalresponseTimeout. The previous shape hung in the success case because per spec the server replies to a successful status query with astatusnotification (no requestid), not astatusReply. The new method races the notification path against the error path and throwsRhpProtocolExceptionon timeout.
Added¶
RhpV2.Clientnow multi-targetsnet8.0andnet10.0. The package ships bothlib/net8.0/andlib/net10.0/assemblies; consumers on .NET 8, 9 or 10 pick up a matching one via NuGet's TFM resolution. The CLI (RhpV2.Tools) stays single-targeted on net10.0 since it's a self-contained binary, not a library.release.ymlgained apublish-nugetjob that pushes the packed.nupkgto nuget.org on tag-triggered runs. The push is gated on theNUGET_API_KEYrepository secret — if it's not set, the job logs a warning and exits cleanly so the rest of the release flow still completes (so the workflow can be exercised before the key is configured).- New
RhpV2.Client.IntegrationTestsproject: drives the publishedghcr.io/packethacking/xrouterimage via Testcontainers to pin client behaviour against a real RHP server. Includes a two-container fixture connected by AXUDP that exercises the full data path (RhpClient → RHP → AX.25 L2 → AXUDP → peer node) — real SABM/UA handshake, real I-frame send/recv, real orderly close. Requires a running Docker daemon; the fixtures fail loudly on startup error rather than silently skipping, so a green run actually means the integration paths were exercised. RhpErrorCode.NotConnected(17): real XRouter returns this onsendagainst a stream socket whose downlink is not connected. The PWP-0222 / PWP-0245 error tables stop at 16; added to the client's enum andText(...)lookup.RecvMessageextensions:Tseq,Ilen,Pid,Ptclfor TRACE-mode I-frames, plusLocal/Remotefor DGRAM-mode receive addressing.StringOrIntConverterfor fields that are wire-typed inconsistently across modes (accept.port,recv.port— string in DGRAM, number in TRACE).
Fixed / aligned with reality¶
connectReplyworkaround: real XRouter returns a successfulconnectReplywitherrCode = handle(rather than 0) buterrText = "Ok". The library now treats anyconnectReplywhose text is"Ok"as success regardless of the numeric code so callers don't see spuriousRhpServerExceptionthrows on a working AX.25 connect. Real failures ("No Route","Not bound", etc.) still raise as before.- Wire-format alignment: every reply now serialises
errCode/errTextwith capital C/T, matching what XRouter actually emits. The published spec only mentions this as an AUTHREPLY quirk, but integration testing confirmed it applies to every reply. Reads remain case-insensitive so older / lowercase wire forms still parse. - Mock alignment:
MockRhpServerno longer echoes the requestidon notification-shaped replies (anything carrying aseqno), matching real XRouter's wire behaviour.
Integration coverage¶
The integration suite verifies (against a live XRouter container):
- AX.25 stream connect / send / recv / close end-to-end across AXUDP — real SABM/UA/I/RR exchange.
- Passive listener accepts an inbound peer connection;
peer-initiated close fires the listener-side
Closedevent. - TRACE-mode listener captures real frames with decoded fields.
- RAW-mode listener surfaces complete on-the-wire AX.25 bytes.
- DGRAM
sendtowith byte-perfect binary round-trip. - NetRom stream connect routing through AX.25 to the peer node.
pfam=inetstream HTTP/1.0 GET to XRouter's own HTTP server, including server-initiated close.seqpkt/customsocket allocation;dgramlisten rejection.- Connect-to-unreachable lifecycle:
connectReplyok →status flags=0→closeafter FRACK retries. - BUSY flag in
sendReply.statuson multi-KB writes; duplicate-listen detection; concurrent streams on one RHP TCP.
Documentation¶
- Protocol primer extended with the spec-vs-reality deltas
surfaced by the integration suite (filed as issues #2–#7):
every reply uses capital
errCode/errText,connectReply.errCodemirrorshandleon success,accept.portis wire-typed as a string,recv.portdiffers between TRACE and DGRAM, undocumentederrCode 17 "Not connected", the ~8 KBsend.datacliff above which XRouter silently drops the request, the global handle namespace, the post-bad-auth lockout, and theRHPPORT=9000config requirement. - Initial public docs site (mkdocs-material) wired up at https://rhp2lib.pages.dev/.
0.1.0 — initial cut¶
RhpV2.Clientlibrary targetingnet10.0:- 2-byte big-endian framing.
- Strongly-typed DTOs for all 22 RHPv2 message types.
RhpClientwith async request/reply correlation and event-style notifications (Received,Accepted,StatusChanged,Closed,Disconnected,UnknownReceived).- Tolerates spec quirks (
errCodevserrcode,ConnectReplyPascalCase) on read.
MockRhpServershipped with the library.rhpCLI withprobe,chat,mon,send,serve.- CI matrix on ubuntu / windows / macOS.
- Release workflow that publishes self-contained single-file binaries
for
linux-x64,linux-arm64,linux-musl-x64,win-x64,win-arm64,osx-x64,osx-arm64, and packs the NuGet. - 31-test xunit suite.