siftp
An FTP, FTPS, and SFTP server in Rust. One daemon, one config file, the same users and permissions on every protocol.
# 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):
The same numbers as a table
| siftp | vsftpd | ProFTPD | OpenSSH | |
|---|---|---|---|---|
| FTPS download, MiB/s | 2357 | 1040 | 1391 | – |
| FTPS upload, MiB/s | 1752 | 683 | 1520 | – |
| SFTP download, MiB/s | 954 | – | – | 763 |
| SFTP upload, MiB/s | 900 | – | – | 811 |
| Memory with 200 FTP sessions, MiB | 17.1 | 65.5 | 174.0 | – |
| First FTP login, 32 clients, logins/s | 142 | 393 | 106 | – |
| Repeat FTP login, 32 clients, logins/s | 97173 | 393 | 106 | – |
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.