Status: Proposed
Author: itdir
Target: dw-0/kiauh upstream
Backward Compatible: Yes — zero behaviour change when no override is set
This proposal introduces a configurable base directory (BASE_DIR) for all
component and extension installations in KIAUH. Today, every install path is
hard-wired to Path.home() (the running user's home directory). This change
replaces those references with a single, centrally-defined BASE_DIR that
defaults to Path.home() and can optionally be overridden via:
- The
KIAUH_BASE_DIRenvironment variable (highest priority), or - The
base_diroption inkiauh.cfg(persisted configuration)
When neither is set, KIAUH behaves identically to upstream — every path
resolves to ~/component_name exactly as before.
KIAUH hard-codes Path.home() in ~25 module-level constants across
components and extensions:
# kiauh/components/klipper/__init__.py (upstream)
KLIPPER_DIR = Path.home().joinpath("klipper")
KLIPPER_ENV_DIR = Path.home().joinpath("klippy-env")
# kiauh/components/moonraker/__init__.py (upstream)
MOONRAKER_DIR = Path.home().joinpath("moonraker")
# kiauh/extensions/octoeverywhere/__init__.py (upstream)
OE_DIR = Path.home().joinpath("octoeverywhere")This design assumes a single user running a single Klipper stack in their home directory. It prevents several legitimate deployment scenarios.
| Scenario | Description |
|---|---|
| Multi-printer farms | A print farm operator wants separate Klipper stacks under /srv/printer1/, /srv/printer2/ |
| System-wide installs | A shared workstation where Klipper is installed under /opt/klipper-stack/ |
| Container/chroot deployments | Docker or systemd-nspawn environments where $HOME doesn't exist or is ephemeral |
| CI/CD testing | Automated test pipelines that need deterministic, non-home install paths |
| Backup portability | Backups that reference a configurable root instead of a user-specific home directory |
Resolution Order (first non-empty absolute path wins):
┌─────────────────────────────────┐
│ 1. KIAUH_BASE_DIR env variable │ ← Highest priority (ops/CI)
├─────────────────────────────────┤
│ 2. base_dir in kiauh.cfg │ ← Persisted user preference
├─────────────────────────────────┤
│ 3. Path.home() │ ← Default (upstream behaviour)
└─────────────────────────────────┘
| File | Change | Impact |
|---|---|---|
core/constants.py |
Add _resolve_base_dir() → BASE_DIR |
Single source of truth |
default.kiauh.cfg |
Add commented #base_dir: option |
Self-documenting |
core/settings/kiauh_settings.py |
Add base_dir to AppSettings |
Persistence support |
main.py |
Log non-default BASE_DIR at startup |
User feedback |
15× __init__.py (components) |
Path.home() → BASE_DIR |
Path consistency |
10× __init__.py (extensions) |
Path.home() → BASE_DIR |
Path consistency |
utils/fs_utils.py |
Path.home() → BASE_DIR in get_data_dir() |
Data dir resolution |
utils/sys_utils.py |
Check both home + BASE_DIR for NGINX perms |
Backward compat |
core/services/backup_service.py |
Search both home + BASE_DIR for fallback |
Backward compat |
components/moonraker/utils/utils.py |
Search both home + BASE_DIR for fallback |
Backward compat |
components/webui_client/client_utils.py |
Use tempfile.mkstemp() for nginx cfg tmp |
Safety fix |
-
Module-level constant, not a function call.
BASE_DIRis evaluated once at import time. This matches the existing pattern used bySYSTEMD,NGINX_SITES_AVAILABLE, etc. incore/constants.py. -
Config file is read with a minimal parser (not
KiauhSettings) to avoid circular imports. Component__init__modules importBASE_DIRbeforeKiauhSettingsis constructed. -
Env var takes priority over config file. This follows the standard twelve-factor app convention and allows ops teams to override the setting without modifying files.
-
Only absolute paths are accepted. Empty strings, whitespace, and relative paths silently fall back to
Path.home(), preventing misconfiguration. -
Backward-compatible fallback search. Backup and NGINX permission checks search both
Path.home()andBASE_DIRwhen they differ, so existing installations in~are always found.
Nothing. When KIAUH_BASE_DIR is unset and base_dir is absent from
kiauh.cfg, BASE_DIR resolves to Path.home() — the exact same value
upstream uses today. Every path remains ~/klipper, ~/moonraker,
~/printer_data, etc.
The diff touches ~25 files, but the pattern in each is identical:
- from pathlib import Path
+ from core.constants import BASE_DIR
- COMPONENT_DIR = Path.home().joinpath("component")
+ COMPONENT_DIR = BASE_DIR.joinpath("component")No logic changes. No new dependencies. No new classes. The only behavioural additions are in backup fallback search (search both directories) and NGINX permission checks (ensure both directories have execute rights).
- Zero-impact default. No behaviour change for the >99% of users who run KIAUH from their home directory.
- Single constant. One
BASE_DIRin one file, imported everywhere. Easy to audit, easy to grep. - Settings integration.
kiauh.cfgalready governs ports, repos, and update preferences —base_dirfits naturally. - Env var support. Standard mechanism for container/CI overrides without file changes.
- Backward-compatible fallbacks. Backup search and NGINX permissions handle
the case where
BASE_DIR ≠ Path.home()gracefully.
- Module-level constant.
BASE_DIRis evaluated at import time, so changing it requires restarting KIAUH. (This is the same constraint as all other constants incore/constants.py.) - Config file parsed twice.
_resolve_base_dir()does a lightweight read ofkiauh.cfgbeforeKiauhSettingsparses it fully. This is intentional (to avoid circular imports) but adds a small amount of duplication. - Touches many files. The diff spans ~25 files, which can be intimidating to review. However, each change is mechanical and identical.
- Multi-printer-per-host support. With a configurable base dir, KIAUH can
be invoked multiple times with different
KIAUH_BASE_DIRvalues to manage separate printer stacks on one machine. - Container-native deployments. Makes KIAUH usable in Docker, Podman, and
systemd-nspawn without special
HOMEmanipulation. - Easier automated testing. CI pipelines can set
KIAUH_BASE_DIRto a temporary directory, avoiding pollution of the test user's home. - Foundation for future features. A configurable root paves the way for profile management, per-printer settings, and fleet administration.
- Upstream merge conflicts. If upstream adds new
Path.home()references in new components or extensions, they would need to useBASE_DIRinstead. This is mitigable with a lint rule or CI check. - User misconfiguration. An incorrect
base_dirpath could cause KIAUH to install into an unexpected location. Mitigated by: (a) only accepting absolute paths, (b) logging the activeBASE_DIRat startup, and (c) leaving the option commented out by default.
- ✅ 100% backward compatible — default behaviour is unchanged
- ✅ Minimal API surface — one constant (
BASE_DIR), one env var, one config option - ✅ Follows existing patterns — uses the same module-level constant style as
SYSTEMD,NGINX_SITES_AVAILABLE - ✅ Self-documenting —
default.kiauh.cfgincludes commented documentation - ✅ Startup feedback — non-default base dir is logged at launch
- ✅ Security improvement — nginx config temp files use
tempfile.mkstemp()instead of predictable~/name.tmp - ✅ Dual-search fallback — backups find data in both
~andBASE_DIR
⚠️ Large diff — touches ~25 files (all mechanical, same pattern)⚠️ Ongoing maintenance — new components must useBASE_DIRinstead ofPath.home()⚠️ Not runtime-changeable — requires restart to take effect (same as all other constants)⚠️ Dual config parse —kiauh.cfgread once by_resolve_base_dir()and once byKiauhSettings
No migration needed. KIAUH works exactly as before.
To use a custom base directory:
# Option A: environment variable (temporary / per-session)
export KIAUH_BASE_DIR=/opt/klipper-farm
./kiauh.sh
# Option B: persistent configuration
# Edit kiauh.cfg and add under [kiauh]:
# base_dir: /opt/klipper-farmWhen adding a new component or extension, use BASE_DIR instead of
Path.home():
from core.constants import BASE_DIR
MY_COMPONENT_DIR = BASE_DIR.joinpath("my-component")| Approach | Pros | Cons |
|---|---|---|
| This proposal (BASE_DIR constant) | Simple, one constant, follows existing patterns | Touches many files |
Monkey-patch Path.home() |
Zero file changes needed | Fragile, affects all Python code, terrible practice |
| Runtime path resolver function | Lazy evaluation possible | Every path access becomes a function call; breaks existing APIs |
| Centralized path registry | All paths in one file | Massive refactor; breaks all existing imports |
| Symlink-based approach | No code changes | Fragile, OS-dependent, hard to debug |
The proposed approach (module-level BASE_DIR constant) offers the best
trade-off between simplicity, compatibility, and maintainability.
# Default behaviour (no override)
PYTHONPATH=kiauh python3 -c "
from core.constants import BASE_DIR
from pathlib import Path
assert BASE_DIR == Path.home()
print('PASS: default')
"
# Environment variable override
KIAUH_BASE_DIR=/opt/test PYTHONPATH=kiauh python3 -c "
from core.constants import BASE_DIR
assert str(BASE_DIR) == '/opt/test'
print('PASS: env var')
"
# Invalid values fall back to home
KIAUH_BASE_DIR='' PYTHONPATH=kiauh python3 -c "
from core.constants import BASE_DIR
from pathlib import Path
assert BASE_DIR == Path.home()
print('PASS: empty env')
"
KIAUH_BASE_DIR='relative' PYTHONPATH=kiauh python3 -c "
from core.constants import BASE_DIR
from pathlib import Path
assert BASE_DIR == Path.home()
print('PASS: relative env')
"- Run KIAUH without any override → verify all paths use
~ - Set
KIAUH_BASE_DIR=/tmp/kiauh-test→ verify startup log message - Add
base_dir: /tmp/kiauh-testtokiauh.cfg→ verify paths update - Set both env var and config → verify env var wins
This proposal provides a clean, backward-compatible mechanism for configuring KIAUH's installation base directory. It follows existing codebase conventions, integrates with the settings system, and opens the door for multi-printer and container deployments — all without changing the experience for existing users.
We respectfully request the upstream maintainers consider this change for inclusion in a future KIAUH release.