npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zero-transfer/sdk

v0.4.7

Published

Unified TypeScript file transfer SDK for Node.js

Downloads

2,129

Readme

ZeroTransfer is a unified, TypeScript-first file transfer SDK for Node.js. One typed API speaks to every backend you actually deploy against - classic protocols, web endpoints, object storage, cloud drives, and local disks - with streaming, resume, verification, dry-run plans, MFT-style scheduling, audit logs, and webhook delivery built in.

import { createS3ProviderFactory, createTransferClient, uploadFile } from "@zero-transfer/sdk";

const client = createTransferClient({
  providers: [createS3ProviderFactory({ region: "us-east-1" })],
});

// One call, any provider you registered above.
await uploadFile({
  client,
  localPath: "./dist/app.tar.gz",
  destination: {
    path: "/lake/bronze/app.tar.gz",
    profile: {
      provider: "s3",
      host: "data-lake-bronze",
      username: { env: "AWS_ACCESS_KEY_ID" },
      password: { env: "AWS_SECRET_ACCESS_KEY" },
    },
  },
});

Why ZeroTransfer

  • One API, every provider. Replace bespoke FTP, SFTP, S3, and cloud-drive code with a single TransferClient and provider-neutral sessions.
  • TypeScript-first. Strict types, exact optional properties, exhaustive capability discovery, and typed errors for every protocol failure mode.
  • Streaming + resume. Backpressure via stream.pipeline, byte-range downloads, multipart uploads, and cross-process resume stores for object storage.
  • Dry-run-first sync. Diff remote trees, generate TransferPlans, and review every step before any byte moves.
  • MFT batteries. Routes, cron + interval schedules, audit logs, HMAC-signed webhooks, retention policies, and approval gates that block on human sign-off.
  • Security by default. Profile redaction in every log, host-key pinning, certificate fingerprint pinning, OAuth refresh, and SecretSource adapters for vaults / env / files / commands.
  • Observable. Structured logger, redaction-safe diagnostics, immutable transfer receipts, and per-attempt history for compliance.

Install

# Batteries-included SDK (every provider):
npm install @zero-transfer/sdk

# Or pick a scoped package with a narrowed export surface:
npm install @zero-transfer/sftp
npm install @zero-transfer/s3
npm install @zero-transfer/mft

Requires Node.js >=20.

Scoped packages

ZeroTransfer publishes 14 scoped packages under the @zero-transfer npm organization. @zero-transfer/sdk is the batteries-included distribution; the other 13 are narrowly scoped packages that publish only the symbols listed in their scope page. Pick one to keep your dependency tree tight, or install the SDK if you want every provider in one go.

Every protocol-scoped package (everything except @zero-transfer/core itself) automatically pulls in @zero-transfer/core as a transitive dependency and re-exports the full core surface (createTransferClient, uploadFile, downloadFile, profiles, errors, sync planner, …). A single import { … } from "@zero-transfer/<scope>" is all you need - no separate @zero-transfer/core install. If your app uses multiple protocols, install the umbrella @zero-transfer/sdk instead of multiple scoped packages.

| Package | Summary | Docs | | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------- | | @zero-transfer/sdk | Batteries-included distribution. Every provider, every helper. | API reference | | @zero-transfer/core | Provider-neutral contracts, transfer engine, queue, profiles, errors. | Scope page | | @zero-transfer/classic | FTP + FTPS + SFTP in one install. | Scope page | | @zero-transfer/ftp | Classic FTP with EPSV/PASV streaming and REST resume. | Scope page | | @zero-transfer/ftps | Explicit + implicit FTPS with full TLS profile support. | Scope page | | @zero-transfer/sftp | SFTP with SSH key auth, known_hosts, and jump-host support. | Scope page | | @zero-transfer/ssh | Standalone SSH 2.0 transport, auth, and channel primitives (exec/subsystem). | Scope page | | @zero-transfer/http | HTTP(S) and signed-URL provider with ranged downloads. | Scope page | | @zero-transfer/webdav | WebDAV with PROPFIND listings and ranged downloads. | Scope page | | @zero-transfer/s3 | S3-compatible storage with SigV4, multipart upload, and cross-process resume. | Scope page | | @zero-transfer/google-drive | Google Drive with OAuth, folder paths, md5 checksums. | Scope page | | @zero-transfer/dropbox | Dropbox with content-hash verification. | Scope page | | @zero-transfer/azure-blob | Azure Blob Storage with SAS or AAD bearer auth. | Scope page | | @zero-transfer/mft | Routes, schedules, audit logs, webhooks, approval gates. | Scope page |

The full per-scope index lives at docs/scopes/.

Quick start

1. Connect a provider-neutral client

import { createSftpProviderFactory, createTransferClient } from "@zero-transfer/sdk";

const client = createTransferClient({
  providers: [createSftpProviderFactory()],
});

const session = await client.connect({
  provider: "sftp",
  host: "files.example.com",
  username: { env: "ZT_USER" },
  password: { env: "ZT_PASSWORD" },
  ssh: {
    knownHosts: { path: "./known_hosts" },
    pinnedHostKeySha256: "SHA256:base64-encoded-host-key-digest",
  },
});

const releases = await session.fs.list("/releases");
await session.disconnect();

2. Move a file with one call

import { uploadFile, type ConnectionProfile } from "@zero-transfer/sdk";

const sftpProfile: ConnectionProfile = {
  host: "files.example.com",
  provider: "sftp",
  username: { env: "ZT_USER" },
  ssh: {
    privateKey: { path: "./keys/id_ed25519" },
    pinnedHostKeySha256: "SHA256:base64-encoded-host-key-digest",
  },
};

await uploadFile({
  client,
  localPath: "./dist/app.tar.gz",
  destination: { path: "/releases/2026.04.28/app.tar.gz", profile: sftpProfile },
  onProgress: (event) => console.log(`${event.bytesTransferred}/${event.totalBytes ?? "?"}`),
});

The profile shape is the same provider-neutral ConnectionProfile used by client.connect(). See Connection profiles below for the full field reference and security guidance.

3. Plan a sync without touching bytes

import { createSyncPlan, diffRemoteTrees, summarizeTransferPlan } from "@zero-transfer/sdk";

const diff = await diffRemoteTrees(srcSession.fs, "/dist", dstSession.fs, "/releases/current");
const plan = createSyncPlan({
  id: "release-sync",
  diff,
  source: { provider: "sftp", rootPath: "/dist" },
  destination: { provider: "s3", rootPath: "/releases/current" },
  deletePolicy: "mirror",
});
console.table(summarizeTransferPlan(plan));

4. Schedule it as an MFT route with audit + approval

import {
  ApprovalRegistry,
  MftScheduler,
  RouteRegistry,
  ScheduleRegistry,
  createApprovalGate,
  runRoute,
} from "@zero-transfer/sdk";

const approvals = new ApprovalRegistry();
const scheduler = new MftScheduler({
  client,
  routes: new RouteRegistry([route]),
  schedules: scheduleRegistry,
  runner: createApprovalGate({
    approvalId: ({ route }) => `release:${route.id}:${Date.now()}`,
    registry: approvals,
    runner: ({ client: c, route: r, signal }) => runRoute({ client: c, route: r, signal }),
  }),
  onResult: ({ receipt }) => console.log(`Released ${receipt.jobId}`),
});

scheduler.start();

Connection profiles

Every operation that touches a remote system takes a ConnectionProfile. Profiles are provider-neutral data - you build one once and pass it to client.connect(), uploadFile(), downloadFile(), copyBetween(), MFT routes, and diagnostics. The same shape works for every provider; only the optional auth blocks (ssh, tls, oauth, s3, …) change.

Required fields

| Field | Type | Notes | | ---------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | host | string | Remote hostname / IP / bucket / drive identifier (provider-specific). Always required. | | provider | ProviderId | One of "ftp", "ftps", "sftp", "http", "https", "webdav", "s3", "azure-blob", "gcs", "google-drive", "dropbox", "one-drive", "local", "memory", or any custom id you registered. |

Optional top-level fields

| Field | Type | Notes | | ----------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------- | | port | number | Provider applies a sensible default when omitted. | | username | SecretSource | String, { env }, { path }, { base64Env }, { value }, or callback. | | password | SecretSource | Same shapes as username. Used as bearer token for cloud providers. | | secure | boolean | Request encrypted transport when the protocol allows opt-in TLS. | | tls | TlsProfile | CA bundle, mTLS cert/key, fingerprint pinning, min/max TLS version. | | ssh | SshProfile | Private key, passphrase, known_hosts, host-key pin, agent, algorithms. | | timeoutMs | number | Connection / operation timeout. | | signal | AbortSignal | Cancels connection setup and long-running operations. | | logger | ZeroTransferLogger | Per-profile structured logger override (still redaction-safe). |

Secret-bearing fields use SecretSource

Every credential field (username, password, tls.ca, tls.key, ssh.privateKey, ssh.knownHosts, ssh.passphrase, …) accepts a SecretSource. Inline strings work for prototypes, but production code should pull from the environment, a file, or a callback so secrets stay out of source control and out of process memory dumps.

// Inline string - fine for tests, avoid in production.
password: "hunter2";

// Read from an environment variable.
password: {
  env: "SFTP_PASSWORD";
}

// Read from a file (e.g. a Docker / Kubernetes secret mount).
privateKey: {
  path: "/run/secrets/sftp_id_ed25519";
}

// Read base64-encoded binary from an environment variable.
ca: {
  base64Env: "FTPS_CA_BUNDLE_B64";
}

// Pull from your vault / credential broker on demand.
password: async () => await vault.read("kv/sftp/deploy");

Profiles are run through redactConnectionProfile() before any log line is emitted, so secret values never appear in logs, audit entries, or diagnostics.

Worked examples

// SFTP with public-key auth + host-key pin (production-hardened)
const sftpProfile: ConnectionProfile = {
  host: "sftp.example.com",
  provider: "sftp",
  username: "deploy",
  ssh: {
    privateKey: { path: "./keys/id_ed25519" },
    pinnedHostKeySha256: "SHA256:abc123basesixfourpinFromKnownHosts=",
  },
};

// FTPS with mTLS + private CA bundle
const ftpsProfile: ConnectionProfile = {
  host: "ftps.internal.example",
  provider: "ftps",
  username: "audit",
  tls: {
    ca: { path: "./certs/ca-bundle.pem" },
    cert: { path: "./certs/client.crt" },
    key: { path: "./certs/client.key" },
    pinnedFingerprint256:
      "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99",
  },
};

// S3-compatible bucket
const s3Profile: ConnectionProfile = {
  host: "data-lake-bronze",
  provider: "s3",
  username: { env: "AWS_ACCESS_KEY_ID" },
  password: { env: "AWS_SECRET_ACCESS_KEY" },
};

// Cloud drive (OAuth bearer token in `password`)
const dropboxProfile: ConnectionProfile = {
  host: "",
  provider: "dropbox",
  password: { env: "DROPBOX_ACCESS_TOKEN" },
};

Security guidance

  • Pin host keys for SSH/SFTP. Without ssh.knownHosts or ssh.pinnedHostKeySha256 the SSH session accepts any key the server presents - a MITM risk.
  • Pin TLS fingerprints when you control the server. tls.pinnedFingerprint256 is defence-in-depth on top of rejectUnauthorized: true and a CA bundle.
  • Never set tls.rejectUnauthorized: false in production. Pair self-signed servers with tls.ca instead.
  • Prefer { env }, { path }, or callback secrets over inline strings or hard-coded values.
  • See examples/sftp-private-key.ts, examples/ftps-client-certificate.ts, and examples/profile-from-env.ts for end-to-end hardened profile builds.

Full per-field reference: ConnectionProfile, SshProfile, TlsProfile, SecretSource.

Capability matrix

Every provider advertises its own CapabilitySet. The full programmatic matrix is exposed via getBuiltinCapabilityMatrix() and renders to Markdown via formatCapabilityMatrixMarkdown().

| Provider | Streaming | Resume | Server-side copy | Multipart upload | Checksum exposed | | ------------- | :-------: | :------------------------------: | :--------------: | :--------------: | :----------------------: | | FTP | ✅ | ⬆/⬇ via REST | - | - | - | | FTPS | ✅ | ⬆/⬇ via REST | - | - | - | | SFTP | ✅ | ⬆/⬇ | rename | - | - | | HTTP(S) | ✅ (read) | ⬇ via Range | - | - | ETag | | WebDAV | ✅ | ⬇ via Range | COPY | - | ETag | | S3-compatible | ✅ | ⬆ via multipart resume / ⬇ Range | CopyObject | ✅ | SHA-256 / md5 | | Azure Blob | ✅ | ⬇ via Range | - | ✅ | md5 | | GCS | ✅ | ⬇ via Range | - | ✅ | crc32c / md5 | | Google Drive | ✅ | ⬇ via Range | - | - | md5 | | Dropbox | ✅ | ⬇ via Range | - | - | content_hash | | OneDrive | ✅ | ⬇ via Range | - | ✅ | sha256 / sha1 / quickXor | | Local | ✅ | ⬆/⬇ | - | - | - | | Memory | ✅ | ⬆/⬇ | - | - | - |

Examples

Real-world examples live in examples/. Run them with tsx examples/<file>.

| Example | What it shows | | --------------------------------------------------------------------------- | ----------------------------------------------------------------- | | local-copy-file.ts | Zero-config local-to-local copy via copyBetween. | | ftp-basic.ts | Plain FTP upload + download round-trip with username/password. | | ftp-directory-ops.ts | FTP session.fs: list, stat, mkdir, rename, remove, rmdir. | | ftps-basic.ts | FTPS with username/password over a public-CA endpoint. | | ftps-client-certificate.ts | FTPS hardened: mTLS + private CA bundle + fingerprint pinning. | | ftps-directory-ops.ts | FTPS session.fs: list, stat, mkdir, rename, remove, rmdir. | | sftp-basic.ts | Minimal SFTP with username/password (no host-key pinning). | | sftp-private-key.ts | SFTP hardened: private-key auth + pinned host-key SHA-256. | | sftp-directory-ops.ts | SFTP session.fs: list, stat, mkdir, rename, remove, rmdir. | | ssh-exec-command.ts | Standalone SSH stack: handshake, auth, run a remote command. | | s3-compatible-upload.ts | S3 multipart upload with cross-process resume store. | | webdav-sync.ts | WebDAV diff + sync plan with deterministic ordering. | | signed-url-download.ts | HTTPS signed-URL download with progress reporting. | | transfer-queue.ts | Concurrent transfers with TransferQueue + executor. | | dry-run-sync.ts | Plan a sync, print a summary, never touch bytes. | | mft-route.ts | SFTP→S3 cron-scheduled MFT route with audit hooks. | | profile-from-env.ts | Build a ConnectionProfile from env / file / base64-env secrets. | | diagnose-connection.ts | Provider summary + redaction-safe connection probe. | | approval-gated-route.ts | Two-person rule: scheduled route blocks until approval lands. | | multi-cloud-orchestration.ts | Fan-out SFTP → S3 + Azure + Local with webhook audit. | | atomic-deploy-with-rollback.ts | Blue/green-style deploy plan with rollback path. |

Documentation

Regenerate everything locally:

npm run docs:all      # HTML + Markdown api refs + per-scope pages + per-package READMEs

Project status

ZeroTransfer is in alpha under the alpha npm dist-tag. The provider-neutral foundation, transfer engine, queue, sync planner, atomic deploy planner, MFT layer, friendly client surface, and diagnostics module are stable. Multipart / resumable upload sessions are now wired up across S3, Azure Blob, GCS, and OneDrive. Phase work in progress: broader real-server compatibility coverage and the push to higher coverage targets.

Contributing

git clone https://github.com/tonywied17/zero-transfer.git
cd zero-transfer
npm install
npm run ci          # lint, format check, typecheck, tests with coverage, build, pack dry-run
npm run test:watch  # iterate

Issues and PRs welcome. Provider integration tests are gated behind opt-in env vars - see test/integration/ for the full list.

License

MIT © Tony Wiedman