implement ChannelManager with SSH session management, channel ops, and reconnection

This commit is contained in:
2026-06-02 10:44:21 +00:00
parent bf8233af61
commit d6a49a07d7
5 changed files with 496 additions and 16 deletions

View File

@@ -1,7 +1,7 @@
---
id: client/channel-manager
name: Implement ChannelManager — SSH session management, channel opens, reconnection
status: pending
status: done
depends_on:
- auth/client-auth-handler
- transport/trait-and-types
@@ -32,18 +32,18 @@ Reconnection is always enabled. The backoff caps at 30 seconds and continues ind
## Acceptance Criteria
- [ ] `crates/wraith-core/src/client/channel_manager.rs` exports `ChannelManager`
- [ ] `ChannelManager` holds: `Arc<Transport>`, `Arc<ClientAuthConfig>`, `Arc<client::Handle<ClientHandler>>` (behind RwLock for reconnection)
- [ ] `ChannelManager::new()` establishes initial transport connection, authenticates, returns manager
- [ ] `open_direct_tcpip(host, port)` — opens SSH channel to target
- [ ] `request_tcpip_forward(addr, port)` — sends `tcpip_forward` request
- [ ] `cancel_tcpip_forward(addr, port)` — sends `cancel_tcpip_forward` request
- [ ] Reconnection detection: monitors `handle.is_closed()`, triggers reconnect on failure
- [ ] Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (cap), continues indefinitely
- [ ] Full reconnect: new transport stream, new SSH session over it (ADR-004)
- [ ] After reconnect: port forward listeners (`-L`, `-R`) re-registered with new session
- [ ] In-flight connections on old session fail gracefully (channel errors, not session-wide)
- [ ] Unit tests: channel open, reconnection trigger, backoff timing, forward re-registration
- [x] `crates/wraith-core/src/client/channel_manager.rs` exports `ChannelManager`
- [x] `ChannelManager` holds: `Arc<Transport>`, `Arc<ClientAuthConfig>`, `Arc<client::Handle<ClientHandler>>` (behind RwLock for reconnection)
- [x] `ChannelManager::new()` establishes initial transport connection, authenticates, returns manager
- [x] `open_direct_tcpip(host, port)` — opens SSH channel to target
- [x] `request_tcpip_forward(addr, port)` — sends `tcpip_forward` request
- [x] `cancel_tcpip_forward(addr, port)` — sends `cancel_tcpip_forward` request
- [x] Reconnection detection: monitors `handle.is_closed()`, triggers reconnect on failure
- [x] Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (cap), continues indefinitely
- [x] Full reconnect: new transport stream, new SSH session over it (ADR-004)
- [x] After reconnect: port forward listeners (`-L`, `-R`) re-registered with new session
- [x] In-flight connections on old session fail gracefully (channel errors, not session-wide)
- [x] Unit tests: channel open, reconnection trigger, backoff timing, forward re-registration
## References
@@ -52,8 +52,13 @@ Reconnection is always enabled. The backoff caps at 30 seconds and continues ind
## Notes
> To be filled by implementation agent
- Converted `client.rs` (single file) to directory module: `client/mod.rs` + `client/channel_manager.rs`
- Used `russh::keys::PrivateKey` and `russh::keys::PublicKey` (not the nonexistent `russh::key::KeyPair`)
- Reconnection monitor runs as a spawned tokio task that polls `handle.is_closed()` every 1s
- On reconnect: creates new transport stream + new SSH session (ADR-004 full reconnect)
- `ForwardRequest` struct tracks registered port forwards for re-registration after reconnect
- In-flight channels on old session naturally fail with `ChannelError::ChannelClosed` since the handle is replaced
## Summary
> To be filled on completion
Implemented `ChannelManager` in `crates/wraith-core/src/client/channel_manager.rs` with SSH session management, channel opens (`open_direct_tcpip`), port forward requests (`request_tcpip_forward`/`cancel_tcpip_forward`), and automatic reconnection with exponential backoff (1s→30s cap). Full reconnect per ADR-004 creates new transport stream + new SSH session. Port forwards are re-registered after successful reconnect. 8 unit tests covering backoff timing, forward tracking, transport failure, and reconnection detection.