Endpoint identity documented the IPv6 scope identifier as an established component alongside family, address and port. The server specification does list the zone, but its remaining-unknowns section records that multi-zone behavior could not be tested on a single-host platform, so no evidence settles how a reference server compares scoped addresses. Presenting the choice as verified compatibility overstated what is known, which the crate's own guidance forbids. Say what it is instead: a deliberate policy taken under a recorded unknown. Two peers reachable at the same link-local address in different zones are genuinely different peers, so treating the zone as identity is the conservative reading — the failure it prevents is one peer closing another's session, and the failure it risks is a scoped client having to reopen. The excluded flow label is labelled the same way, and `irtt-server` guidance now carries the rule so the echo slice reuses this comparison rather than inventing a second one. Behavior is unchanged; this is documentation and a test comment.
4.1 KiB
irtt-server guidance
Repository-wide guidance in the root AGENTS.md also applies here.
Where server behavior comes from
Server wire behavior comes from the sanitized clean server documents, not from upstream implementation material:
docs/protocol/IRTT_SERVER_PROTOCOL_SPEC.mddocs/protocol/test-vectors/SERVER_BEHAVIORAL_VECTORS.mddocs/protocol/BLACKBOX_VERIFICATION_REPORT.mddocs/protocol/IRTT_CLIENT_PROTOCOL_SPEC.mdfor the shared wire format
Never derive behavior from upstream heistp/irtt source or tests. If the clean
material does not establish a semantic, state the ambiguity instead of guessing.
Boundaries
- Protocol behavior comes from
irtt-proto.irtt-servermust not depend onirtt-clientto reuse wire logic; a needed primitive belongs inirtt-proto, where both sides get it. - Do not reimplement magic validation, flag rules, HMAC placement, request classification, token/header offsets or parameter parsing here.
- No statistics, CLI or presentation concerns.
SocketAddrbelongs here: endpoint identity is session state. It does not belong inirtt-proto.- Endpoint identity is compared field by field, not with
SocketAddrequality. Family, address and port are established components. The IPv6 flow label is excluded because it identifies no endpoint; the IPv6 zone is included as a deliberate project policy, because the specification records multi-zone behavior as untested and two peers sharing a link-local address in different zones are genuinely different peers. Reuse that one comparison rather than writing a second identity rule, and keep both choices labelled as policy rather than as verified compatibility.
Core versus runtime
- Deterministic protocol and session logic — admission, negotiation, the session table, resource decisions, packet construction — belongs in the core.
- Socket and runtime orchestration —
UdpSocket, IPv4/IPv6 listeners,recv_from/send_to, timers, expiry, shutdown,select!, socket options — belongs around the core. - The server is intentionally Tokio-native. Tokio does not need to be optional for this crate.
- Do not add a blocking server, an alternate-runtime variant, a transport trait,
a runtime abstraction layer, or a mirrored client-style API family. There is
no product requirement for any of them, and
ServerCorebeing public is not a promise of runtime independence.
Rejection is silence
Malformed, unauthenticated, unsupported and policy-refused input is discarded
without a reply and without disturbing any live session. The protocol defines no
error reply, no reset and no NACK. Reserve the error type for internal failures
on the server's own side; an inbound ProtoError must never surface as a server
failure. Hostile UDP must leave the server running.
Do not let reply behavior reveal which stage rejected a datagram — in particular, an authentication failure must be indistinguishable from an unknown token.
Resource policy
Total session and resource state must stay bounded. A single unauthenticated datagram creates a session and opens are never deduplicated, so an unbounded table is a liability. Upstream's observed absence of session and per-peer bounds is explicitly not a compatibility target and must not be reproduced.
Keep fallible preparation before irreversible state mutation: a reply is prepared before a session is inserted, so an internal failure cannot leave a half-created session.
Testing
- Drive the core with real packets built by the production encoders. Do not maintain shadow encoders for well-formed wire data.
- Hand-build packets only where the point is a payload a compliant encoder cannot produce — truncated varints, out-of-range enums, corrupted MACs.
- Token generation is the only nondeterministic part; tests inject a scripted source so identity, collisions and allocation failure are assertable. Keep that seam private.
- As the server gains behavior, prefer it over fake peers for normal compliant behavior in other crates' tests. Keep small raw/adversarial peers for intentionally malformed or non-compliant wire behavior.