fix: use FHS-compliant /var/lib/numa as Linux data dir default #43
Reference in New Issue
Block a user
Delete Branch "fix/linux-fhs-paths"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
numahardcodes/usr/local/var/numaas the default system-wide data directory for all Unix platforms. This is the right path on macOS (Homebrew prefix convention) but non-FHS on Linux, where Arch / Fedora / Debian / SUSE expect persistent state under/var/lib/<pkg>.The mismatch was invisible to existing users (numa silently creates the directory on first run) but became sharp the moment community packaging arrived: #33 had to add fragile sed-based path-patching at PKGBUILD build time, and even that patching was incomplete (only matched a stale comment).
How
A small helper extraction in
src/lib.rs:Both
data_dir()andconfig_dir_unix()route throughdaemon_data_dir()so there's a single source of truth.Linux behavior table
legacyexistsfhsexists/var/lib/numa/usr/local/var/numa/var/lib/numa/var/lib/numaMigration safety
The legacy fallback is the critical row above. Existing v0.10.0 Linux users have their CA cert +
services.jsonunder/usr/local/var/numa. If we returned the new path unconditionally, every browser that had trusted the previous CA would start throwing cert errors after the upgrade because numa would generate a fresh CA at the new location.The fallback is checked at startup via
std::path::Path::exists— zero-config, no migration command needed. Users can manually move the data to/var/lib/numalater if they want; the helper handles both states.macOS behavior
Unchanged.
/usr/local/var/numais still correct because Homebrew's prefix is/usr/local.Tests
The path-decision logic is extracted as a pure function
resolve_linux_data_dir(legacy_exists: bool, fhs_exists: bool)gatedcfg(any(target_os = "linux", test))so the same code path is unit-tested on every platform's CI run, regardless of the host OS.Four tests cover all combinations:
Plus a new end-to-end integration test in
tests/docker/smoke-arch.shthat builds numa from source insidearchlinux:latest, runs it with NO[server] data_diroverride (so the production code path fires), and asserts:/var/lib/numa/ca.pem(FHS)/usr/local/var/numadoesn't exist (no accidental dual-creation)Why this exists at all (retrospective)
The
tests/docker/install-trust.shcontract test added in #41 doesn't cover this — it tests the shell command contract (doesupdate-ca-trustactually put the cert in the system bundle?) and doesn't run numa code at all. Convention bugs like "the data directory isn't where Linux packagers expect it" are invisible to behavior-focused tests because numa works either way; the path is just non-idiomatic.PR #33 itself was effectively the test that surfaced this — a community contributor (@CaseyLabs) packaging numa for Arch noticed the convention mismatch and submitted a fix. We didn't catch it ourselves because the dogfood machine is macOS, where the paths are correct.
Test plan
cargo fmt --checkcleancargo clippy --libcleancargo test --lib— 134/134 pass locally (4 new path-decision tests included)cargo build+cargo test+cargo clippy -- -D warningson every platform's real toolchain. Thecfg(any(target_os = "linux", test))gate makes the four new tests run on macOS and Windows hosts too, not just Linux.tests/docker/smoke-arch.sh: fresharchlinux:latestcontainer, builds numa from source, starts it without adata_diroverride (exercises the live wiring), confirms/var/lib/numa/ca.pemexists and/usr/local/var/numais absent. Verified locally on Apple Silicon (Rosetta + named cargo cache, ~1m 9s warm). Output:linux_data_dir_upgrading_install_keeps_legacy. Integration test for the upgrade scenario (pre-create/usr/local/var/numa/, start numa, confirm legacy path is preserved) deferred — the underlying conditional is a singleif legacy_exists && !fhs_existsstatement, fully unit-tested. Acceptable risk per the deferral pattern used in #41 (Windows behavioral test) and #42 (no automated test for shell-call substitution).What this unlocks
Once shipped in v0.10.1, PR #33 (Arch AUR packaging) becomes much simpler:
/usr/local/var/numapaths inprepare()— numa already does the right thing on LinuxCloses the convention gap between numa's defaults and Linux packaging norms.
🤖 Generated with Claude Code