Skip to content
Files SDK
Esc
navigateopen⌘Jpreview
On this page

SFTP

SFTP (SSH File Transfer Protocol) via ssh2-sftp-client. Node-only. Connect-per-operation with an injectable client for batch work; url() needs an HTTP front.

Installation

ssh2-sftp-client is an optional peer dependency of files-sdk - install alongside the SDK so the adapter’s imports resolve at runtime.

npm install files-sdk ssh2-sftp-client
pnpm add files-sdk ssh2-sftp-client
yarn add files-sdk ssh2-sftp-client
bun add files-sdk ssh2-sftp-client

Usage

SFTP via the ssh2-sftp-client library (built on ssh2). Virtual keys map to paths under a configurable root on the remote server, with a .. traversal guard. Node-only — SFTP uses raw sockets, so this adapter does not run on edge/browser/Workers runtimes.

By default the adapter opens a fresh connection per operation and closes it afterwards. For batch work, connect once and pass the client so every call reuses the same connection — you own its lifecycle.

import { Files } from "files-sdk";
import { sftp } from "files-sdk/sftp";

const files = new Files({
  adapter: sftp({
    host: "files.example.com",
    username: process.env.SFTP_USERNAME!,
    // Password or private-key auth (privateKey takes precedence):
    privateKey: process.env.SFTP_PRIVATE_KEY!,
    // passphrase: process.env.SFTP_PASSPHRASE,
    root: "/uploads", // virtual keys resolve under here; defaults to "."
  }),
});

await files.upload("reports/q1.csv", csv, { contentType: "text/csv" });
const file = await files.download("reports/q1.csv");

Auth falls back to SFTP_HOST, SFTP_USERNAME, SFTP_PASSWORD, SFTP_PRIVATE_KEY, SFTP_PASSPHRASE, and SFTP_PORT (default 22) when the matching option is omitted. Pass connectOptions to forward anything else to ssh2 (e.g. hostVerifier, algorithms, agent).

Reusing a connection

import SftpClient from "ssh2-sftp-client";

const client = new SftpClient();
await client.connect({ host: "files.example.com", username, privateKey });

const files = new Files({ adapter: sftp({ client }) });
// ...many operations over the one connection...
await client.end();

Options

PropType
host?string

SFTP host. Falls back to `SFTP_HOST`.

Typestring
port?number

Port. Falls back to `SFTP_PORT`, then `22`.

Typenumber
username?string

Username. Falls back to `SFTP_USERNAME`.

Typestring
password?string

Password authentication. Falls back to `SFTP_PASSWORD`. When both this and `privateKey` are set, ssh2 tries the key first, then the password.

Typestring
privateKey?string | Buffer

Private key (PEM) for key-based auth. Falls back to `SFTP_PRIVATE_KEY`.

Typestring | Buffer
passphrase?string

Passphrase for an encrypted `privateKey`. Falls back to `SFTP_PASSPHRASE`.

Typestring
root?string

Remote base directory. Virtual keys resolve under it; keys that escape it (e.g. `../etc/passwd`) throw `Provider`. An absolute root (`/uploads`) yields absolute paths; the default (`"."`) keeps paths relative to the connection's login directory — the common chroot/home case.

Typestring
publicBaseUrl?string

Origin used to build URLs from `url()`. When set, `url(key)` returns `${publicBaseUrl}/${key}` — useful when an HTTP server fronts the same tree. When unset, `url()` throws: SFTP serves no HTTP and has no signing primitive.

Typestring
readyTimeout?number

Connection-ready timeout in milliseconds, passed through to ssh2.

Typenumber
connectOptions?SftpClient.ConnectOptions

Extra ssh2 connect options merged into the resolved config (e.g. `hostVerifier`, `algorithms`, `agent`). Wins over the discrete fields.

TypeSftpClient.ConnectOptions
client?SftpClient

Pre-connected `ssh2-sftp-client` instance. When passed, the adapter reuses it for every call and never opens or closes a connection — the caller owns the socket lifecycle. This is the high-throughput path: connect once and inject, rather than paying a handshake per operation.

TypeSftpClient

Limitations

Connect-per-operation means a high call rate becomes a high connection rate, and SSH servers commonly cap sessions per IP - inject a pre-connected client (shown above) for batch jobs.

Compatibility

Method Status Notes
upload ⚠️ User metadata and cacheControl throw - SFTP files have no arbitrary-metadata or cache-header field. contentType is accepted for the return value but not stored (it’s inferred from the key’s extension on read). Stream bodies upload directly. Node-only (raw sockets).
download
delete
list ⚠️ Walks the directory tree recursively on every call - SFTP has no native prefix scan or pagination - and skips symlinks. prefix/limit/cursor are applied client-side over the full walk, so they’re accurate but a large tree means a full traversal per call. Content type is inferred from each key’s extension; size/lastModified come from the listing.
search ⚠️ Built on listAll — inherits this adapter’s list behavior above. Client-side key match (glob, regex, substring, exact).
head ⚠️ SFTP stores no content type, etag, or user metadata - head() infers the type from the key’s extension (or application/octet-stream) and returns no etag. size and lastModified come from stat.
exists
copy ⚠️ Read-then-write - base SFTP has no portable server-side copy, so the source is downloaded and re-uploaded over one connection. The whole object is buffered in memory; not atomic.
url Throws unless publicBaseUrl is set (an HTTP server fronting the same tree), in which case it returns <publicBaseUrl>/<key>. SFTP serves no HTTP and has no signing primitive. responseContentDisposition always throws because the HTTP-front URL cannot bind the override.
signedUploadUrl Throws - SFTP has no presigned-upload concept. Use upload(), or inject a pre-connected client for batch transfers.

Was this page helpful?