- ServeConfig: OQ-08 shell_argv / exec_wrapper_argv as Option<Vec<String>> with unset-declines doc contracts (no crate default shell/wrapper); OQ-09 session_cwd/session_env (assembly-supplied, never wire-derived); injected Arc<dyn IdentityProvider> + Arc<dyn HostKeySource> handles; optional Arc<ChannelClient> route-1 handle (absent -> tunnel rows decline even under the tunnel feature); Limits. - DialConfig: address + injected Arc<dyn KeySource> and Arc<dyn HostKeyPolicy> handles + Limits. - Limits: the bounded-resources budget model both directions carry — in-crate caps (max_concurrent_channels 64, max_exec_command_len 8192) and the russh-knob mapping with documented postures (max_auth_attempts 10 per ADR-004 decision 6; window 2 MiB / packet 32 KiB / buffer 100; inactivity 600 s always-set shape; keepalive Some(15 s)/3 client posture). - Types only: no russh Config construction (wave 2), no dialing (wave 4); no construction-time validation — unset-declines lives in the type shape (error.rs no-config-error contract). Verified: cargo test (36), cargo test --all-features, clippy (all-targets, -D warnings, default + all-features), fmt --check.
7.8 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |||
|---|---|---|---|---|---|---|---|---|---|---|
| w1-config | Wave 1 — the config surface (serve-config, dial-config, Limits) | completed |
|
moderate | low | project | implementation |
Description
Land the pure configuration types both directions implement against — the shapes every later wave's constructors take:
- Serve-config (server direction,
mapping.md/overview.md) — the assembly-supplied serving configuration: the OQ-08 fields (shell argv and exec wrapper argv, eachOption<Vec<String>>— unset → its row declines with a clean error, never a fallback, no crate default; the wrapper form is platform-neutral), theIdentityProviderhandle (Arc<dyn IdentityProvider>— the assembly injects it; the crate never resolves identities itself), the host-key source handle (the wave-1 key-source seam), the tunnelChannelClienthandle (the route-1 bridge's family-fabric entry —Option, absent → tunnel rows decline even under the feature), and theLimits.cwd/envinNegotiateRequestare assembly-supplied serve-config values, never wire-derived (OQ-09 posture). - Dial-config (client direction,
client.md§The dial surface) — address, key source (the wave-1 seam — never static keys, never env vars), theHostKeyPolicyhandle, and the bounded-resource knobs:keepalive_interval/keepalive_max(15 s / 3 default posture per the reverse-runner evidence),inactivity_timeout, window/packet knobs. Limits— the bounded-resources budget model (overview.md's table): the in-crate max-concurrent-channels cap, exec-parse length caps (consumed by the wave-3 grammar), and the russh knob mapping with documented default postures —max_auth_attemptsdefault 10 (sized for the countingnone-probe, ADR-004 decision 6),window_size/maximum_packet_size/channel_buffer_sizeset,inactivity_timeoutset (assembly-tunable). Every session carries it; no unbounded loops, no unbounded buffers.
Types only — no russh Config construction (wave 2), no dialing
(wave 4). The OQ-08 unset-declines semantics live in the type shape
(Option fields + doc contracts), not in construction-time
validation.
Acceptance Criteria
- Serve-config carries the OQ-08 fields as
Optionwith unset-declines doc contracts; no crate default shell/wrapper IdentityProvider, host-key source, andChannelClientare injected handles; the crate holds no identity/trust policy of its own- Dial-config carries key source +
HostKeyPolicy+ the keepalive/inactivity/window knobs with the documented defaults Limitscarries the in-crate caps (channels, exec-parse) and the russh knob defaults (max_auth_attempts10, keepalive 15 s / 3 client posture)- Unit tests cover default construction and the
Option-field semantics cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --checkpass
References
- docs/architecture/overview.md §Public surface, §Bounded-resources policy
- docs/architecture/mapping.md (OQ-08 rows, the data-plane/limits section)
- docs/architecture/client.md §The dial surface
- docs/architecture/open-questions.md (OQ-08, OQ-09 resolutions)
- docs/architecture/decisions/004-auth-dispositions.md (decision 6 —
the
max_auth_attemptsdefault)
Notes
Landed
src/config.rs(ServeConfig,DialConfig,Limits, four posture constants) + lib.rs re-exports. Design decisions pinned against the docs and the alkcall/russh 0.65 source:
Limitsis the single budget model both directions carry (Copy, plain tuning data): the in-crate caps (max_concurrent_channels64,max_exec_command_len8192 — numbers chosen here, documented as assembly-tunable posture constants) plus the russh-knob mapping (max_auth_attempts10 per ADR-004 decision 6;window_size2 MiB /maximum_packet_size32 KiB /channel_buffer_size100 — russh's own defaults, kept;inactivity_timeout600 s carried as plainDurationso the "set (assembly-tunable)" posture is structural — wave 2/4 map it to russh'sOptionas always-Some;keepalive_interval/keepalive_maxSome(15 s)/3 riding inside as the client-direction posture, satisfying the dial-config criterion without a duplicate knob set). russh's rekeyLimitsstay russh-enforced at their RFC-4253 defaults and are not carried (documented; wave-2 material).- Dial-config carries the knobs via its
Limitsfield — the criterion's "keepalive/inactivity/window knobs with the documented defaults" read as the budget model riding the config; the accepted criterion onLimitsitself pins that placement, so a second knob set onDialConfigwould have duplicated state.- No
Defaultfor the two configs — the required handles (Arc<dyn IdentityProvider>,Arc<dyn HostKeySource>,Arc<dyn KeySource>,Arc<dyn HostKeyPolicy>) have no crate-defaultable shape (identity resolution and trust are injection-only, per the criterion);ServeConfig::new/DialConfig::newtake them, and every tunable is apubfield +Limits::default().- **The OQ-08 fields are plain
Option<Vec<String>>pubfields
- doc contracts**; no construction-time validation (the
error.rsno-config-error contract). Two request-time check helpers exist (shell_configured/exec_wrapper_configured, plustunnel_configured) for the wave-2 dispatch to call — reading theOption, never validating.cwd/env(OQ-09 posture) carried onServeConfigasOption<PathBuf>/BTreeMap<String, String>(empty defaults; never wire-derived — doc contract).channel_clientis not feature-gated — alkcall is a base dependency and a field presence is not a capability; thetunnelfeature gate applies at wave 2 on top (absent → decline even under the feature; unfeatured → decline too). Doc contract records both gates.- Verified the alkcall 0.10 surfaces in-source (registry):
core::auth::IdentityProvideris a sync,Send + Sync + 'statictrait (dyn-compatible with no async-trait help on alkssh's side —ChannelClientand theNoopIdentityProvidertest fixture ridealkcall::channels::client/alkcall::core::auth), and the russh 0.65.0 knob names (window_size,maximum_packet_size,channel_buffer_size,inactivity_timeout,keepalive_interval,keepalive_max,max_auth_attempts) match this module's fields one-for-one.
Summary
Config surface landed:
src/config.rswithLimits(in-crate caps:max_concurrent_channels64,max_exec_command_len8192; russh knobs:max_auth_attempts10, window 2 MiB, packet 32 KiB, buffer 100, inactivity 600 s, keepalive Some(15 s)/3 — every number a named posture constant or documented ADR anchor),ServeConfig(OQ-08shell_argv/exec_wrapper_argvas unset-declinesOption<Vec<String>>, OQ-09session_cwd/session_env, injectedIdentityProvider+HostKeySource+ optionalChannelClient,limits), andDialConfig(address, injectedKeySource+HostKeyPolicy,limitswith the keepalive/inactivity/window posture riding inside). No construction-time validation, no crate defaults for the unset rows, noDefaultfor the configs (handles only). Tests: defaults carry the documented postures;Optionfields flip independently; handles hold byArc::ptr_eq; the dial's key source and policy stay fail-closed through the config. Verified:cargo test(36),cargo test --all-features,cargo clippy --all-targets -- -D warnings,cargo clippy --all-features --all-targets -- -D warnings,cargo fmt --checkall pass;taskgraph validateclean.