hum-paths
every filesystem path hum touches lives in this crate. Other crates ask, never assume.
If a path appears in another crate as a literal ("~/.config/hum/...",
PathBuf::from(env!("HOME")).join(...), hard-coded socket paths,
hard-coded state directories), that is drift. The convention is one
direction only: anywhere in the workspace that wants a path goes
through hum_paths::*. This crate is small and stable on purpose, so
the rest of the workspace can lean on it.
Layout
The hum process tree is anchored at four XDG roots:
| function | resolves to | what lives here |
|---|---|---|
state_dir() | $XDG_STATE_HOME/hum | identity key, thehum log, drift rings, runtime info, bees snapshot |
config_dir() | $XDG_CONFIG_HOME/hum | hum.json, peers.json, orch.d/, hum.orch |
data_dir() | $XDG_DATA_HOME/hum | installed source clone, recipes |
runtime_dir() | $XDG_RUNTIME_DIR/hum | thrum socket, http socket, penny.json |
Every concrete path is one accessor down from these:
hum_paths::humd_key() // state_dir/humd.keyhum_paths::hum_json() // config_dir/hum.jsonhum_paths::peers_json() // config_dir/peers.jsonhum_paths::thrum_sock() // runtime_dir/thrum.sockhum_paths::penny() // runtime_dir/penny.jsonhum_paths::drift_dir() // state_dir/drift/hum_paths::thehum_dir() // state_dir/thehum/hum_paths::runtime_info() // state_dir/runtime-info.jsonhum_paths::bees_snapshot() // state_dir/bees.jsonhum_paths::bee_key(kind) // state_dir/bees/<kind>.keyhum_paths::hum_bin(kind) // local_bin/<kind>hum_paths::humd_bin() // local_bin/humdinit()
Call once at process startup, before any other accessor.
hum_paths::init();init() reads HOME and sets any unset XDG env var to its
HOME-relative default. After it runs, every subsequent accessor in the
process resolves cleanly off the env without fallback logic. Without
init() first, accessors will still work in most cases (the XDG vars
are usually already set), but tests and embedders that need explicit
XDG control rely on init() having normalized them.
init() panics if HOME is unset. That is always a configuration
error, not something a graceful fallback should hide.
RuntimeInfo
The one in-process write surface this crate owns is RuntimeInfo, a
small struct humd writes to state_dir/runtime-info.json when it
boots and removes when it shuts down. Other tools (hum status,
hum ensemble, dev scripts, third-party probes) read it to learn the
running daemon’s pid, socket path, version, and the wire-shaped reach
hints (ensemble_addrs: iroh:<node>, iroh-ip:<sockaddr>, tcp:...)
a peer would paste into its peers.json.
let info = RuntimeInfo { socket: bound_socket.to_path_buf(), pid: process::id(), version: env!("CARGO_PKG_VERSION").into(), thrum_version: thrum_core::THRUM_VERSION.into(), bound_at_ms: now_ms(), ensemble_addrs: peer_reach,};info.write()?; // atomic: tmp + renameRuntimeInfo::read() returns Option<Self>. Missing or unreadable
file resolves to None, never an error. The file is best-effort
discovery, not a contract; the daemon survives without it (and
removes it on clean shutdown).
What this crate does not own
- Config values.
hum_paths::hum_json()returns the path tohum.json. Loading it, validating against the schema, and parsing the values lives inconfig. - Network addresses. A socket address is not a filesystem path.
humd.tcpListen = "0.0.0.0:14601"lives inconfig, not here. - Repo-source paths.
codegen::pathsownsthrum-core/src/chi.rsandthrum-clients/{ts,python,go}/...because those are build-time, repo-local concerns, anchored toCARGO_MANIFEST_DIR. hum-paths is runtime/user-side, anchored to XDG.
The split is deliberate. hum-paths answers “where does humd find runtime stuff on the user’s host”. codegen::paths answers “where in the source tree does codegen read inputs and emit outputs”. Different concerns; no overlap.
Adding a path
When a new artifact lands somewhere on disk:
- Add a
pub fn foo() -> PathBufthat returns the resolved path. Anchor onstate_dir()/config_dir()/data_dir()/runtime_dir()as fits the lifecycle of the artifact. - Add a basename const if the file name is reused elsewhere (the
*_BASENAMEconstants insrc/lib.rsare the pattern). - Call it from the rest of the workspace. Never let a literal
~/.config/hum/...orstate_dir-style string land in another crate.