feat(app): add stdin-managed targets and per-target HMAC #136

Merged
Lochnair merged 4 commits from feature/targets-stdin into main 2026-09-01 04:06:19 +00:00
Owner

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:

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:

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.

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

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
## 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 ```
Lochnair added 4 commits 2026-09-01 03:53:01 +00:00
Lochnair merged commit a2f542bb97 into main 2026-09-01 04:06:19 +00:00
Lochnair deleted branch feature/targets-stdin 2026-09-01 04:06:20 +00:00
Sign in to join this conversation.