paths
paths
¶
Platform-aware path resolution for the terok ecosystem.
Provides generic namespace resolvers that any sibling package can
call to place its state/config/runtime under the shared terok/
namespace.
Resolution priority for each resolver:
- Package-specific override (
env_varargument) TEROK_ROOTenv var (namespace-wide override; state only)- Platform default (FHS root paths or XDG dirs via
platformdirs)
Layered config-file reading (/etc/terok/config.yml →
~/.config/terok/config.yml) is a sibling-package concern — packages
that want it compose ConfigStack
against these resolvers themselves.
__all__ = ['config_file_paths', 'host_uid', 'namespace_config_dir', 'namespace_runtime_dir', 'namespace_state_dir', 'read_config_section', 'read_config_top_level']
module-attribute
¶
host_uid()
¶
Return the current process's UID as the initial user namespace sees it.
Inside an unprivileged user namespace (rootless podman / crun
hook, sandboxed CI runner, unshare -U), os.geteuid() returns
the inner-userns UID — typically 0 even when the operator
really ran the program as UID 1000. Network peers (D-Bus
SO_PEERCRED, AUTH EXTERNAL) and kernel-level checks see the
outer (host) UID via the userns uid_map translation, so a
process that advertises its inner UID over the wire is rejected for
a credential mismatch. This helper hands callers the outer UID those
peers expect.
The mapping comes from /proc/self/uid_map. When it is
unavailable (macOS, BSD, exotic chroot) or no row covers the
effective UID, the bare geteuid() answer is returned — correct on
systems without Linux user namespaces.
Source code in src/terok_util/paths.py
config_file_paths()
¶
Ordered config.yml locations with scope labels (lowest → highest priority).
TEROK_CONFIG_FILE → single override (no layering). Otherwise:
/etc/terok/config.yml (system) → ~/.config/terok/config.yml
(user). Root processes see only the system path.
Public so consumers can render an "edit one of these to override X" hint to the operator (which file gets the highest priority, where on disk the operator would put the override, etc.).
Source code in src/terok_util/paths.py
read_config_section(section)
¶
Read a top-level section from layered terok configs (cached, fail-silent).
Merges system and user config.yml files via
ConfigStack — user values
override system defaults at the leaf level. Lazy-imports
config_stack so importing paths doesn't drag the YAML
parser into a process that only needs the platform defaults.
Source code in src/terok_util/paths.py
read_config_top_level(key)
¶
Read a top-level scalar / list / mapping from layered terok configs.
Counterpart to
read_config_section for
keys whose value isn't a dict — e.g. the ecosystem-wide
experimental: true opt-in or a bare log_level: debug knob.
Returns the merged value (user wins over system) or None when
the key is absent or the config files can't be loaded. Cached for
the lifetime of the process; reaches for the _config_top_level_cache
private to flush in tests.
Source code in src/terok_util/paths.py
namespace_state_dir(subdir='', *, env_var=None)
¶
Resolve a state directory under the terok/ namespace.
Priority:
- env_var (package-specific override, e.g.
TEROK_SANDBOX_STATE_DIR) TEROK_ROOTenv var (namespace override)config.yml→paths.root(Podman model — all packages honour it)- Platform default (
/var/lib/terok/<subdir>for root, XDG data dir otherwise)
env_var is keyword-only so a positional second argument can never accidentally be reinterpreted as an override name.
Source code in src/terok_util/paths.py
namespace_config_dir(subdir='', *, env_var=None)
¶
Resolve a config directory under the terok/ namespace.
Priority: env_var → /etc/terok/<subdir> (root) → platformdirs
→ ~/.config/terok/<subdir>. env_var is keyword-only.
Source code in src/terok_util/paths.py
namespace_runtime_dir(subdir='', *, env_var=None)
¶
Resolve a runtime directory under the terok/ namespace.
Priority: env_var → /run/terok/<subdir> (root)
→ $XDG_RUNTIME_DIR/terok/<subdir> → $XDG_STATE_HOME/terok/<subdir>
→ ~/.local/state/terok/<subdir>. env_var is keyword-only.