Add dynamic target management to irtt-client continuous mode through --targets-stdin, and extend the shared target syntax with per-target HMAC
authentication.
This builds on the existing managed-client target replacement machinery rather
than introducing a separate dynamic-client path.
Target parsing is deferred until preparation so malformed arguments containing
HMAC material are not echoed through Clap diagnostics. New target/auth debug
representations also redact key contents.
Dynamic targets from stdin
irtt-client --targets-stdin is available with continuous mode
(--duration 0).
Each non-empty stdin record declares the complete desired target set rather
than a delta. For example:
ams=ams.example:2112,sg=sg.example:2112
followed by:
sg=sg.example:2112,nyc=nyc.example:2112
retires ams, preserves the unchanged sg target, and starts nyc.
Additional behavior:
positional targets form the initial desired set
an empty initial set is allowed
[] selects an empty desired set without stopping the controller
blank records are ignored
EOF requests graceful shutdown
peer closure is target-local rather than terminating the entire stdin-managed run
unchanged target configurations retain their managed generation
changed address or authentication creates a fresh generation
Bounded latest-wins updates
Dynamic target management remains bounded under arbitrary target churn:
maximum desired target set: 128
maximum live target generations: 256
maximum stdin record payload: 64 KiB
bounded managed outcome and frontend deduplication history
one logical latest unapplied desired set
If a replacement temporarily exceeds the live-generation limit because older
generations are still draining, the update is retained for retry rather than
treated as fatal.
Newer unapplied desired state supersedes an older capacity-blocked revision, so
the client converges toward the controller's latest state instead of replaying
stale intermediate sets.
Malformed input and non-capacity managed update failures remain fatal.
EOF deliberately takes precedence over unapplied state: a revision that has
been read but not yet acknowledged as applied may be discarded when the stdin
control plane closes.
Continuous statistics
Dynamic stream accounting is generation-aware.
Statistics are keyed by TargetInstance, preventing measurements from a
replaced endpoint or authentication configuration from being combined with its
predecessor.
Retired generations and terminal bookkeeping are pruned so continuous mode
retains its bounded-memory behavior. Final summaries include only the bounded
set of generations retained at shutdown rather than lifetime target history.
Blocking managed updates
Add ManagedCommandReceipt::blocking_wait() for synchronous managed-client
callers.
This lets the blocking stream frontend wait for atomic target-update
acknowledgements without introducing another Tokio runtime in the application
layer.
## Summary
Add dynamic target management to `irtt-client` continuous mode through
`--targets-stdin`, and extend the shared target syntax with per-target HMAC
authentication.
This builds on the existing managed-client target replacement machinery rather
than introducing a separate dynamic-client path.
## Per-target HMAC
Targets now support:
```text
TARGET
LABEL=TARGET
TARGET@hmac=KEY
LABEL=TARGET@hmac=KEY
LABEL=TARGET@hmac=
````
Authentication semantics are:
* no `@hmac=`: inherit the global `--hmac` value
* `@hmac=KEY`: override HMAC for that target
* `@hmac=`: explicitly disable HMAC for that target
The syntax is shared by `irtt-client` and `irtt-tui`.
Target parsing is deferred until preparation so malformed arguments containing
HMAC material are not echoed through Clap diagnostics. New target/auth debug
representations also redact key contents.
## Dynamic targets from stdin
`irtt-client --targets-stdin` is available with continuous mode
(`--duration 0`).
Each non-empty stdin record declares the complete desired target set rather
than a delta. For example:
```text
ams=ams.example:2112,sg=sg.example:2112
```
followed by:
```text
sg=sg.example:2112,nyc=nyc.example:2112
```
retires `ams`, preserves the unchanged `sg` target, and starts `nyc`.
Additional behavior:
* positional targets form the initial desired set
* an empty initial set is allowed
* `[]` selects an empty desired set without stopping the controller
* blank records are ignored
* EOF requests graceful shutdown
* peer closure is target-local rather than terminating the entire stdin-managed run
* unchanged target configurations retain their managed generation
* changed address or authentication creates a fresh generation
## Bounded latest-wins updates
Dynamic target management remains bounded under arbitrary target churn:
* maximum desired target set: 128
* maximum live target generations: 256
* maximum stdin record payload: 64 KiB
* bounded managed outcome and frontend deduplication history
* one logical latest unapplied desired set
If a replacement temporarily exceeds the live-generation limit because older
generations are still draining, the update is retained for retry rather than
treated as fatal.
Newer unapplied desired state supersedes an older capacity-blocked revision, so
the client converges toward the controller's latest state instead of replaying
stale intermediate sets.
Malformed input and non-capacity managed update failures remain fatal.
EOF deliberately takes precedence over unapplied state: a revision that has
been read but not yet acknowledged as applied may be discarded when the stdin
control plane closes.
## Continuous statistics
Dynamic stream accounting is generation-aware.
Statistics are keyed by `TargetInstance`, preventing measurements from a
replaced endpoint or authentication configuration from being combined with its
predecessor.
Retired generations and terminal bookkeeping are pruned so continuous mode
retains its bounded-memory behavior. Final summaries include only the bounded
set of generations retained at shutdown rather than lifetime target history.
## Blocking managed updates
Add `ManagedCommandReceipt::blocking_wait()` for synchronous managed-client
callers.
This lets the blocking stream frontend wait for atomic target-update
acknowledgements without introducing another Tokio runtime in the application
layer.
## Documentation
Update `irtt-client.md` with:
* stdin desired-state semantics
* latest-wins behavior
* EOF and empty-set handling
* target and generation limits
* target escaping rules
* per-target HMAC inheritance/override/disable behavior
* stable-label guidance for dynamic targets
* a persistent FIFO controller example
## Verification
```text
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
git diff --check origin/main...HEAD
```
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
Add dynamic target management to
irtt-clientcontinuous mode through--targets-stdin, and extend the shared target syntax with per-target HMACauthentication.
This builds on the existing managed-client target replacement machinery rather
than introducing a separate dynamic-client path.
Per-target HMAC
Targets now support:
Authentication semantics are:
@hmac=: inherit the global--hmacvalue@hmac=KEY: override HMAC for that target@hmac=: explicitly disable HMAC for that targetThe syntax is shared by
irtt-clientandirtt-tui.Target parsing is deferred until preparation so malformed arguments containing
HMAC material are not echoed through Clap diagnostics. New target/auth debug
representations also redact key contents.
Dynamic targets from stdin
irtt-client --targets-stdinis available with continuous mode(
--duration 0).Each non-empty stdin record declares the complete desired target set rather
than a delta. For example:
followed by:
retires
ams, preserves the unchangedsgtarget, and startsnyc.Additional behavior:
[]selects an empty desired set without stopping the controllerBounded latest-wins updates
Dynamic target management remains bounded under arbitrary target churn:
If a replacement temporarily exceeds the live-generation limit because older
generations are still draining, the update is retained for retry rather than
treated as fatal.
Newer unapplied desired state supersedes an older capacity-blocked revision, so
the client converges toward the controller's latest state instead of replaying
stale intermediate sets.
Malformed input and non-capacity managed update failures remain fatal.
EOF deliberately takes precedence over unapplied state: a revision that has
been read but not yet acknowledged as applied may be discarded when the stdin
control plane closes.
Continuous statistics
Dynamic stream accounting is generation-aware.
Statistics are keyed by
TargetInstance, preventing measurements from areplaced endpoint or authentication configuration from being combined with its
predecessor.
Retired generations and terminal bookkeeping are pruned so continuous mode
retains its bounded-memory behavior. Final summaries include only the bounded
set of generations retained at shutdown rather than lifetime target history.
Blocking managed updates
Add
ManagedCommandReceipt::blocking_wait()for synchronous managed-clientcallers.
This lets the blocking stream frontend wait for atomic target-update
acknowledgements without introducing another Tokio runtime in the application
layer.
Documentation
Update
irtt-client.mdwith:Verification