Commit Graph
671 Commits
Author SHA1 Message Date
Nils Andreas Svee 2521959364 Merge pull request #127 from Lochnair/docs/release-readiness-audit-fixes
docs: implement release-readiness audit findings
2026-08-20 15:33:08 +07:00
Nils Andreas Svee 1754b77ea5 docs: scope kernel-timestamp claim to the Tokio server runtime
irtt_server::ServerCore::handle_datagram is a public API that
always passes kernel_rx_timestamp = None; only the crate-private
entry point the Tokio runtime (the irtt-server applet) uses
supplies one. Say so explicitly instead of implying every use of
this crate's server gets the preference.
2026-08-20 15:24:43 +07:00
Nils Andreas Svee cab1fd1aab docs: scope server kernel-timestamp claim to this crate's server
irtt-client can talk to any IRTT-compatible peer, not only this
repo's own irtt-server, and has no way to know what clock source
produced a peer-reported wall-clock endpoint. Restrict the
kernel-receive-timestamp-preference claim to when the peer is
this crate's own Linux irtt-server; state plainly that another
peer's reported value is used as-is otherwise.
2026-08-20 15:18:48 +07:00
Nils Andreas Svee c9269ca0ee docs: qualify RTT-unaffected claim to raw RTT only
adjusted_rtt_us/effective_rtt_us are derived in part from
server-reported processing time, which is computed from the same
server receive endpoint the client_to_server one-way-delay field
documents as kernel-preferred. Only raw RTT is guaranteed to stay
on userspace monotonic timing; say so precisely instead of
claiming RTT in general is unaffected.
2026-08-20 15:12:01 +07:00
Nils Andreas Svee 98e7231ede fix(app): gate feature-specific formatting helpers
RUSTFLAGS="-D warnings" cargo check -p irtt-rs --no-default-features
--features client (or --features tui) warned on dead code: some
format.rs helpers are only called from the client applet, others
only from the TUI. Narrowly #[cfg(feature = "...")]-gate each to
the applet that actually uses it instead of allowing the warning
or introducing a new feature, and split the tests that exercised
both groups to match.
2026-08-20 14:57:02 +07:00
Nils Andreas Svee a0b51032f2 docs: document update_targets() replacement semantics
ManagedClientHandle::update_targets() had a one-line rustdoc that
didn't say targets is a full desired-set replacement, that an
unchanged live target continues undisturbed, or that a changed
config or a terminal-generation resubmission starts a fresh
generation even for byte-identical config.
2026-08-20 14:56:56 +07:00
Nils Andreas Svee 13a907bc28 docs: document Linux kernel-timestamp preference for OWD
On Linux, one-way delay measurement best-effort prefers kernel
send/receive timestamps over userspace timing, falling back
whenever a kernel reading is unavailable or implausible. RTT is
unaffected and remains userspace-timed.

Add a user-facing README note near the measurement/output
explanation, and bring OneWayDelaySample::client_to_server's
rustdoc in line with the send-endpoint behavior, matching the
style already used for server_to_client. Also fix a stale
socket_io module doc that still claimed the captured kernel
receive timestamp went unconsumed; ServerCore has used it via
preferred_receive_wall_ns since d3f93a9.
2026-08-20 14:56:50 +07:00
Nils Andreas Svee 1ce65c0db4 docs: note that final target summaries follow CLI order
Final per-target summaries print in the order targets were
supplied on the command line, not finish order or alphabetical
label order. This only concerns the end-of-run summary printout;
it says nothing about ordering of live/interleaved per-event
output.
2026-08-20 14:56:34 +07:00
Nils Andreas Svee 90178cfb75 docs: point installation and library usage at crates.io
The README led application users through cloning the repo and
building with cargo install --path, and led library consumers
through a local path dependency, even though irtt-rs and
irtt-client are now published. Make the registry the default
path for both and keep the local-checkout/path forms documented
as the alternative for development.
2026-08-20 14:56:22 +07:00
Nils Andreas Svee 4928661467 docs: clarify testing ownership and cost 2026-08-20 14:20:49 +07:00
Nils Andreas Svee 65eb494f7a test(app): yeet even more nonsense tests dreamt up by the criminally insane 2026-08-20 14:02:47 +07:00
Nils Andreas Svee 08a9bc4e63 test(app): remove applet dispatch integration tests
This is utter nonsense dreamt up by insane clankers
2026-08-20 13:48:41 +07:00
Nils Andreas Svee 3c676edb38 Merge pull request #125 from Lochnair/docs/continuous-mode-memory
docs: describe continuous-mode memory bounds
2026-08-20 13:44:34 +07:00
Nils Andreas Svee e936d8147d docs: describe continuous-mode memory bounds 2026-08-20 13:40:21 +07:00
Nils Andreas Svee d51e154783 build(app): mirror release profile from workspace
because cargo's workspace profiles are not inherited when doing cargo install
2026-08-20 12:41:48 +07:00
Nils Andreas Svee 02bad76676 build: optimize release and distribution profiles
Remove the panic abort override and synchronize LTO settings across release and distribution profiles to ensure consistent binary optimization and debugging behavior.
2026-08-20 12:41:16 +07:00
Nils Andreas Svee 0e23017100 Merge pull request #124 from Lochnair/refactor/application-binaries
refactor(app): clarify application and binary roles
irtt-rs/v0.6.0
2026-08-20 12:09:10 +07:00
Nils Andreas Svee d71503e4fd fix(app): preserve featureless build support
The shared with_shutdown_flag helper, and the AtomicBool/Arc/
install_signal_handler imports it uses, only exist when at least one
applet feature (client, server, tui) is enabled. Gate them accordingly
instead of leaving them unconditionally compiled, which broke
`--no-default-features` workspace CI under -D warnings (unused
function/dead-code).

Also correct the README installation section: tui is already part of
the default feature set, so an ordinary `cargo install` already
produces irtt-tui alongside the other three binaries. Document a
lightweight `--no-default-features --features client` install instead
of the previous (incorrect) framing of tui as an opt-in extra, and fix
the same gap in the binaries/features table.
2026-08-20 12:01:07 +07:00
Nils Andreas Svee 7bea3dd54e refactor(app): clarify application and binary roles
Rename the application Cargo package from irtt-cli to irtt-rs
(crates/irtt-cli -> crates/irtt-app), and hard-rename its scriptable
client binary from irtt-cli to irtt-client, now that the application
also ships a first-class server and the old name reads as an umbrella
CLI for both roles.

Give each dedicated binary (irtt-client, irtt-tui, irtt-server) its
own entry point instead of funneling through the multicall
dispatcher's argv0/subcommand logic, so a dedicated binary only links
the applet code for the role it implements and always runs that role
regardless of the name it's invoked or copied under. irtt-rs remains
the feature-selectable multicall dispatcher and is now the only binary
that inspects argv0 or subcommands.

Move future application releases to the irtt-rs/vX.Y.Z tag namespace
(regenerated via `dist generate`), and bump the application package to
0.6.0 as a breaking distribution change. Library crate versions are
unaffected.
2026-08-20 11:44:35 +07:00
Nils Andreas Svee 7129ee0bc5 Merge pull request #123 from Lochnair/fix/cli-empty-hmac
fix(cli): reject empty HMAC keys
2026-08-20 10:48:54 +07:00
Nils Andreas Svee 75aaf68fea Merge pull request #122 from Lochnair/fix/table-target-width
fix(cli): bound target labels in table output
2026-08-20 10:48:35 +07:00
Nils Andreas Svee f470d4d7a6 Merge pull request #121 from Lochnair/fix/summary-target-order
fix(cli): preserve target order in summaries
2026-08-20 10:48:17 +07:00
Nils Andreas Svee 2cdc084ef1 fix(cli): reject empty HMAC keys
--hmac "" silently produced an empty-byte HMAC key at the protocol
boundary, so an accidentally-empty shell expansion (--hmac "$VAR")
could look like auth was deliberately enabled while actually using a
predictable empty key. Reject a zero-length --hmac value at parse
time in the client CLI, the TUI client (via the shared
CommonClientArgs), and the server CLI. Non-empty values, including
whitespace-only ones, are accepted exactly as supplied.
2026-08-20 10:43:53 +07:00
Nils Andreas Svee 5c7d75e39e fix(cli): bound target labels in table output
TABLE output formatted every column value with format!("{value:<width$}"),
which only sets a minimum width. Target is free-form/user-controlled and
is the first default table column, so a long target label pushed every
later column out of alignment while numeric/measurement columns stayed
correct.

Add a table-only truncation for Column::Target: labels within
table_width() (18) render unchanged; longer labels keep a
character-boundary-safe prefix and end with "..." so the rendered cell
is exactly 18 characters. This is scoped to format_table_cell's Target
case only, so it cannot silently shorten any measurement/data column.
CSV, TSV, and JSONL are untouched and keep emitting the full,
untruncated target label.
2026-08-20 10:42:48 +07:00
Nils Andreas Svee 6a4dce68b3 fix(cli): preserve target order in summaries
Final multi-target summaries were printed by iterating the label-keyed
BTreeMap directly, so they came out in lexicographic label order
instead of the order targets were given on the command line. Iterate
setup.targets (the canonical prepared CLI order) instead, looking up
each target's stats by label, and keep the BTreeMap only for
event-time lookup.
2026-08-20 10:42:31 +07:00
Nils Andreas Svee 0d9826800b Merge pull request #120 from Lochnair/test/session-machine-model
test(client): model SessionMachine operation sequences
2026-08-20 10:09:41 +07:00
Nils Andreas Svee 2dee315a6e Merge pull request #119 from Lochnair/test/server-set-runtime-failure
test(server): cover mid-run ServerSet listener failure
2026-08-20 10:09:19 +07:00
Nils Andreas Svee 369e166a6c Merge pull request #118 from Lochnair/test/stats-properties
test(stats): add rolling-window and IPDV property coverage
2026-08-20 10:09:01 +07:00
Nils Andreas Svee 6df16e0205 test(client): model SessionMachine open-session operation sequences
Adds a proptest-based model test that drives generated sequences of
probe sends, valid/duplicate/late/unknown/wrong-token replies, bounded
and unbounded timeout polling, peer/local close, and u32 wire-sequence
wraparound through a real SessionMachine and an independent reference
model, asserting the observable state and every emitted event match
after each operation. This combinatorial interleaving of an open
session's operations was previously untested; existing tests are all
narrow single-scenario cases.
2026-08-20 10:07:20 +07:00
Nils Andreas Svee 7c190058f4 test(server): cover mid-run ServerSet listener failure
ServerSet's startup/bind-failure paths were already covered, but nothing
exercised a listener failing after every listener had bound and was
actively serving. Inject the failure through the crate's own established
token-source test seam (the same one core::tests already uses) via a
narrow #[cfg(test)] Server::from_socket_with_core constructor, so one
listener answers a first open like its healthy sibling before its second
draw fails deterministically. Confirms ServerSet reports the failure,
shuts the sibling down, joins both tasks and releases both sockets, with
no external shutdown ever sent.
2026-08-20 10:07:19 +07:00
Nils Andreas Svee 3e2cf3bad7 test(stats): add rolling-window and IPDV property coverage
Add proptest-based generative tests for irtt-stats's stateful rolling
windows and IPDV pairing.

The rolling_count and rolling_time bounds turn out to be two
independent VecDeques (rolling.rs), not a single window bounded by
both, so the main property test proves that independence: it drives a
StatsCollector configured with both bounds simultaneously through
generated operation streams, checking after every operation that each
window matches a from-scratch reference model applying only its own
bound (and that the cumulative snapshot matches a full-history
replay). Since both the production window and the reference replay
the same events through the same deterministic aggregation code, the
comparison is exact equality, not a tolerance.

Also add two smaller property tests for IpdvTracker's bounded FIFO
state and at-most-once adjacent-pair emission under arbitrary
(including non-monotonic and duplicate) sequence streams, complementing
the existing strictly-increasing hand test.

No discrepancies between production and the reference model were
found.
2026-08-20 10:07:04 +07:00
Nils Andreas Svee a055e80fd3 Merge pull request #117 from Lochnair/test/proto-properties
test(proto): add codec property tests
2026-08-20 00:48:25 +07:00
Nils Andreas Svee 2634649852 test(proto): add codec property tests
Add proptest-based property tests for irtt-proto's varint and Params
codecs, covering the full u64/i64 domains for varint/zigzag round trips
and generated valid Params for semantic round trips, presence
omission rules, repeated-tag last-wins behavior, and unknown-tag
tolerance.
2026-08-20 00:45:16 +07:00
Nils Andreas Svee b30cf9b3a4 Merge pull request #116 from Lochnair/test/proto-fuzzing
test(proto): add decoder fuzzing
2026-08-20 00:31:40 +07:00
Nils Andreas Svee 5ce5f4fcda fix(fuzz-ci): build cargo-fuzz with stable, not the nightly override
Installing cargo-fuzz under a nightly directory override made rustix's
build script (a cargo-fuzz dependency) gate on `rustc_attrs`, which this
runner's nightly rejects as reserved-for-rustc. Install nightly without
overriding the directory default so `cargo install cargo-fuzz` still
builds under stable; only `fuzz build`/`fuzz run` opt into `+nightly`.
2026-08-19 23:36:08 +07:00
Nils Andreas Svee 719073df1c test(proto): add decoder fuzzing
Add isolated cargo-fuzz/libFuzzer infrastructure for irtt-proto, fuzzing
decode_request, decode_open_reply, decode_echo_reply, Params::decode(_with_presence),
and the varint codec against arbitrary and structurally-derived wire input.
Each target's core invariant is that arbitrary bytes must never panic; a
small clean-room seed corpus (generated from irtt-proto's own encoders) and
a bounded nightly smoke CI workflow are included. The fuzz project is its
own isolated cargo workspace and does not affect the root workspace, its
lockfile, or any published crate's dependency graph.
2026-08-19 23:33:27 +07:00
Nils Andreas Svee 9b70bd8b9d Merge pull request #113 from Lochnair/renovate/lock-file-maintenance
chore(deps): maintain cargo lockfile
2026-08-17 23:16:51 +07:00
renovate[bot] 7b7cb6042e chore(deps): maintain cargo lockfile 2026-08-17 02:55:09 +00:00
Nils Andreas Svee 444ec3dd6a Merge pull request #115 from Lochnair/fix/break-client-server-dev-dep-cycle
fix(tests): break the irtt-client/irtt-server dev-dependency cycle
irtt-cli/v0.5.0 irtt-server/v0.5.0 irtt-proto/v0.5.0 irtt-client/v0.5.0 irtt-stats/v0.5.0
2026-08-17 09:53:49 +07:00
Nils Andreas Svee 3f1ac88989 fix(deny): add a license field to irtt-integration-tests
cargo-deny's licenses check requires a license expression even on an
unpublished crate; add the workspace's Apache-2.0 license so the check
passes rather than treating irtt-integration-tests as unlicensed.
2026-08-17 09:51:03 +07:00
Nils Andreas Svee 1753e91d5f fix(tests): break the irtt-client/irtt-server dev-dependency cycle
irtt-client and irtt-server mutually dev-depended on each other for
cross-crate integration tests. That cycle blocks `cargo publish` for
either crate whenever the other doesn't already exist on the registry
at the required version, which is exactly the situation for a crate's
first-ever release (irtt-server has never been published before).

Introduce a new `irtt-integration-tests` crate (publish = false, no
library target) to own tests whose actual subject is the interaction
between two or more irtt-* production crates:

  crate-local behavior          -> crate's own tests/
  cross-crate/system behavior   -> irtt-integration-tests

Move every test that imports both irtt_client and irtt_server there:
the client-side support harness (hmac, large_packets, protocol_options,
fill_negotiation) and the server-side real-socket tests that drive a
real AsyncClient against a real Server (async_client, negotiation_policy,
fill_wire_bytes, traffic_class, wildcard_source). server_set.rs stays in
irtt-server/tests since it only uses irtt_proto as test machinery to
drive irtt-server directly, which is a normal one-directional test
dependency, not a peer relationship.

irtt-server's dev-dependency on irtt-client is dropped entirely now
that nothing under crates/irtt-server/tests needs it, which fully
breaks the cycle from that side.

irtt-client keeps a one-directional dev-dependency on irtt-server: a
handful of white-box unit tests in src/async_client/tests.rs assert on
private AsyncClient internals (socket, remote, test_hooks) while also
needing a real in-tree server for blocking/async equivalence checks, so
they can't move to an external crate without losing that access. Their
InTreeServer helper is duplicated locally rather than shared, matching
the existing precedent of irtt-cli's own tests/support/in_tree_server.rs.
2026-08-17 09:49:32 +07:00
Nils Andreas Svee e120cca9bc Merge pull request #114 from Lochnair/chore/independent-crate-versioning
chore(release): independent per-crate versioning, first 0.5.0 releases
2026-08-17 09:32:39 +07:00
Nils Andreas Svee d66b5ed8de chore(release): move to independent per-crate versioning at 0.5.0
Each workspace crate now owns its own version instead of the previous
lockstep scheme; irtt-proto, irtt-client, irtt-server, irtt-stats, and
irtt-cli all become 0.5.0 as the first independently-versioned release.
Workspace dependency requirements are bumped to match.

Add a root CHANGELOG.md with a real per-crate 0.5.0 section drawn from
archaeology of every merge since v0.4.0, not just path-filtered commits.

Move cargo-dist to a crate-qualified `irtt-cli/vX.Y.Z` tag namespace so
only irtt-cli (the one dist-able application) triggers a GitHub Release;
library crate tags (irtt-proto/irtt-client/irtt-server/irtt-stats) exist
purely to pin the source for a crates.io release and no longer risk
tripping the release workflow. Regenerate the renamed
irtt-cli-release.yml via `dist generate` and update the few other files
that referenced the old release.yml path by name. Also fix a stale
`features = ["full"]` in dist-workspace.toml left over from a feature-flag
refactor that dropped the "full" feature entirely, which was silently
breaking every dist build.

Rescope semver.yml's tag trigger to the same five crate-qualified tags
so semver checks keep running under the new convention.
2026-08-17 09:29:25 +07:00
renovate[bot] 6722093120 chore(deps): update github actions to ff2f1c6 2026-08-16 17:35:43 +00:00
Nils Andreas Svee 046e1f52f1 Merge pull request #111 from Lochnair/fix/linux-receive-eintr
fix(io): retry interrupted receives
2026-08-17 00:35:19 +07:00
Nils Andreas Svee a598dbdc5c fix(io): address review feedback on interrupted-receive retries
- Error-queue drain: an interrupted MSG_ERRQUEUE read no longer ends
  the bounded drain early as if the queue were empty (which could
  strand a still-queued TX timestamp past the point its probe is
  looked up and removed). It now reports ErrorQueueRecord::Ignored,
  consuming one attempt of the drain's existing per-call work budget
  and retrying the same slot instead.
- Fake-server recv_from test helpers (client_multi_target.rs and
  tests/support/mod.rs) now keep one absolute deadline across
  interruptions, re-applying only the remaining time on each retry
  and restoring the configured timeout afterward, mirroring the
  production blocking-receive fix these tests exist to guard rather
  than restarting the full timeout on every interruption.
2026-08-17 00:32:37 +07:00
Nils Andreas Svee ca2389174f fix(io): retry interrupted receives
Real signal delivery on Linux (observed via x86_64-musl workspace
testing) can interrupt blocking and nonblocking recv/recvmsg calls
with EINTR. Several production and test receive paths treated that as
a fatal error rather than a transient interruption:

- Client::open's blocking receive loop and Client::recv_once/
  recv_available's blocking receive returned ClientError::Socket on
  Interrupted instead of retrying within their existing timeout
  budget.
- AsyncClient::poll_open and poll_recv's nonblocking recvmsg path
  (Linux ancillary) failed the same way instead of retrying through
  the existing readiness/work-budget machinery.
- The server's ancillary receive loop could terminate the listener on
  an interrupted nonblocking recvmsg.
- The client's MSG_ERRQUEUE TX-timestamp drain turned an interrupted
  read of optional metadata into a fatal ClientError::Socket.
- Fake-server test helpers panicked or misclassified EINTR as a
  timeout instead of retrying it.

Interrupted is now retried transparently everywhere a receive can
observe it, while preserving each path's existing timing contract:

- Blocking Open reuses its already-absolute per-attempt deadline, so
  interruptions resume within the same attempt without retransmitting
  or extending the deadline.
- recv_once/recv_available compute one absolute deadline per logical
  receive (when a timeout is configured) so repeated interruptions
  cannot extend it past the configured timeout, and restore the
  socket's configured read timeout afterward so a later call in the
  same recv_available budget is unaffected.
- AsyncClient's poll_open already bounds its retry loop with
  OPEN_POLL_WORK_BUDGET; poll_recv gains an equivalent
  RECV_POLL_WORK_BUDGET, since unlike WouldBlock, Interrupted does not
  clear Tokio's readiness state, so an unbounded retry loop was
  otherwise possible under sustained signal delivery.
- The server's receive loop retries Interrupted the same way it
  already retries WouldBlock, yielding to the runtime after a bounded
  number of consecutive interruptions for the same reason.
- The error-queue drain treats an interrupted read as no record
  obtained this drain (deferring to the next natural drain point)
  rather than a socket error, matching its existing EWOULDBLOCK
  handling.

Deterministic regression tests use a small test-only injection hook
(mirroring existing send-injection test hooks) rather than real signal
timing, covering: Open completing despite interruptions with no
retransmit, Open's deadline not extending under repeated
interruptions, recv_once classifying a reply normally despite
interruptions, recv_once's timeout not extending under repeated
interruptions, and the AsyncClient equivalents for open and recv.

No protocol, session, or timeout-classification behavior changes; no
new dependencies; send-side EINTR handling is unchanged and out of
scope.
2026-08-17 00:23:24 +07:00
Nils Andreas Svee e3815fdeed Merge pull request #110 from Lochnair/fix/server-i686-timespec-test
test(server): use native timespec bounds
2026-08-16 22:06:29 +07:00
Nils Andreas Svee 0793a7e1c5 test(server): use native timespec bounds 2026-08-16 22:03:03 +07:00
Nils Andreas Svee c1d75ba4cc Merge pull request #109 from Lochnair/fix/managed-terminal-reconcile
fix(managed): restart resubmitted terminal targets
2026-08-15 22:56:44 +07:00