Skip to content

feat: add sftp, port forwarding and jump hosts - #3

Merged
iscekic merged 3 commits into
mainfrom
feat/sftp-forwarding
Nov 13, 2026
Merged

iscekic merged 3 commits into
mainfrom
feat/sftp-forwarding

Conversation

@iscekic

@iscekic iscekic commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

Adds file transfer, port forwarding and jump hosts to the SSH session.

  • SFTP: list, stat, realpath, mkdir, rename, remove, and streaming upload/download with throttled progress and cancellation. A cancelled or failed transfer leaves no file at the destination.
  • Forwarding: local forwards (device listener to a remote destination) and remote forwards (server listener to a device destination), each stopped through its handle.
  • Jump hosts: connect through an existing session; chains compose.

Verified: Rust unit tests and the container suites (SFTP paths, a 50 MB round trip with matching checksums and monotonic progress, cancellation, local and remote forwards, two- and three-hop bastion chains). On an Android emulator, the example lists a remote directory, downloads a file into the app sandbox and fetches through a local forward. The macOS CI job verifies the iOS build.

`SshSession.open_sftp()` returns an `SftpClient` with list, stat, realpath,
mkdir, rename, remove_file and remove_dir, plus chunked upload and download.
Transfers stream in 256 KiB pieces, report progress at most ten times a
second, honour a cancel token, and stage through `<path>.part` so an
interrupted transfer never leaves a truncated file at the destination.

`start_local_forward` binds on the device and opens a direct-tcpip channel per
connection; `start_remote_forward` requests a server listener and pumps each
forwarded-tcpip channel to a device destination. Both return a handle whose
`stop()` closes the listener and its connections, and report per-connection
failures through a `ForwardSink`.

`SshSession.connect_via` runs SSH over a direct-tcpip channel of an
authenticated session, so chains of bastions compose.

The new categories NotFound, PermissionDenied, Cancelled, Forwarding and
LocalIo join the stable error union, and the Kotlin, Swift and TypeScript
layers expose all of it. The integration fixtures gain an http role, a bastion
and a target container on a private network, and a forwarded-tcpip port.
…sinks

`ForwardStartRecord` now assigns its fields instead of using the memberwise
initializer, matching `ExecOutputRecord`. The SFTP entry mapping moved next to
the record, so `SftpEntryRecord` keeps the synthesized initializers Expo needs,
and the progress and forward sinks are public classes with public conforming
methods.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​async-trait@​0.1.9210010093100100
Addedcargo/​russh-sftp@​3.0.110010093100100

View full report

@iscekic
iscekic merged commit fa78f62 into main Nov 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant