Files
codingetandClaude f659f230cd
CI / lint (pull_request) Successful in 3m39s
CI / format (pull_request) Successful in 3m24s
CI / install (pull_request) Successful in 9m4s
CI / typetest (pull_request) Successful in 3m5s
CI / typecheck (pull_request) Successful in 3m20s
CI / node-tests (pull_request) Successful in 3m31s
CI / browser-tests (pull_request) Successful in 5m16s
docs: add per-package READMEs and position Tailshare as the flagship app
Give every workspace a README describing what it does, its entry points,
and a short usage example, and link them from the root package table.

Frame Tailshare in the root README as the application the libraries exist
to be composed into, with the other apps as development and testing
surfaces. Its own README records what works today and what the open
issues intend for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:51:59 +00:00
..

@webnet/ftp

FTP/FTPS client and server built on @webnet/transport and @webnet/vfs.

FTPClient implements AsyncVFS, so it can be used anywhere an AsyncVFS is expected (stat, readdir, readFile/readFileRange, writeFile, delete, mkdir, move). It supports plaintext FTP, implicit FTPS (TLS from the first byte, via dialer.dialTls), and explicit FTPS (plaintext control connection upgraded with AUTH TLS/PROT P). FTPServer serves an AsyncVFS over FTP, with optional per-login authenticate to select a different VFS per user and optional TLS credentials for explicit AUTH TLS. Errors from both sides surface as FTPError, carrying the underlying Reply.

Entry points

Entry point Description
@webnet/ftp FTPClient, FTPServer, FTPError, and their option/reply types.
@webnet/ftp/client FTPClient and its option types only.
@webnet/ftp/server FTPServer and its option types only.

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

Usage

Client

import { FTPClient } from "@webnet/ftp/client"
import type { RawDialer } from "@webnet/transport"

declare const dialer: RawDialer

const client = new FTPClient({ dialer, host: "ftp.example.com", user: "anonymous" })
const entries = await client.readdir("/pub")
const stream = await client.readFile("/pub/readme.txt")
await client.close()

Server

import { FTPServer } from "@webnet/ftp/server"
import type { RawListener } from "@webnet/transport"
import type { AsyncVFS } from "@webnet/vfs"

declare const vfs: AsyncVFS
declare const controlListener: RawListener
declare const dataListen: (port: number) => Promise<RawListener>

const server = new FTPServer({ vfs, dataListen })
await server.listen(controlListener)

See also

  • @webnet/vfs — the AsyncVFS interface this package implements and serves
  • @webnet/transport — the RawDialer/RawListener/RawTransport primitives this package is built on
  • @webnet/sftp — an alternative file-transfer protocol over SSH instead of FTP