fix(ssh): scope tunnel port-forward to DynamoDB Local (fixes Mongo/ES) - #499
Merged
Conversation
DynamoDB Local (endpointUrl=http://host:port) over an SSH tunnel failed
with SdkError::DispatchFailure (Connection reset by peer). Root cause:
the AWS SDK's ProxyConfig::http sends origin-form HTTP requests (not
CONNECT) to the proxy for http:// targets, but the dual-protocol tunnel
only accepts SOCKS5 (0x05) and HTTP CONNECT ('C'), so the request byte
0x50 ('P') was dropped and the connection reset.
Fix: resolve_ssh_in_place forces expose_lan for http:// endpoints, which
switches the tunnel to port-forward mode — the client talks plain HTTP
to 127.0.0.1:{local_port} and SSH forwards it to the target. HTTPS and
mongodb:// targets (Mongo/ES/AWS DynamoDB) are unaffected.
Also:
- invoke_capability accepts an optional raw config (for unsaved connect-
dialog connections), routing on config vs connection_id — unifies the
listTables entry point and removes the listTablesViaSsh dual path
- normalize_dynamo falls back to host:port for Local connections missing
endpointUrl (mirrors dynamo_test_connection)
- list_tables errors use Debug format to surface the real transport
cause instead of the generic 'dispatch failure'
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…ctive) The previous attempt set sshTunnel.exposeLan at the JSON top level, which SshConnectionConfig silently ignores (expose_lan lives in the SSH profile). This is the working fix: - config.rs: add runtime-only force_port_forward field (serde skip, not persisted, distinct from expose_lan — does NOT widen the bind address) - tunnel.rs: effective_tunnel_mode + spawn_tunnel_config honor it; spawn_tunnel/spawn_chain pass it through (multi-hop supported) - ssh_bridge: resolve_ssh_tunnel gains a force_port_forward param; callers pass scheme == "http" for the endpoint. Port-forward mode rewrites endpointUrl to http://127.0.0.1:{local_port}, so the AWS SDK talks plain HTTP to the local port and SSH forwards to the target — sidestepping the origin-form-vs-CONNECT mismatch entirely. - dynamo.rs: ProxyConfig::http -> https. ProxyConfig::http only matches http:// targets; AWS DynamoDB (https://) was silently bypassing the tunnel. - invoke_capability: normalize the raw config arg (nested auth -> flat) so unsaved connect-dialog configs carry accessKeyId; fixes Missing accessKeyId. - dynamo_test_connection: also honor http scheme for the tunnel. Multi-hop chains forward through each hop and reach the target in both schemes. Mongo (mongodb://) and ES (https://) keep SOCKS5/CONNECT. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Completes the in-progress SSH tunnel work: honors socks5Proxy and HTTP-proxy transport in the MongoDB capability client factory, normalizes MongoDB URI auth in tunnel mode, and restores Data Studio auto-scroll with stop/cancel controls on the chat panel. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Remove the runtime-only force_port_forward field from SshTunnelConfig and pass it as an explicit parameter through start_transport_layers, start_tunnel/start_chain, and spawn_tunnel. - SshTunnelConfig is pure serializable data again (no #[serde(skip)] runtime field; drops force_port_forward: false boilerplate from 5 construction sites) - No mutation of cloned layer configs in resolve_connection_target; the override flows as an explicit parameter - The "force_port_forward only applies to the last hop" invariant is now explicit in start_chain (is_last && force_port_forward) The semantic "force port-forward for DynamoDB Local only" stays at the call sites via force_port_forward_for (type == DYNAMODB && http).
listTables now passes the connection as a 4th config arg to invokeCapability (Rust resolve_ssh_in_place handles SSH from the config). Update the 4 toHaveBeenCalledWith assertions to expect the config object, and drop the obsolete listTablesViaSsh tests (the function was removed — listTables handles SSH transparently now).
Blankll
force-pushed
the
fix/ssh-tunnel-port-forward
branch
from
August 16, 2026 08:48
4be7f31 to
768e0e1
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #499 +/- ##
==========================================
+ Coverage 60.28% 60.33% +0.04%
==========================================
Files 122 122
Lines 18962 19113 +151
Branches 885 881 -4
==========================================
+ Hits 11431 11531 +100
- Misses 7268 7322 +54
+ Partials 263 260 -3
🚀 New features to boost your workflow:
|
Blankll
added a commit
that referenced
this pull request
Aug 17, 2026
…496) ## What this PR does Expands the agent-visible DBA capability surface across the three database types. ### DynamoDB (26 → 33 agent-visible tools) - Flip 12 UI-only capabilities to agent-visible: `create_table`, `delete_table`, `truncate_table`, `update_table_config`, `update/delete_gsi`, `update_ttl`, `update_pitr`, `update_streams`, `describe_ttl`, `describe_continuous_backups`, `get_table_metrics` - Add 7 new: `restore_table` (from backup + PITR), `create_backup`, `list_backups`, `describe_backup`, `describe_limits`, `list_tags`, `tag_resource` - Enriched the 12 newly-exposed descriptions with DBA-scenario guidance (中文/English) ### Elasticsearch (19 → 24) `cluster_health`, `cat_nodes`, `cat_shards`, `list_snapshots`, `restore_snapshot` ### MongoDB (30 → 32) `get_slow_queries` (currentOp with system.profile fallback), `list_users` (usersInfo) Agent-tag regression tests assert 33/24/32 capabilities are agent-visible. ## Verification - agent-tag tests: dynamo@33 / es@24 / mongo@32 green - `cargo test --lib`: green > Note: the SSH tunnel fixes live in #499. This branch currently also carries the SSH commits (duplicated with #499); it will be rebased onto #499's master before merge so only the capability commits remain. --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
SSH tunnel behavior for the three database types. The tunnel defaults to SOCKS5/CONNECT (keeps the real hostname for TLS/SNI); PortForward is forced only for DynamoDB Local (plain HTTP), where the AWS SDK emits origin-form HTTP (not CONNECT) that a SOCKS5 proxy cannot forward.
Port-forward scoped to DynamoDB Local
force_port_forwardistrueonly whentype == "DYNAMODB" && endpointUrlishttp://(force_port_forward_for).httpfor configs withoutendpointUrl, which wrongly forced MongoDB and ES into PortForward mode and broke their connections (Connection reset by peeron the rewritten127.0.0.1:port). Type-based gating plus carryingtypethroughnormalize_configfixes this — Mongo/ES never force.httpsDynamoDB (AWS), ES, and MongoDB keep SOCKS5/CONNECT.Implementation
force_port_forwardflows as an explicit parameter throughstart_transport_layers → start_tunnel/start_chain → spawn_tunnel, not a runtime-only#[serde(skip)]field onSshTunnelConfig.SshTunnelConfigstays pure serializable data; the "last-hop-only" invariant is explicit instart_chain(is_last && force_port_forward); no mutation of cloned layer configs.AWS DynamoDB through the tunnel
ProxyConfig::http → httpsin the DynamoDB client —httponly matchedhttp://targets, so AWS DynamoDB (https) silently bypassed the SOCKS5/CONNECT tunnel.httpsmatches and tunnels via CONNECT.MongoDB through the tunnel
socks5Proxyin the MongoDB capability client factory; normalize MongoDB URI auth (mongodb://u:p@host:port/db) through the SSH tunnel (single-host host/port rewrite).Verification
cargo build --lib: cleancargo test --lib: SSH / ssh_bridge / connection_resolver / dynamo / mongo_client suites greenforce_port_forward_forunit tests cover: DynamoDB-Local→force, AWS-https→no force, MongoDB→no force (even with http endpointUrl), ES/OpenSearch→no force, case-insensitivetype, missingtype→no force