Mount an S3 bucket.
Get a filesystem that tells you the truth.
ObjectFS presents a POSIX interface over AWS S3 for research computing. Every object it writes
carries a SHA-256. A failed upload fails close(2). The operations it cannot
implement return an error instead of pretending.
Runs with your AWS credentials ยท Not a POSIX-compliant filesystem, deliberately
$ objectfs --cache-size 8GB s3://lab-genomics /mnt/data mounted s3://lab-genomics at /mnt/data # ordinary tools, no SDK $ cp results.bam /mnt/data/run-42/ $ samtools view /mnt/data/run-42/results.bam | head # a failed PUT fails the syscall โ it is not logged and swallowed $ cp big.tar /mnt/readonly-bucket/ cp: closing '/mnt/readonly-bucket/big.tar': Permission denied $ echo $? 1 โ the exit status you would get from a local disk
What is on the mount path
Everything below is reachable from a running mount. Capabilities that have code but no path from a mount are listed separately in the docs rather than mixed in here.
Verified reads
Every object ObjectFS writes records a SHA-256 of its uncompressed content. A read that returns a complete object verifies that hash and refuses a mismatch.
- Catches truncation, bit-rot, a lost
Content-Encoding - Refused with an integrity error, not returned with exit 0
- Partial reads of large objects are not verified โ see below
Real read-modify-write
Writing at an offset buffers dirty byte ranges, fetches the ranges of the stored object it needs, splices, and PUTs the result โ it does not replace the object with the fragment you wrote.
- Write at any offset; truncate and extend both work
close/fsyncare synchronous and return errors- Growing a file leaves a hole that reads as zeros
Caching and read-ahead
An LRU tier, an optional persistent tier on local disk, and a prefetcher that runs ahead of a sequential reader. A read served from cache makes no S3 request.
- Large reads fanned out as concurrent ranged GETs
- Read-ahead tuned by
performance.read_ahead - Multipart uploads for large files
S3 capabilities, probed
AWS S3 is the target, not a lowest common denominator. Capabilities are established by probing the endpoint โ never a config flag or a URL heuristic.
- Transfer Acceleration falls back silently when absent
- Conditional writes fail closed rather than drop a precondition
- MinIO, Ceph RGW and RustFS are best-effort, and say so
Observable while it runs
A Prometheus /metrics endpoint and a /health endpoint, both
served by the mount process, both defaulting to loopback because neither is
authenticated.
- Billable S3 request counts and dollar figures, counted in the SDK response path
- One templated systemd unit,
objectfs@.service - Strict config decoding โ a typo'd key fails at startup, by name
Encryption and compression
Server-side encryption on every object, SSE-S3 or SSE-KMS. Transparent compression is available and off by default.
- ZSTD, LZ4 and gzip, opt-in per mount
- Off by default: a compressed object is no longer readable by
aws s3 cp - Storage class per mount via
storage_tier
What it does not do
S3 is not a disk, and the gap cannot be closed by wishing. These fail, and the failure is the correct answer rather than a missing feature.
RENAME_NOREPLACE and RENAME_EXCHANGE are refused with
EINVAL rather than approximated.link has no meaning when a key names exactly one
object โ it will never be supported. symlink, mknod and
fallocate return ENOTSUP.security.* and system.* xattrs are refused: object
metadata is writable by anyone with bucket write access, so an attribute the kernel acts
on must not be stored there.internal/fuse is
//go:build linux || darwin. macOS needs macFUSE.The supported-operations table is the contract โ derived from the methods that exist, not from intent. Read it before pointing a workload at a mount.
Install
The install script resolves your platform, verifies the SHA-256 published beside the
binary, and installs under ~/.local/bin.
curl -fsSL https://raw.githubusercontent.com/scttfrdmn/objectfs/main/scripts/install.sh | bash
# elsewhere, or pinned to a release
curl -fsSL https://raw.githubusercontent.com/scttfrdmn/objectfs/main/scripts/install.sh \
| bash -s -- --prefix /usr/local --version v0.13.0
# Packages are attached to each release from v0.14.0 onward.
# There is no apt or yum repository to add โ install the downloaded file.
apt install ./objectfs_*.deb # Debian, Ubuntu
dnf install ./objectfs-*.rpm # RHEL, Fedora, openSUSE
git clone https://github.com/scttfrdmn/objectfs.git
cd objectfs
make build # produces ./bin/objectfs
# or, without a checkout
go install github.com/scttfrdmn/objectfs/cmd/objectfs@latest
# macOS needs macFUSE first
brew install --cask macfuse
The default prefix is ~/.local rather than /usr/local
deliberately: many of this project's users are on a shared login node with no root, and an
installer whose default needs sudo teaches people to run the whole thing under
sudo โ which writes a root-owned binary and root-owned cache directories into a
home their own jobs use.
The checksum is always verified and there is no flag to skip it. Note what
that does and does not establish: the .sha256 travels the same channel as the
tarball, so a mismatch means a corrupted or tampered download โ not that the release itself
is authentic. That is a signature's job, and this project does not sign releases yet.
How this project decides
Integrity first, performance a close second. When the two conflict, the order is not negotiable.
An error must reach the caller
A failed PUT fails close(2). rm on a missing file is
ENOENT, not a silent success. go-fuse defaults an unimplemented
Unlink to success, which once let rm exit 0 while the
object survived โ the kind of defect that only surfaces as lost data months later.
Capabilities are probed, never asked
A store that accepts a header and ignores it looks identical to one that honours it, from every angle except the outcome. So there is no config flag or endpoint heuristic for a backend capability. If a probe cannot get an answer, the capability is absent.
Performance degrades, correctness refuses
Transfer Acceleration falling back to a plain endpoint is a correct outcome โ slower. A dropped write precondition is not: it tells every contender it won. Correctness capabilities fail closed with an operator-facing reason.
No unmeasured numbers
This site quotes no throughput figures because no benchmark in the repository produced
any. A table of five once sat in the docs โ 800โ1200 MB/s, a 4.6ร BBR improvement โ and
not one was measured. benchmarks/ exists; until it is run against a named
bucket and object size, the honest answer is silence.
Tests go through the real seam
A mock on the far side of a seam agrees with its caller by construction, which is how 32,680 lines of tests missed roughly 45 defects. The suite now runs the real S3 backend against an in-process endpoint over real HTTP, plus a differential oracle that replays an operation sequence against the local filesystem and demands they agree.
Documentation is checked, not written
Tests assert that documented config keys exist in the schema, that documented Go symbols exist, and that the docs navigation matches the files on disk. Prose has no way to be told it has gone stale, so the parts that matter are gates instead.