Encrypted backups. Same everywhere.
FileFerry is a planned all-Rust backup CLI for operators, IT directors,
developers, and teams that need reliable encrypted backups without platform
drama. The command will be ferry.
FileFerry is in early implementation. The active build plan lives
in BUILD.md. This README describes the target product and the
contracts the implementation must satisfy before release.
Homepage: fileferry.app.
The public homepage is implemented as a separate lightweight Rust binary,
fileferry-web, using Axum and server-rendered Leptos views. It is marketing
infrastructure for fileferry.app, not a FileFerry backup server mode.
FileFerry gives operators a secure, scriptable backup tool that behaves predictably on every machine they manage.
It is for people who want:
- One binary.
- One config format.
- One scripting contract.
- One encrypted repository format.
- Boring restores.
- First-class Windows, macOS, Linux, and BSD behavior.
It is not a GUI, SaaS dashboard, agent service, FUSE mount, scheduler, server, mobile app, or compatibility layer for an existing backup repository format.
ferry init s3://company-backups/laptops
ferry backup ~/Documents --tag laptop --jsonl
ferry snapshots --json
ferry restore latest ~/restore-test
ferry check --read-data-subset 5%
ferry forget --keep-daily 14 --keep-weekly 8 --pruneCore command surface under design:
ferry init
ferry backup
ferry restore
ferry snapshots
ferry ls
ferry find
ferry diff
ferry check
ferry forget
ferry prune
ferry key
ferry repo
ferry policy
ferry doctor
ferry completion
ferry version
Global flags:
--repo <URL> Repository URL
--profile <NAME> Config profile
--config <FILE> Config file path
--json Emit one stable JSON document on stdout
--jsonl Emit stable newline-delimited JSON events on stdout
--quiet Reduce human output
--log-level <LEVEL> Set log level
--no-progress Disable progress UI
FileFerry is automation-first:
- Stdout is data.
- Stderr is logs, progress, and diagnostics.
--jsonemits one JSON document.--jsonlemits one JSON event per line.- Human progress is optional and never appears in JSON or JSONL output.
- Exit codes are documented and stable before v1.
- Destructive commands support
--dry-run. - Long operations can be interrupted safely.
Planned exit code families:
0 success
1 generic failure
2 invalid command, arguments, config, or environment
3 repository not found, uninitialized, locked, or incompatible
4 authentication, password, key, or permission failure
5 storage, network, or filesystem I/O failure
6 integrity, corruption, tampering, or verification failure
7 requested snapshot, path, tag, or policy was not found
8 operation was interrupted after reaching a safe state
9 unsupported platform, filesystem feature, or backend capability
10 partial success; inspect JSON output for item-level failures
These numbers are a target contract. Once marked stable for v1, they should not change without a compatibility plan.
FileFerry repositories are encrypted client-side before anything leaves the machine.
The repository format must protect:
- File contents.
- File names.
- Directory structure.
- Snapshot metadata.
- Indexes.
- Policy/config objects that reveal sensitive backup shape.
The target model is envelope encryption:
- Each repository has a random master key.
- Passphrases or key files unlock that master key.
- Data, metadata, and indexes use derived subkeys.
- Every encrypted object is authenticated.
- Read, check, and restore detect corruption and tampering.
Security-facing commands:
ferry key add
ferry key remove
ferry key rotate
ferry key export-recovery
ferry repo verify
ferry repo inspect --jsonOnly non-sensitive bootstrap fields, such as format version and key derivation parameters, may be plaintext. Any plaintext repository field must be justified in the security design before the format freezes.
FileFerry uses an original repository format. It does not read or write restic, rustic, Borg, Kopia, or rclone-native repository formats.
Core object groups:
- Encrypted chunks.
- Encrypted snapshot manifests.
- Encrypted indexes.
- Encrypted policy/config object.
- Temporary upload state.
- Prune marks and maintenance metadata.
Design goals:
- Append-friendly writes.
- Safe interruption.
- No required rename operations.
- Safe concurrent backups.
- Two-phase prune.
- Deterministic integrity checks.
- Clear future migrations.
Object storage is not treated like a filesystem. Repository operations must use immutable objects, idempotent writes, explicit commit markers, retry-safe upload state, and backend capability checks.
Target Rust workspace:
crates/
fileferry-cli/ clap commands, output formats, config loading
fileferry-core/ snapshots, repository format, backup/restore engine
fileferry-storage/ local and object storage abstraction
fileferry-crypto/ key derivation, encryption, authenticated metadata
fileferry-platform/ filesystem metadata across supported platforms
fileferry-policy/ retention, pruning, lifecycle rules
fileferry-testkit/ fake stores, corruption fixtures, platform helpers
fileferry-web/ Axum + Leptos public homepage for fileferry.app
xtask/ release, fixtures, and repo automation when useful
docs/ durable architecture, security, operations, release docs
Expected Rust stack:
clapfor command parsing.tokiofor async storage and network work.serde,toml, andserde_jsonfor config and machine output.tracingfor logs and instrumentation.mietteorcolor-eyrefor human diagnostics.object_storefirst for S3-compatible, cloud, and local-style object backends.- Optional OpenDAL adapter later for broader backend support.
fastcdcfor content-defined chunking.blake3for fast content IDs and checksums.zstdfor compression.- Argon2id for passphrase key derivation.
zeroizeandsecrecyfor secret handling.
The homepage stack is intentionally separate from the CLI/runtime stack:
fileferry-web uses axum, tokio, and Leptos SSR to serve static marketing
content, /assets/site.css, and /healthz.
Run the current homepage locally:
cargo run -p fileferry-webBy default it binds 0.0.0.0:8080. Set FILEFERRY_WEB_ADDR to override the
listener, for example:
FILEFERRY_WEB_ADDR=127.0.0.1:8080 cargo run -p fileferry-webUbuntu self-hosting notes live in
docs/homepage-deployment.md.
Target config example:
[repository]
url = "s3://company-backups/fileferry/laptops"
profile = "default"
[backup]
sources = [
"~/Documents",
"~/Projects"
]
exclude = [
"**/.git",
"**/node_modules",
"**/target",
"**/.DS_Store"
]
tags = ["laptop", "workstation"]
[retention]
keep_daily = 14
keep_weekly = 8
keep_monthly = 12
[storage]
concurrency = 16
timeout = "60s"
retry = 5
[output]
progress = "auto"
log_level = "info"Environment variables:
FILEFERRY_REPOSITORY
FILEFERRY_PASSWORD
FILEFERRY_PASSWORD_FILE
FILEFERRY_CONFIG
FILEFERRY_PROFILE
FILEFERRY_LOG
Secrets must be redacted from logs, diagnostics, JSON, crash output, and test fixtures.
V1 target:
- Local filesystem.
- S3-compatible object storage.
Later candidates:
- Azure Blob Storage.
- Google Cloud Storage.
- WebDAV.
- Backblaze B2 native or S3-compatible.
- Optional OpenDAL extra backends.
- Optional rclone bridge.
rclone must not be a core dependency. FileFerry's default identity is a Rust-native backup tool.
FileFerry is cross-platform first, but support is earned by CI and releases.
Target v1 release artifacts:
- Windows x86_64 MSVC.
- Windows ARM64 MSVC.
- macOS x86_64.
- macOS ARM64.
- Linux x86_64 GNU.
- Linux x86_64 musl.
- Linux ARM64 GNU/musl.
- FreeBSD x86_64.
- NetBSD x86_64 where feasible.
OpenBSD is best-effort until build, test, CI, and release support are real.
No platform should be called supported unless CI builds it, tests the relevant behavior, and release artifacts exist.
V1 should include:
init,backup,restore,snapshots,ls,check,forget, andprune.- Key management.
- Local backend.
- S3-compatible backend.
- JSON and JSONL output.
- Config profiles.
- Shell completions.
- Signed cross-platform releases with checksums and SBOMs.
V1 should not include:
- GUI or TUI.
- FUSE mount.
- Daemon mode.
- Server mode.
- Every storage provider.
- restic or rustic repository compatibility.
- Mobile apps.
- Built-in scheduling.
The repo contains the initial Rust workspace, crate boundaries, CLI shell, CI
workflow, planning docs, tested crypto primitives, local and S3-compatible
storage groundwork, and core backup/restore/check primitives. The CLI
currently exposes version, completion, local and S3-compatible repository
init, backup, restore, snapshots, ls, check, and marker-only
forget; it also exposes local and S3-compatible repository prune as a
two-phase delete path for objects reachable only from forgotten snapshots,
ferry key add as an append-only passphrase key-slot addition command,
ferry key remove as marker-based external key-slot removal,
ferry key rotate as unlock rotation that adds one new passphrase key slot
and marker-removes explicitly selected external key slots, plus
ferry key export-recovery --output <FILE> as an encrypted recovery-package
export protected by the current repository passphrase. The key-management
commands work for initialized local and S3-compatible repositories.
Restore currently covers directory
entries, regular-file contents, Unix symlinks, and modified timestamps for
restored regular files and directories from initialized local and
S3-compatible repositories. On Unix destinations it also restores captured
regular-file and directory permission bits where representable and verifies
captured Unix UID/GID ownership for restored regular files and directories,
warning when created destination ownership does not match. It does not call
chown. Symlink targets are restored, but selected regular-file and directory
creation/birth timestamps, selected symlink timestamps, and captured Unix
symlink mode/ownership are reported as not restored. New manifests record
reportable xattr presence/count status where the platform and filesystem
expose xattr listing, plus ACL status, file flag status, resource fork status,
Windows attribute status, and sparse extent status scaffolding. ACL contents,
file flag values, resource fork values, Windows attribute values, sparse extent
maps, and xattr names/values are not restored; special mode bits and other
metadata application are not implemented yet.
Non-dry-run restore preflights selected destination paths for observed
case-insensitive path collisions and rejects Windows reserved-name segments on
Windows destinations before writes; this is not a broader platform-support
claim.
Check failures
in JSON and JSONL modes now
emit machine-readable failure envelopes with stable codes and object-key
context where available.
ferry check --read-data-subset <N|PERCENT> reads a deterministic subset of
referenced chunk data for initialized local and S3-compatible repositories.
ferry forget evaluates retention keep rules, supports dry-run, writes forget
markers only when not in dry-run, and does not delete objects itself.
ferry prune supports initialized local and S3-compatible repositories,
supports dry-run, writes encrypted command lease state before non-dry-run
marking or sweeping, writes encrypted prune plan/completion state, resumes an
incomplete sweep when repository commit/forget state still matches the marked
plan, and deletes only forgotten-snapshot commit markers, forget markers,
manifests, indexes, and chunks that are not reachable from non-forgotten
committed snapshots. It rejects another active readable prune lease as a locked
repository, ignores expired readable leases, and fails closed on malformed
lease state before candidate deletion. It does not clean stale temporary
objects, repair corrupted repositories, compact beyond unreachable-object
deletion, implement stale-lease breaking, or provide provider-specific S3
lifecycle policy management. ferry key add writes one
immutable additional key slot for the existing repository master key; it does not
re-encrypt repository objects or recover lost keys. ferry key remove writes
one immutable key-slot-removals/<key-slot-id> marker for an externally
added key slot; it does not delete key-slot objects, remove the original
bootstrap slot, rekey, re-encrypt repository objects, or recover lost keys.
ferry key rotate writes
one immutable new key slot for the existing repository master key, proves the
new passphrase unlock path before marker-removing selected externally added
old slots, and does not rekey, re-encrypt repository objects, delete key-slot
objects, remove the original bootstrap slot, remove unselected slots, or
recover lost keys. ferry key export-recovery writes a standalone encrypted
recovery package to a destination file that must not already exist; it does
not implement recovery import, export raw master-key material, rekey, rewrite
repository objects, or recover lost passphrases. Broader metadata application is not implemented yet. The
repository format is still not frozen.
The normal local gate is:
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo build --workspaceFor docs-only changes:
git diff --checkDurable implementation details should move into docs/ as they settle,
especially architecture, repository format, security, CLI/JSON contracts,
storage behavior, platform metadata, operations, and release process. Current
design docs include docs/security.md,
docs/repository-format.md,
docs/cli-contract.md,
docs/operations.md, and
docs/platform-metadata.md.
MIT. See LICENSE.