ObjectFS v0.10.2
A packaging release, cut for one reason: v0.10.1 was tagged two hours before the module-path fix
merged, so the published tag still declared module github.com/objectfs/objectfs and
go get github.com/scttfrdmn/objectfs@v0.10.1 failed with module declares its path as — the exact
defect #213 was filed for. The fix existed on main and in no tag, which from a user's position is
indistinguishable from not being fixed. Everything else here is the packaging and contributor-path
work that landed alongside it.
Added
SECURITY.md— a security policy, with private vulnerability reporting enabled on the repository so a finding has somewhere to go that is not the public issue tracker. It documents what a reader cannot get from the code quickly: that the trust boundary is the mounting host and ObjectFS enforces no authorization of its own, that two unauthenticated HTTP listeners bind all interfaces by default (:8080metrics anddebugendpoints,:8081health) with the switch that turns each off, thatmode: offis the encryption default and what changed after the withdrawn v0.10.0at_restkey, and both stated limits of the SHA-256 read verification — a partial read is not verified, and an object with no recorded checksum verifies trivially. Every claim in it was verified by execution rather than read off the configuration schema, which is how the two listener defects below were found
Fixed
- The test harness could record a request after the client already had the response, so its own assertions were load-dependent.
internal/testawsproxies every request and logs it, and the read-path suite asserts on that log: bytes transferred and GETs issued are how read amplification and cache behaviour are measured, because neither the AWS SDK nor the emulator reports them. The log entry was appended afterproxy.ServeHTTPreturned — but the proxy writes the body to the socket inside that call, so a client could hold every byte of a response whose request was not yet recorded. Measured at 45–70 of 640 concurrent ranged reads. The visible symptom was in a different package:internal/fuseTestShortFileIsServedFromCachefailed on its precondition — "the first read issued no GET" — which reads as the read path serving bytes from a cache the fixture had just created empty, in a test whose entire subject is cache correctness. One CI run in seven. Requests are now published on arrival and their response fields filled in on completion, with the accessors waiting for anything still in flight; verified in both directions, since a regression test that cannot fail proves nothing - The module could not be imported under the name it gave for itself.
go.moddeclaredmodule github.com/objectfs/objectfs, and the code lives atgithub.com/scttfrdmn/objectfs. Go resolves an import path by fetching that path, sogo get github.com/scttfrdmn/objectfsfailed on the mismatch between the path requested and the path declared, while the declared path is a different project — an unrelated Python repository from 2017, 28 stars, last pushed 2019, in a single-repo organisation created the same day. Nothing published has ever existed at the declared path, which is whypkg.go.devhad nothing to index and the Go Reference badge rendered empty. The path is corrected ingo.modand in all 154 files that named it — 132 Go files, plus thegoimportslocal-prefixessetting in.golangci.yml, theDockerfileimage-source label, and the repository URLs in the Python and JavaScript SDK manifests, which pointed contributors at the wrong project. Verified by building an external consumer module against the corrected path, rather than by grepping for the string. This is breaking for any code that imported the old path, though nothing could have: it was never fetchable - Dependabot could not update Go dependencies, and had never merged anything. Two unrelated defects presenting as one symptom. The Go ecosystem failed on twelve consecutive weekly runs while
dockersucceeded in the same runs — Dependabot aborts per-ecosystem, so one broken ecosystem is silent unless the run list is read. The cause was upstream and is now resolved:proxy.golang.orghad no.modfor the pinnedcargoshipversion, Go fell through to direct git, and git reported the proxy's 404 ascould not read Username for 'https://github.com'— an authentication message for what was not an authentication failure, which is what sent the previous diagnosis after a credential that was never missing. Separately and more consequentially,.github/dependabot.ymllabelled every PRautomergeand that label did not exist; Dependabot drops unknown labels without reporting it, and every approve and merge step independabot-automerge.ymlwas gated on it, so 46 PRs were opened and none were ever merged. The label is now declared in.github/labels.ymlalongside the four others the config names .github/dependabot.yml:mavenandnpmecosystems forsdks/java,docs-platform, andsdks/javascript. Eight open Dependabot alerts — five againstjackson-databind, three againstvite, three of the eight high severity — were against manifests no ecosystem entry covered, so nothing could act on them.sdks/javascriptis included because CI runsnpm install && npm testthere on every PR, which makes its dependencies executed code. A ceiling worth stating: the npm security updates still cannot apply, because neither directory commits a lockfile and Dependabot cannot determine the installed version without one (#214).github/workflows/dependabot-automerge.ymlwaited oncheck-regexp: (test|lint|security).*, which is case-sensitive and start-anchored, so it matched 2 of the 9 checks CI produces and ignoredcoverage,config-examples, everycross-buildmatrix leg,sdk-metrics,fuzz-smoke, andSecurity Scan. The wait step is removed rather than corrected: which checks must pass now lives in branch protection onmain, which also governs human PRs and cannot drift from a regexp in a workflow file. Native auto-merge is enabled on the repository, without which the--autoflag would have failed even once the label matcheddocs-platform/docker-compose.ymlwas not valid YAML. Twohealthcheckentries put a bare URL inside a flow sequence, where the scanner readshttpas a plain scalar and then meets:in place of,or]. Docker's own parser is lenient enough to accept it, so it went unnoticed — butpre-commit run check-yaml --all-filesfailed on the file, which is the first thing a new contributor runs.gitignoredid not covercoverage/, the directorymake coveragewrites into. The three barecoverage.*filenames only match a profile written to the repository root, which no target producesscripts/setup-hooks.sh— the first commandCONTRIBUTING.mdtells a contributor to run — failed on any current macOS or Debian host, and exited 0 having installed nothing. Five defects:pip3 install pre-commitran first and dies withexternally-managed-environmenton a Homebrew or Debian Python (PEP 668), and because the installer was an if/elif chain testing only whether each command exists, a failingpip3never fell through to thebrewbranch that would have worked; the failure happened inside a condition, soset -euo pipefaildid not catch it; it installed gosec fromgithub.com/securecodewarrior/gosec, which is a 404 (the real module issecurego/gosec, whichsecurity.ymlalready uses); it pinned golangci-lint v1.55.2 against aversion: "2"config only v2.x can parse, handing contributors a lint failure that looks like their fault; and it wrote a.golangci.ymlif none was present, containing linters removed from golangci-lint years ago — now that a real config is committed, that branch would have overwritten it with an unusable one. Each install method is now tried until one succeeds,pipxfirst, every path verifies the command is onPATHafterwards, and the golangci-lint check is version-aware rather than presence-only. It also no longer overwrites.git/hooks/pre-commitwith a hand-rolled wrapper that blocked any commit touching a line matchingfmt.PrintorTODO, including inside a string literal or a comment explaining why a TODO is deliberatemakeprinted fouroverriding commands for targetwarnings on every invocation, includingmake help.BUILD_DIR := buildandCOVERAGE_DIR := coveragemade the directory-creation rule readbin build dist coverage:, colliding with the realbuildandcoveragetargets. The build worked — the later recipe wins, and both are.PHONY— but a build system that opens with four warnings reads as unmaintained, and the names would have genuinely collided the moment one stopped being.PHONY. Replaced with a%/.mkdirsentinel rule, which keeps the pattern out of the target namespace, declared as an order-only prerequisite so writing one binary does not rebuild its siblingspre-commit run --all-filescould not complete: thepretty-format-yamlhook crashed on import, because the pinned rev importspkg_resources, which modern setuptools no longer ships (Python 3.14 here). Bumped to a rev that does not. Fixing it exposed a second problem worth recording, since the obvious repair is the wrong one:check-yamlis PyYAML and follows YAML 1.1, where a bare URL in a flow sequence is a syntax error, whilepretty-format-yamlis ruamel and follows YAML 1.2, where it is legal — so quoting the URL to satisfy the first makes the second strip the quotes straight back off, and the two hooks disagree forever. Thedocker-compose.ymlhealthchecks are now block sequences, the one form both parsers accept and where there are no quotes left to remove.golangci.ymlis excluded from thepretty-format-yamlhook, which damages it: the formatter dedents every block sequence to its parent's column, destroying the nesting that makeslinters.settings.<name>readable and pushing each trailing comment out of alignment with the entry it annotates — and those comments are the recorded reason each linter is on or off. It also unquotes thepath:/text:regexes, leavingfuzz_.*_test\.gobare where the escapes were visibly protected. Both forms are valid YAML and golangci-lint parses either; the file is configuration a human has to read, and the formatter optimises for neither.markdownlint.yaml: MD010 (no-hard-tabs) now exempts code blocks. The hook runsmarkdownlint --fix, and at the rule's default the fixer silently rewrites tabs inside fenced blocks to spaces — so every Go sample in the repository was being converted to somethinggofmtwould immediately change back. It had already done this to thes3.NewBackendexample inREADME.md, on a commit that only touched badges. Tabs in prose are still flagged, which is the part of the rule worth having- The security issue template pointed at "Security → Advisories → New draft advisory" as prose rather than a link, on a repository where private reporting was not enabled — so the instruction was both unfollowable and, until now, describing a feature that was off. It now links directly to the advisory form and to
SECURITY.md
Removed
README_CROSS_PLATFORM.mdandRELEASE_NOTES_v0.1.0.md. The first documented installing and running anobjectfs-windows.exeand stated the filesystem "works on Windows/macOS/Linux"; Windows is unsupported, there is no Windows binding, andmake build-alldeliberately builds linux and darwin only. Nothing linked to either file- Three README badges that reported nothing.
codecovrendered "unknown" against a service nothing has ever uploaded to (coverage is gated per-package byscripts/coverage-gate.shin CI, which the CI badge already covers), Go Report Card rendered "retired", and Go Reference rendered empty becausego.moddeclared a module path that was not where the code lives (fixed below, #213). A badge that renders "unknown" reads as a broken project to anyone who looks at it and as a passing check to anyone who does not
Verify a download:
sha256sum -c objectfs-<platform>.tar.gz.sha256
Container image: ghcr.io/scttfrdmn/objectfs:0.10.2
Full changelog: https://github.com/scttfrdmn/objectfs/blob/v0.10.2/CHANGELOG.md