zftp-core is the embeddable engine behind zftp, a Cyberduck-style file-transfer client. It owns the saved bookmark model, connection-URL and Cyberduck .duck parsing, a session-lifecycle manager, a download/upload/sync transfer queue, per-session logs and throughput, a remote-filesystem transport boundary, and a credential boundary — and exposes all of them through one command surface (Engine::invoke), so every host drives identical behaviour. It carries no GUI and no platform UI deps, so the standalone app and every embed share the exact same code. The crate builds as rlib for native Rust/Tauri hosts and as staticlib/cdylib for non-Rust hosts over a C ABI. This project is in active development.
Many Protocols
FTP, FTPS, SFTP and WebDAV plus the cloud object stores (S3, GCS, Azure, B2, Swift, Drive, Dropbox, OneDrive, Box), each modeled with its scheme, default port and credential shape behind one transport trait.
Embeddable
One command surface — Engine::invoke(cmd, args_json) — drives every host identically: native Rust, a Tauri plugin, or a C/C++ host over the FFI (zftp_invoke).
Transfer Queue
A real download/upload queue with a concurrency cap, byte + file progress, a throughput sampler, cancel and clear — the heart of a transfer client, driven over the same protocol-blind transport.
Honest Status
The local-filesystem transport is fully wired (real listings and byte copies), and the network backends are wired too — behind the default net feature, OpenDAL drives FTP/FTPS/SFTP/WebDAV and the cloud object stores while russh drives SCP/SFTP. Build --no-default-features and connect reports a needs_transport error state instead — it never fakes a live connection.
Quick start
use zftp_core::Engine;
use serde_json::json;
let mut engine = Engine::new()?;
// One dotted command surface for every host.
let info = engine.invoke("version", &json!({}))?;
// Add a bookmark from a URL, connect, then list its default directory.
let added = engine.invoke("bookmark.add", &json!({ "url": "sftp://user@host/pub", "name": "files" }))?;
let sess = engine.invoke("session.connect", &json!({ "id": added["id"] }))?;
let files = engine.invoke("fs.list", &json!({ "id": added["id"] }))?;
Surfaces
Depend on the rlib and call Engine directly, or go through Engine::invoke with JSON for parity across hosts. Enable the tauri feature to mount the engine as a Tauri v2 plugin. Link the staticlib/cdylib and call zftp_invoke from a C ABI host. Asynchronous facts (status changes, byte counts, log lines, transfer progress) are pushed to the host through an installed event sink.
Where it goes next
See the engineering report for the module map, dependency footprint, FFI surface and test status, the Cyberduck port report for feature coverage, and the MenkeTechnologiesMeta umbrella for the wider stack this engine plugs into.