Files
webnet/packages/sftp
codingetandClaude b4886507bb
CI / lint (pull_request) Successful in 1m9s
CI / format (pull_request) Successful in 1m9s
CI / install (pull_request) Successful in 5m40s
CI / typetest (pull_request) Successful in 59s
CI / node-tests (pull_request) Failing after 1m49s
CI / typecheck (pull_request) Successful in 1m51s
CI / browser-tests (pull_request) Successful in 3m3s
test(sftp): show the in-flight cap stalling the peer, not buffering for it
The pipelining test only proved requests still complete under the cap. It
did not show the peer being slowed, which is the whole claim: with the cap
at one and every handler parked, the client's send() blocks on the channel
window and finishes once the handlers are released. Raising the cap to 64
makes the test fail, so the stall comes from the cap rather than the gate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 01:13:09 +00:00
..

@webnet/sftp

SFTP (protocol version 3) client and server built on @webnet/ssh and @webnet/vfs.

SFTPClient implements AsyncVFS (stat, readdir, readFile/readFileRange, writeFile, delete, mkdir, move) over an SFTP session. It can be created three ways: SFTPClient.connect runs SFTP as a new subsystem channel on an SSHClientConnection the caller owns, SFTPClient.fromChannel runs it over a subsystem channel the caller already opened, or new SFTPClient({ dialer, host, user, ... }) dials its own SSH connection. SFTPServer drives a full SSH server (via @webnet/ssh) and serves an AsyncVFS over the SFTP subsystem to accepted connections, with optional per-login authenticate to select a different VFS per credential; serveSFTPChannel runs the SFTP server protocol directly over a Channel for callers who already have an authenticated SSH connection. Errors surface as SFTPError, and SSH_FX holds the SFTP status codes.

Entry points

Entry point Description
@webnet/sftp SFTPClient, SFTPServer, serveSFTPChannel, SFTPError, SSH_FX, and their option types.
@webnet/sftp/client SFTPClient and its option types only.
@webnet/sftp/server SFTPServer, serveSFTPChannel, and their option types only.

_internals entry points are unstable and are not part of the public API.

Limits

The server bounds what one session can be made to hold. SFTP_LIMITS holds the defaults; SFTPServerOptions and the third argument to serveSFTPChannel override them per session. The SSH connection underneath applies its own limits.

What is bounded Default Option When the peer exceeds it
Requests dispatched at once 64 maxInflightRequests The server stops reading until a reply goes out.
Handles open per session 256 maxOpenHandles SSH_FXP_OPEN and SSH_FXP_OPENDIR fail with SSH_FX_FAILURE.
Bytes one SSH_FXP_READ returns 256 KiB maxReadLength The reply is clamped, which is a legal short read.
Bytes in one inbound packet 1 MiB The session fails.

Pipelining is how SFTP clients are meant to reach full speed, so exceeding the in-flight cap throttles the peer through the SSH channel window instead of failing the session. The read cap matters because the packet-size limit bounds the request, not the reply it asks for: without it, a single 4 GiB SSH_FXP_READ is a 4 GiB allocation.

Usage

Client

import { SFTPClient } from "@webnet/sftp/client"
import type { RawDialer } from "@webnet/transport"

declare const dialer: RawDialer

const client = new SFTPClient({ dialer, host: "example.com", user: "u", password: "p" })
const entries = await client.readdir("/home/u")
const stream = await client.readFile("/home/u/file.txt")
await client.close()

Server

import { SFTPServer } from "@webnet/sftp/server"
import type { RawListener } from "@webnet/transport"
import type { AsyncVFS } from "@webnet/vfs"

declare const vfs: AsyncVFS
declare const listener: RawListener

const server = new SFTPServer({ vfs })
await server.listen(listener)

See also

  • @webnet/ssh — the SSH connections, channels, and subsystems this package runs over
  • @webnet/vfs — the AsyncVFS interface this package implements and serves
  • @webnet/ftp — an alternative file-transfer protocol using FTP/FTPS instead of SSH