Adds the two remaining open-time server capability restrictions as configurable
negotiation policy:
a configurable timestamp allowance (dual, single, none);
a configurable DSCP permission (allowed or disallowed);
both exposed through irtt_server::ServerConfig and through the real irtt-server / irtt-rs server applet;
defaults preserve current behavior exactly — the controls are opt-in, and
an existing configuration negotiates what it always did.
Timestamp policy
TimestampAllowance is server policy, deliberately a separate public type from
the wire StampAt: one says what a session requested, the other what this
server is willing to provide. It defaults to Dual.
Dual preserves the requested StampAt.
Single maps Both to Midpoint and preserves Send, Receive and Midpoint. The midpoint substitution is the observed one, not an arbitrary
choice between the two instants.
None negotiates every timestamp request to StampAt::None.
Clock is never rewritten. The allowance restricts timestamp placement;
the echo layout simply carries no timestamp field once the placement is none.
Restriction runs before the effective-session executability check. That
ordering is load-bearing: it makes a timestamp-disabled session whose request
omitted Clock acceptable, where the same request is silently refused under
the default policy.
DSCP policy
Allowed by default, negotiating the requested raw value unchanged — including
values outside 0..=255, which stay in Params and are transported unmarked
as before.
--no-dscp / dscp_allowed=false negotiates raw Params.dscp to zero,
for every requested value, before the session is created. The open is never
refused over DSCP, and nothing is clamped or wrapped.
Params::encode omits a zero integer, so the reply carries no DSCP tag and
decodes back to zero.
The runtime then naturally sends the session unmarked: the negotiated params
are the single source of truth, so no extra Session policy bit and no
runtime knowledge of ServerConfig were added.
Socket-level traffic-class support and error behavior are unchanged.
Client behavior
Strict negotiation rejects the changed timestamp placement and the changed
DSCP value.
Loose negotiation accepts them and reports the existing StampAtChanged / DscpChanged restrictions, with DscpChanged keeping the
established codepoint convention (requested 46, negotiated 0).
No irtt-client production changes were required, and no new client
restriction variant was added.
Scope exclusions
No runtime or socket changes (no wildcard/pktinfo, recvmsg/sendmsg,
traffic-class application, maintenance or Server::run churn).
No ServerFill changes.
No multi-listener orchestration — that remains the next architecture slice.
irtt-server --bind 127.0.0.1:<port> --timestamp-allowance single --no-dscp
against the in-repo client requesting --tstamp both --clock both --dscp 46 --loose: the test completed successfully, and the replies carried server_midpoint_wall_ns / server_midpoint_mono_ns with no receive or send
timestamp fields and no marking — StampAt -> Midpoint, DSCP -> 0.
The same server with a strict client: the open is refused and no target
completes, as expected.
A default server with the ordinary client: no restriction, ordinary session
succeeds.
## Summary
Adds the two remaining open-time server capability restrictions as configurable
negotiation policy:
- a configurable **timestamp allowance** (`dual`, `single`, `none`);
- a configurable **DSCP permission** (allowed or disallowed);
- both exposed through `irtt_server::ServerConfig` and through the real
`irtt-server` / `irtt-rs server` applet;
- **defaults preserve current behavior** exactly — the controls are opt-in, and
an existing configuration negotiates what it always did.
## Timestamp policy
`TimestampAllowance` is server policy, deliberately a separate public type from
the wire `StampAt`: one says what a session requested, the other what this
server is willing to provide. It defaults to `Dual`.
- `Dual` preserves the requested `StampAt`.
- `Single` maps `Both` to `Midpoint` and preserves `Send`, `Receive` and
`Midpoint`. The midpoint substitution is the observed one, not an arbitrary
choice between the two instants.
- `None` negotiates every timestamp request to `StampAt::None`.
- **`Clock` is never rewritten.** The allowance restricts timestamp placement;
the echo layout simply carries no timestamp field once the placement is none.
- Restriction runs **before** the effective-session executability check. That
ordering is load-bearing: it makes a timestamp-disabled session whose request
omitted `Clock` acceptable, where the same request is silently refused under
the default policy.
## DSCP policy
- Allowed by default, negotiating the requested raw value unchanged — including
values outside `0..=255`, which stay in `Params` and are transported unmarked
as before.
- `--no-dscp` / `dscp_allowed=false` negotiates raw `Params.dscp` to **zero**,
for every requested value, before the session is created. The open is never
refused over DSCP, and nothing is clamped or wrapped.
- `Params::encode` omits a zero integer, so the reply carries no DSCP tag and
decodes back to zero.
- The runtime then naturally sends the session unmarked: the negotiated params
are the single source of truth, so no extra `Session` policy bit and no
runtime knowledge of `ServerConfig` were added.
- Socket-level traffic-class support and error behavior are unchanged.
## Client behavior
- Strict negotiation rejects the changed timestamp placement and the changed
DSCP value.
- Loose negotiation accepts them and reports the existing
`StampAtChanged` / `DscpChanged` restrictions, with `DscpChanged` keeping the
established codepoint convention (requested 46, negotiated 0).
- **No `irtt-client` production changes were required**, and no new client
restriction variant was added.
## Scope exclusions
- No runtime or socket changes (no wildcard/pktinfo, `recvmsg`/`sendmsg`,
traffic-class application, maintenance or `Server::run` churn).
- No `ServerFill` changes.
- No multi-listener orchestration — that remains the next architecture slice.
- No `irtt-proto` changes.
- No upstream rerun and no protocol-evidence edits.
## Verification
All run and passing:
```
cargo fmt --all -- --check
cargo check --workspace --all-targets --all-features --locked
cargo test --workspace --all-targets --all-features --locked
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --all-features --no-deps
cargo check -p irtt-cli --no-default-features --all-targets --locked
cargo check -p irtt-cli --no-default-features --features server --all-targets --locked
cargo test -p irtt-cli --no-default-features --features server --locked
```
Manual CLI smoke, in-repo binaries only:
- `irtt-server --bind 127.0.0.1:<port> --timestamp-allowance single --no-dscp`
against the in-repo client requesting `--tstamp both --clock both --dscp 46
--loose`: the test completed successfully, and the replies carried
`server_midpoint_wall_ns` / `server_midpoint_mono_ns` with no receive or send
timestamp fields and no marking — `StampAt -> Midpoint`, `DSCP -> 0`.
- The same server with a strict client: the open is refused and no target
completes, as expected.
- A default server with the ordinary client: no restriction, ordinary session
succeeds.
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
### 💡 Codex Review
Here are some automated review suggestions for this pull request.
**Reviewed commit:** `5be224d1b2`
<details> <summary>ℹ️ About Codex in GitHub</summary>
<br/>
[Your team has set up Codex to review pull requests in this repo](https://chatgpt.com/codex/cloud/settings/general). Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
</details>
Describe Single as limiting placement, not field count
When a client negotiates Clock::Both, Single maps StampAt::Both to Midpoint but deliberately leaves the clock unchanged, so timestamp_fields emits both midpoint_wall and midpoint_mono. The public API's “at most one timestamp field” guarantee is therefore false and can mislead callers estimating reply layout or bandwidth; describe this as one timestamp instant/placement with one field per negotiated clock, and align the CLI/README wording.
**<sub><sub></sub></sub> Describe `Single` as limiting placement, not field count**
When a client negotiates `Clock::Both`, `Single` maps `StampAt::Both` to `Midpoint` but deliberately leaves the clock unchanged, so `timestamp_fields` emits both `midpoint_wall` and `midpoint_mono`. The public API's “at most one timestamp field” guarantee is therefore false and can mislead callers estimating reply layout or bandwidth; describe this as one timestamp instant/placement with one field per negotiated clock, and align the CLI/README wording.
AGENTS.md reference: [crates/irtt-server/AGENTS.md:L273-L276](https://github.com/Lochnair/irtt-rs/blob/5be224d1b2e0cc4ad2a9f2d52f7a36ed25f6a0f9/crates/irtt-server/AGENTS.md#L273-L276)
Useful? React with 👍 / 👎.
Reworded: Single now bounds one timestamp instant, not one field, and every place that stated otherwise says the clock is untouched so a midpoint on Clock::Both still reports one field per domain — TimestampAllowance::Single and with_timestamp_allowance rustdoc, the crate-root docs, restrict_stamp_at, the --timestamp-allowance help and value doc, README, and the server AGENTS.md bullet (which now explicitly says not to document it as one field).
Reworded: `Single` now bounds one timestamp *instant*, not one field, and every place that stated otherwise says the clock is untouched so a midpoint on `Clock::Both` still reports one field per domain — `TimestampAllowance::Single` and `with_timestamp_allowance` rustdoc, the crate-root docs, `restrict_stamp_at`, the `--timestamp-allowance` help and value doc, README, and the server AGENTS.md bullet (which now explicitly says not to document it as one field).
_🤖 Addressed by [Claude Code](https://claude.com/claude-code)_
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Adds the two remaining open-time server capability restrictions as configurable
negotiation policy:
dual,single,none);irtt_server::ServerConfigand through the realirtt-server/irtt-rs serverapplet;an existing configuration negotiates what it always did.
Timestamp policy
TimestampAllowanceis server policy, deliberately a separate public type fromthe wire
StampAt: one says what a session requested, the other what thisserver is willing to provide. It defaults to
Dual.Dualpreserves the requestedStampAt.SinglemapsBothtoMidpointand preservesSend,ReceiveandMidpoint. The midpoint substitution is the observed one, not an arbitrarychoice between the two instants.
Nonenegotiates every timestamp request toStampAt::None.Clockis never rewritten. The allowance restricts timestamp placement;the echo layout simply carries no timestamp field once the placement is none.
ordering is load-bearing: it makes a timestamp-disabled session whose request
omitted
Clockacceptable, where the same request is silently refused underthe default policy.
DSCP policy
values outside
0..=255, which stay inParamsand are transported unmarkedas before.
--no-dscp/dscp_allowed=falsenegotiates rawParams.dscpto zero,for every requested value, before the session is created. The open is never
refused over DSCP, and nothing is clamped or wrapped.
Params::encodeomits a zero integer, so the reply carries no DSCP tag anddecodes back to zero.
are the single source of truth, so no extra
Sessionpolicy bit and noruntime knowledge of
ServerConfigwere added.Client behavior
DSCP value.
StampAtChanged/DscpChangedrestrictions, withDscpChangedkeeping theestablished codepoint convention (requested 46, negotiated 0).
irtt-clientproduction changes were required, and no new clientrestriction variant was added.
Scope exclusions
recvmsg/sendmsg,traffic-class application, maintenance or
Server::runchurn).ServerFillchanges.irtt-protochanges.Verification
All run and passing:
Manual CLI smoke, in-repo binaries only:
irtt-server --bind 127.0.0.1:<port> --timestamp-allowance single --no-dscpagainst the in-repo client requesting
--tstamp both --clock both --dscp 46 --loose: the test completed successfully, and the replies carriedserver_midpoint_wall_ns/server_midpoint_mono_nswith no receive or sendtimestamp fields and no marking —
StampAt -> Midpoint,DSCP -> 0.completes, as expected.
succeeds.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:
5be224d1b2ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
@@ -53,12 +53,41 @@ pub const DEFAULT_BURST_ALLOWANCE: u32 = 5;/// emits no final reply. See [`ServerConfig::with_idle_timeout`].Singleas limiting placement, not field countWhen a client negotiates
Clock::Both,SinglemapsStampAt::BothtoMidpointbut deliberately leaves the clock unchanged, sotimestamp_fieldsemits bothmidpoint_wallandmidpoint_mono. The public API's “at most one timestamp field” guarantee is therefore false and can mislead callers estimating reply layout or bandwidth; describe this as one timestamp instant/placement with one field per negotiated clock, and align the CLI/README wording.AGENTS.md reference: crates/irtt-server/AGENTS.md:L273-L276
Useful? React with 👍 / 👎.
@@ -53,12 +53,41 @@ pub const DEFAULT_BURST_ALLOWANCE: u32 = 5;/// emits no final reply. See [`ServerConfig::with_idle_timeout`].Reworded:
Singlenow bounds one timestamp instant, not one field, and every place that stated otherwise says the clock is untouched so a midpoint onClock::Bothstill reports one field per domain —TimestampAllowance::Singleandwith_timestamp_allowancerustdoc, the crate-root docs,restrict_stamp_at, the--timestamp-allowancehelp and value doc, README, and the server AGENTS.md bullet (which now explicitly says not to document it as one field).🤖 Addressed by Claude Code