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.
86 lines
4.1 KiB
Markdown
86 lines
4.1 KiB
Markdown
# `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.md`
|
|
- `docs/protocol/test-vectors/SERVER_BEHAVIORAL_VECTORS.md`
|
|
- `docs/protocol/BLACKBOX_VERIFICATION_REPORT.md`
|
|
- `docs/protocol/IRTT_CLIENT_PROTOCOL_SPEC.md` for 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-server` must **not** depend
|
|
on `irtt-client` to reuse wire logic; a needed primitive belongs in
|
|
`irtt-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.
|
|
- `SocketAddr` belongs here: endpoint identity is session state. It does not
|
|
belong in `irtt-proto`.
|
|
- Endpoint identity is compared field by field, not with `SocketAddr` equality.
|
|
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 `ServerCore` being 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.
|