- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
Some checks failed
Rust / build (push) Has been cancelled
Runs cargo update, picking the newest versions compatible with the toolchain's MSRV (1.94). Notable bumps: h2 0.4.14 -> 0.4.20, hyper 1.11 -> 1.12, tokio stack, rustls stack, rand 0.10.2 -> 0.10.3, and reqx 0.1.27 -> 0.1.34 (newer reqx requires Rust 1.96). This clears 8 of the 10 cargo-audit advisories that had accumulated since the last lockfile refresh. The two that remain (RUSTSEC-2026-0194 and RUSTSEC-2026-0195) are both in quick-xml 0.39.4, pulled in transitively by s3 0.1.27: the fixes land in quick-xml 0.41, which s3 only depends on from s3 0.1.38, which requires Rust 1.99.1 and is thus unreachable on the current toolchain. cargo build/test/clippy (-D warnings, all features) pass. |
||
| .github/workflows | ||
| rpm | ||
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE.md | ||
| README.md | ||
swfss3
swfss3 is a small CLI for performing filesystem-like operations (list/read/write/delete)
against a SeaweedFS S3-compatible API endpoint using the Rust s3 crate.
Build
cargo build --release
Binary will be at target/release/swfss3.
Authentication and configuration
You must provide:
- Endpoint:
--endpoint(custom domain / S3 API URL) - Bucket:
--bucket - Region:
--region(defaults tous-east-1)
Authentication (choose one):
- Flags:
--access-key-idand--secret-access-key(optional--session-token) - Environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN - Anonymous:
--anonymous(unsigned requests)
If you see invalid config: missing AWS_ACCESS_KEY_ID, it means you did not pass credentials
flags and swfss3 could not find the AWS environment variables. Fix it by using one of:
- Provide keys via flags:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
--access-key-id YOUR_KEY --secret-access-key YOUR_SECRET \
list --prefix some/path/
- Export keys via environment variables:
export AWS_ACCESS_KEY_ID=YOUR_KEY
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET
# optional (temporary credentials)
export AWS_SESSION_TOKEN=YOUR_TOKEN
- Use unsigned requests (only works if the bucket/object is publicly readable/writable):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style --anonymous \
list --prefix some/path/
Addressing style (optional, but often important for S3-compatible servers):
--path-style(recommended for many self-hosted endpoints)--virtual-hosted(bucket as a subdomain)- default is automatic selection
Commands
All commands share the common flags above and then take their own arguments.
List objects
List a prefix (like listing a directory):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
list --prefix some/path/
Recursive listing (no delimiter grouping):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
list --prefix some/path/ --recursive
Fetch all pages:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
list --prefix some/path/ --all
Read an object
To stdout:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
read some/path/file.txt
To a local file:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
read some/path/file.txt --out ./file.txt
Write an object
Upload a local file to an object key:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
write ./local.bin some/path/local.bin
Optionally set Content-Type:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
write ./index.html some/path/index.html --content-type text/html
Delete an object
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
delete some/path/local.bin
Stat (HEAD) an object
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
stat some/path/file.txt
Copy / move (rename) an object
Server-side copy:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
cp some/path/file.txt some/path/file-copy.txt
Move (copy then delete):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mv some/path/file.txt some/path/renamed.txt
Delete a directory
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
rmdir photos/2025
Create a directory
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mkdir photos/2025
Presigned URLs
Create a temporary download URL (default 900s expiry):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
presign-get some/path/file.txt --expires 300
Create a temporary upload URL:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
presign-put some/path/upload.bin --expires 300
Delete everything under a prefix
Dry-run (prints keys that would be deleted):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
rm-prefix some/path/
Actually delete:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
rm-prefix some/path/ --yes
Mount bucket via FUSE3 (Linux)
Mount the whole bucket read-only:
mkdir -p /tmp/s3mnt
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mount /tmp/s3mnt
Mount only a prefix (the prefix becomes the filesystem root):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mount /tmp/s3mnt --prefix photos/2025/
Enable writes (buffered uploads on flush/close):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mount /tmp/s3mnt --read-write
Allow access by other users (requires user_allow_other in /etc/fuse.conf):
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mount /tmp/s3mnt --allow-other
How objects are laid out
The filesystem maps a directory tree onto the flat key space of an object store. Two conventions make that mapping unambiguous and keep the bucket usable with other tools:
| Kind | Key | Example |
|---|---|---|
| File | the plain name | photos/2025/cat.jpg |
| Directory | the name with a trailing /, stored as an empty object |
photos/2025/ |
A directory is therefore stored as a directory marker: an empty object
whose key ends with /. The marker is what makes an empty directory
survive a round trip, and it is what mkdir and rmdir create and remove.
A directory also exists implicitly when other objects live below it, so
photos/2025/cat.jpg implies that photos/2025/ is a directory even when
no marker was ever written. Both forms are recognised.
The mkdir and rmdir commands create and remove markers directly:
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mkdir photos/2025
A trailing / is optional: photos/2025 and photos/2025/ name the same
directory.
Using it with git
A git repository can live in a bucket. Mount the bucket read-write and point git at it:
mkdir -p /tmp/s3mnt
swfss3 --endpoint http://localhost:8333 --bucket mybucket --path-style \
mount /tmp/s3mnt --read-write
cd /tmp/s3mnt
git init myrepo
cd myrepo
echo hello > README.md
git add README.md
git commit -m "initial"
Git needs a few operations that a plain object store does not provide, and they are implemented: creating and removing directories, truncating files, setting modification times, flushing and renaming.
Two things are worth knowing:
- A rename is a copy followed by a delete. The object store has no atomic rename, so there is a short window in which both objects exist. A crash in that window leaves the copy behind; the original is only removed once the copy has succeeded.
- The page cache of the kernel is bypassed. Every read goes to the server, so what is read is always what is stored. This costs some performance but avoids showing stale data.
Git writes loose objects and a packfile into .git/objects, and keeps its
references in .git/refs. Both are ordinary files as far as the
filesystem is concerned.
Troubleshooting
NoSuchBucket but the bucket “exists”
If you get an error like code=NoSuchBucket, double-check:
- You are hitting the S3 gateway endpoint, not the filer UI or another reverse-proxied service.
- The bucket exists in the same endpoint/cluster. Buckets are endpoint-scoped.
- Addressing style: if one style doesn’t work, try the other:
--path-style(common for self-hosted)--virtual-hosted(requires bucket DNS/wildcard)
Help
swfss3 --help
swfss3 list --help
swfss3 read --help
swfss3 write --help
swfss3 delete --help
swfss3 stat --help
swfss3 cp --help
swfss3 mv --help
swfss3 mkdir --help
swfss3 rmdir --help
swfss3 presign-get --help
swfss3 presign-put --help
swfss3 rm-prefix --help
swfss3 mount --help