Quickstart
From nothing to FTP, FTPS, and SFTP transfers on loopback ports that need no privileges. Production installs (package, container, systemd) are in Operations.
1. Get the binary
Download a release for Linux x86_64 or aarch64 from Download, or build it:
cargo build --release -p siftpd # Rust 1.89+; a C compiler for ring and the asm kernels
./target/release/siftpd --version
2. Write a config
Hash a password, then put it in siftp.toml:
./target/release/siftpd hash-password 'choose a strong password'
# $argon2id$v=19$m=65536,t=3,p=4$...
[server]
root = "/tmp/siftp-demo/srv"
user_store = "/tmp/siftp-demo/users.toml"
[ftp]
bind = "127.0.0.1:2121"
passive_port_range = [40000, 40100]
[ftps]
# FTPS stays off until these files exist (step 4).
cert_file = "/tmp/siftp-demo/tls.crt"
key_file = "/tmp/siftp-demo/tls.key"
implicit_bind = "127.0.0.1:2990"
[sftp]
bind = "127.0.0.1:2222"
host_key_file = "/tmp/siftp-demo/ssh_host_ed25519_key"
[admin]
bind = "127.0.0.1:8080"
token = "${ENV:SIFTP_ADMIN_TOKEN}"
[logging]
audit_file = "/tmp/siftp-demo/audit.jsonl"
[[users]]
name = "alice"
password_hash = "$argon2id$v=19$m=65536,t=3,p=4$...paste the hash..."
permissions = "rwdnml"
alice's root defaults to /tmp/siftp-demo/srv/alice and is created on first login. Check the file; problems come back as JSON naming the field:
export SIFTP_ADMIN_TOKEN=$(openssl rand -hex 32)
./target/release/siftpd --check-config --config siftp.toml
3. Run and transfer
./target/release/siftpd --config siftp.toml
In another terminal:
echo hello > hello.txt
curl -T hello.txt ftp://alice:'choose a strong password'@127.0.0.1:2121/
curl ftp://alice:'choose a strong password'@127.0.0.1:2121/hello.txt
sftp -P 2222 alice@127.0.0.1 # interactive
For SFTP in a script, pin the host key instead of trusting the first connection. The fingerprint comes from the key file:
fp=$(ssh-keygen -lf /tmp/siftp-demo/ssh_host_ed25519_key | awk '{print $2}' | cut -d: -f2)
curl --hostpubsha256 "$fp" -u alice:'choose a strong password' \
sftp://127.0.0.1:2222/hello.txt
4. Add FTPS
Put a PEM certificate and key at the paths in [ftps] and restart. For a local test, a self-signed pair:
openssl req -x509 -newkey rsa:2048 -nodes -days 30 -subj /CN=localhost \
-keyout /tmp/siftp-demo/tls.key -out /tmp/siftp-demo/tls.crt
Explicit FTPS uses the FTP port (curl --ssl-reqd ftp://...), implicit FTPS the implicit_bind port (curl ftps://...). Set require_tls = true under [ftp] to refuse plaintext logins. A new certificate is picked up on kill -HUP without a restart.
5. Manage users through the API
curl -H "Authorization: Bearer $SIFTP_ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"bob","password":"bob-password","permissions":"rwl"}' \
http://127.0.0.1:8080/api/v1/users
curl -H "Authorization: Bearer $SIFTP_ADMIN_TOKEN" http://127.0.0.1:8080/api/v1/users
Without permissions, a user gets the union of their groups' permissions, or rl (read and list) if they have no groups. API users are written to user_store and survive restarts. Users in the config file stay read-only through the API.
Next
- Configuration: every key and override.
- Users and access: groups, directory rules, keys, LDAP.
- Security before exposing a listener.