Skip to main content
Deno 2 is finally here 🎉️
Learn more

DFtpS - Deno Ftp Server

logo deno doc

DFtpS is an FTP server based on ftp-srv with Deno.

Version 2.0.0 - Fully modernized for Deno 2.x with:

  • JSR-based imports (@std/*, @cliffy/*)
  • Drizzle ORM (replacing deprecated denodb)
  • Argon2 password hashing (replacing scrypt)
  • Native Streams API (no more BufReader/BufWriter)
  • Full TypeScript strict mode

Check our guide in https://mnlaugh.github.io/dftps-guide/guide/

Usage

Install

curl -fsSL https://deno.land/x/dftps/install.sh | sh

Install Specific Version

curl -fsSL https://deno.land/x/dftps/install.sh | sh -s v1.0.0

Make your own

Simple

  • First, we import the Server class and type for user authentication.
import { Server } from "jsr:@dftps/server";
import type { LoginResolvable, UsernameResolvable } from "jsr:@dftps/server";
  • Then we just need to create an instance of Server with these options described below.
const serve = new Server({ port: 21, hostname: "127.0.0.1" });
  • All we have to do is wait for a new connection and check the veracity of it using the authentication tools (awaitUsername, awaitLogin).
for await (const connection of serve) {
  const { awaitUsername, awaitLogin } = connection;
  /** waiting to receiving username from connection */
  awaitUsername.then(({ username, resolveUsername }: UsernameResolvable) => {
    if (username !== "my-username") return resolveUsername.reject("Incorrect username!");
    resolveUsername.resolve();
  });
  /** waiting to receiving password from connection and finalize the user authenticate */
  awaitLogin.then(({ password, resolvePassword }: LoginResolvable) => {
    if (password !== "my-password") return resolvePassword.reject("Wrong password!");
    resolvePassword.resolve({ root: "my-folder", uid: 1000, gid: 1000 });
  });
}

With database

  • Import the database utilities and the Users repository:

    import { Server, verify } from "jsr:@dftps/server";
    import { createDb, type User, Users } from "jsr:@dftps/server/db";
  • Database configuration (SQLite):

    • SQLite Options
      • filepath [string] (Required) - Path to the SQLite database file
/** Initialize SQLite database */
const db = createDb({ connector: "SQLite", filepath: "./data/dftps.db" });

const serve = new Server(ListenOptions, FTPServerOptions);

for await (const connection of serve) {
  const { awaitUsername, awaitLogin } = connection;
  let user: User | undefined;

  /** Waiting to receiving username from connection */
  awaitUsername.then(async ({ username, resolveUsername }: UsernameResolvable) => {
    /** Find user in database */
    user = await Users.findByUsername(username);
    if (!user) return resolveUsername.reject("Incorrect username!");
    resolveUsername.resolve();
  });

  /** Waiting to receiving password from connection and finalize the user authenticate */
  awaitLogin.then(async ({ password, resolvePassword }: LoginResolvable) => {
    if (!user) return resolvePassword.reject("User not found!");
    if (!await verify(user.password, password)) return resolvePassword.reject("Wrong password!");
    const { root, uid, gid } = user;
    resolvePassword.resolve({ root, uid, gid });
  });
}

Log example

output_example


Deno Dependencies

All dependencies are now available via JSR with modern Deno 2.x compatibility:

List of FTP commands

See COMMANDS.md

Contributing

See CONTRIBUTING.md

References