v1.8.0 — hardening release
This is the hardening release. Everything in it is about making farhand
trustworthy under a skeptical eye: the security claims are enforced by machines
rather than prose, the unsafe surface is documented and fenced, and the two
monolithic source files are gone.
Security defaults that used to be opt-in
fhdnow refuses to start without an authentication token unless
--allow-unauthenticatedis passed explicitly. Empty tokens are rejected as
a misconfiguration, and non-loopback binds warn loudly.- Token comparison is constant time (SHA-256 digests) across HELLO/RUN,
STATUS, HISTORY, and CLEAN — no length or prefix leakage. - Frame memory now grows only as bytes actually arrive (64 KiB incremental
reads) instead of trusting the length header, with a 1 MiB pre-authentication
cap. A malicious header can no longer induce a large allocation. - New
--max-connections(default 32) and--max-queued-runs(default 16). - Environment forwarding blocks infrastructure credentials (
AWS_,GITHUB_,
NPM_,DATABASE_, …) and credential suffixes (*_TOKEN,*_SECRET,
*_API_KEY, …).fh --print-envlists exactly which names would be sent —
values are never shown.
A real data-corruption fix
Workspace CoW cloning shelled out to cp -c -R and fell back to hardlinks.
A hardlinked file modified in place — a formatter, git checkout, an editor
save — silently corrupted the shared CAS object or seed workspace behind it.
Cloning is now pure Rust (clonefile / FICLONE / copy, preserving modes,
mtimes, and symlinks) and the hardlink fallback is gone entirely.
Correctness and operations
-
fhnow runs on Windows at all. Windows gives the main thread a 1 MiB
stack where Linux gives 8 MiB, and the client needed more than that before
doing any work — it aborted withSTATUS_STACK_OVERFLOW, even
fh --version. The work now runs on a thread with an explicit stack, and CI
runs both binaries underulimit -s 1024so it cannot regress silently. -
CAS garbage collection (
--cas-ttl-days, default 30 /--cas-max-gb):
storage growth was previously unbounded. Eviction is LRU-by-use. -
Collision-free run IDs (were
millis ^ pid, which collided for runs
started in the same millisecond and clobbered STATUS entries). -
Blocking filesystem work moved off the async runtime via
spawn_blocking. -
A queued run whose client disconnects now frees its slot immediately.
CI, MSRV, and receipts
- MSRV corrected from a false 1.75 to 1.88, and now enforced in CI. The old
claim was untrue twice: the dependency graph needs 1.88, and our own
is_multiple_ofneeds 1.87. We briefly pinnedtimeto reach 1.85, then
rejected it because that version carries RUSTSEC-2026-0009 — the
advisory-fixed dependency and the honest floor won. - CI is now a 3-OS matrix plus MSRV, coverage (Codecov), cargo-deny, RustSec
audit, and a fuzz smoke pass; nightly adds ThreadSanitizer, a deep fuzz run,
and benchmark trends. cargo-deny is green with zero exceptions. All
third-party actions are pinned by commit SHA. - Real cargo-fuzz targets (
fuzz_read_frame,fuzz_unpack_tar,
fuzz_wire_paths) asserting protocol invariants, with committed seed corpora
for known attack vectors (traversal tar, absolute-path tar, lying header). - All 11
unsafeblocks carry SAFETY contracts; five crates now
#![forbid(unsafe_code)]outright and CI fails on undocumented unsafe. - Cancellation is now proven, not assumed. The previous disconnect test
dropped the connection, slept 500 ms, and asserted nothing. It now runs a
two-level process tree remotely and requires both the direct child and the
grandchild to die, plus proof the cancelled run released its concurrency
permit. It was validated by mutating the daemon to kill only the direct
child — the test fails with the grandchild surviving, which is precisely the
orphan-process bug the design forbids.
Architecture
fhd/src/lib.rs: 2,209 → 1,232 lines, split intoactive,exec,
stream, andsessionmodules.fh/src/main.rs: 1,883 → 1,609 lines, with the clap surface incli.rs,
RunParamsreplacing 18 positional arguments, one sharedperform_handshake,
and watch mode in its own function.
All refactoring moved code verbatim — no behavior changes — and the per-suite
test counts were verified after every step.
Upgrade notes
fhdnow needs a token. Start it with--token <token>or
FARHAND_TOKEN=<token>, or pass--allow-unauthenticatedif you are
deliberately running tokenless on a trusted loopback interface.- Build with Rust 1.88 or newer (edition 2021).
Full details in CHANGELOG.md.
Assets include archives for Linux (x86_64/aarch64 musl), macOS (x86_64/aarch64),
and Windows, each with a .sha256 checksum.