siftp

siftp

An FTP, FTPS, and SFTP server in Rust. One daemon, one config file, the same users and permissions on every protocol.

QuickstartDownloadSpecification

# argon2id hash for the config
$ siftpd hash-password 'a strong password'
# validate; errors are JSON
$ siftpd --check-config --config siftp.toml
# serve
$ siftpd --config siftp.toml
$ curl -T report.pdf ftp://alice:'a strong password'@127.0.0.1:2121/

FTPS download

2,357 MiB/s

1.7× ProFTPD

SFTP download

954 MiB/s

1.3× OpenSSH

Memory, 200 FTP sessions

17.1 MiB

3.8× less than vsftpd

Repeat FTP logins, 32 clients

97,173 logins/s

247× vsftpd; first login 142/s

Install

Debian 12 or later and Ubuntu 22.04 or later, amd64 and arm64 (including AWS Graviton):

sudo curl -fsSLo /etc/apt/keyrings/siftp.gpg https://apt.siftp.org/siftp-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/siftp.gpg] https://apt.siftp.org stable main" | sudo tee /etc/apt/sources.list.d/siftp.list
sudo apt update && sudo apt install siftpd

Key fingerprint, release files, and other platforms: Download.

What it does

Protocols

FTP (RFC 959, 2428, 3659, 7151), FTPS explicit and implicit (TLS 1.2/1.3), SSH/SFTP v3 with OpenSSH extensions, read-only WebDAV

Storage

Local directories with a symlink-proof jail, or S3-compatible object storage, behind the same interface

Access

argon2id passwords, SSH keys with expiry and revocation, groups, per-directory permissions, quotas, rate limits, login bans, LDAP

Data path

sendfile and splice on Linux, optional io_uring, a bounded buffer pool for backpressure, assembly CRC32C and scanning kernels

Operations

Durable user store, SIGHUP reload, graceful drain, /healthz and /readyz, Prometheus metrics, hash-chained audit log, systemd unit, container image, .deb

Automation

REST API with OpenAPI 3.1, MCP tools, a capability document, and ten stable error codes shared by every protocol

Measured

Against vsftpd 3.0.5, ProFTPD 1.3.8c, and OpenSSH 10.0p2 on one bare-metal Linux host (loopback, tmpfs, median of 5 runs):

FTPS downloadMiB/s, higher is better
siftp2,357
vsftpd1,040
ProFTPD1,391
FTPS uploadMiB/s, higher is better
siftp1,752
vsftpd683
ProFTPD1,520
SFTP downloadMiB/s, higher is better
siftp954
OpenSSH763
SFTP uploadMiB/s, higher is better
siftp900
OpenSSH811
Memory with 200 FTP sessionsMiB, lower is better
siftp17.1
vsftpd65.5
ProFTPD174
First FTP login, 32 clientslogins/s, higher is better
siftp142
vsftpd393
ProFTPD106
Repeat FTP login, 32 clientslogins/s, higher is better
siftp97,173
vsftpd393
ProFTPD106
The same numbers as a table
siftpvsftpdProFTPDOpenSSH
FTPS download, MiB/s235710401391–
FTPS upload, MiB/s17526831520–
SFTP download, MiB/s954––763
SFTP upload, MiB/s900––811
Memory with 200 FTP sessions, MiB17.165.5174.0–
First FTP login, 32 clients, logins/s142393106–
Repeat FTP login, 32 clients, logins/s97173393106–

Plaintext FTP is the same for all three FTP servers: each uses sendfile, and the client or the host is the limit. A first login in siftp runs argon2id, which is deliberately expensive; a repeat login by the same user is served from a keyed-digest cache without recomputing the hash.

Method, full table, and how to reproduce it: Performance.

Errors are data

Every failure carries one of ten stable codes. The admin API, MCP, and WebDAV return the whole object: code, message, hint, and a link to the docs. FTP replies and SFTP status messages start with the code, followed by the message.

550 permission_denied: permission denied: write is not allowed for this user
{"code":"permission_denied","message":"permission denied: write is not allowed for this user","hint":"grant the 'w' permission bit or choose a different operation","docs_url":"https://siftp.org/docs/errors#permission_denied"}

Ten codes, append-only: Error codes.

For agents

A running server describes itself at GET /.well-known/siftp.json on its admin port. Start there, then read /llms.txt and the OpenAPI document it points to. Agents and MCP has the full sequence.

License

MIT or Apache-2.0, at your option.