Shared setup runner
setup/runner.common.sh is implementation
scaffolding for managed setup entrypoints. It is not a standalone
feature and must not be piped directly into a shell.
The helper provides:
- one-time sudo authentication through
/dev/tty; - exact operator confirmations through
/dev/ttyfor an explicitly selected, high-impact feature action; - noninteractive delegated-root commands using
sudo -n; - unique per-invocation runtime directories;
- an optional
devs-guideoransibleruntime namespace selected explicitly by the owning feature, with the existing namespace retained by default; - path-constrained cleanup;
- one unprivileged HTTP(S) implementation for managed runner-source files;
- local-copy or published-source manifest staging;
- verification that every declared dependency is regular, readable, and non-empty before a privileged command consumes it;
- release-helper staging and syntax validation;
- Ansible feature preparation, clean controller bootstrap, explicit playbook execution, and preflight report helpers shared by migrated CLI runners.
Delegated-root flow
For migrated runners, wget | bash is the primary
streamed form:
- The unprivileged shell downloads the feature and its small shared-runner bootstrap.
- Read-only preflight continues without sudo.
- A managed mode accepts an existing root, cached, or
passwordless session. Otherwise it runs
sudo -vexactly once with input from/dev/tty. A feature can also require an exact typed acknowledgement through that same TTY before it permits an explicitly requested high-impact action; there is no environment-variable or noninteractive bypass. - The original process stages and verifies release helpers, Ansible files, and support files.
- Privileged package and Ansible commands run through
noninteractive
sudo -nwith a clean explicit environment.
No runner source or managed dependency is downloaded a second
time from inside sudo. wget | sudo bash remains
supported when a caller deliberately starts the whole runner as
root. sudo wget | bash is not equivalent because it
leaves the shell unprivileged.
Without root, cached/passwordless sudo, or a usable
/dev/tty, managed modes fail before privileged
activity. Noninteractive callers should allocate a TTY with
ssh -t, establish a credential first, or use narrowly
scoped passwordless sudo. Interactive feature inputs such as
NVLink --select-gpus independently require
/dev/tty.
Feature manifest contract
Migrated CLI features declare four source-neutral arrays:
GROUP_VARS_FILES=("all.yml" "debian.yml")
FEATURE_PLAYBOOKS=("cli/example.yml")
RUNTIME_SUPPORT_REFS=("packages.yml" "files/example/helper")
FEATURE_TEMPLATE_REFS=("templates/example.conf.j2")runner.stage.ansible.feature combines these arrays
into one verified manifest. All four arrays are required, even
when a category is empty. A repository checkout copies every
dependency into the isolated runtime directory. A streamed runner
downloads the same relative paths from the published
ansible/ tree. If a local manifest is incomplete, the
runner fails instead of silently mixing local and published
revisions.
The initial runner.common.sh download is
necessarily a small unprivileged bootstrap performed by the
streamed entrypoint. Once loaded, all release helper, playbook,
task, file, and template staging goes through the shared
implementation. Those managed source-file downloads are never
performed inside sudo.
After the bootstrap trust boundary is established, migrated features use:
runner.source.release.commonto stage, syntax-check, and source the release helper;runner.prepare.ansible.featureto stage the declared manifest and build group-variable and playbook path arrays;runner.ensure.local.ansibleto invoke the existing controller bootstrap through a clean delegated-root environment;runner.run.ansible.playbooksto run explicit playbook paths with one verified extra-vars file;runner.report.commandandrunner.report.textto append read-only preflight evidence.
Feature-specific argument parsing, policy checks, extra-vars
rendering, preflight contents, and managed-mode decisions remain
in each CLI runner. The optional third argument to
runner.create.runtime and
runner.adopt.runtime selects the allow-listed runtime
namespace. ipmctl uses ansible, while existing
callers that omit it retain their current behavior.
Migration status
The GPU, ipmctl, NVIDIA, NVLink, LLM-host, llama.cpp, and KTransformers runners use the shared privilege and staging contracts:
setup/cli/gpu.shsetup/cli/ipmctl.shsetup/cli/llm/host.shsetup/cli/llm/llamacpp.shsetup/cli/llm/ktransformers.shsetup/cli/nvidia.shsetup/cli/nvlink.sh
The following legacy runners still contain privileged self-reentry and a second fetch when started unprivileged from a stream:
setup/bootstrap.shsetup/hardware.shsetup/autologin.shsetup/cli/node.shsetup/cli/codex.shsetup/cli/kiosk.app.shsetup/cli/x11.shsetup/cli/openbox.shsetup/cli/touchscreen.shsetup/cli/startx.shsetup/cli/tauri.sh
They remain unchanged until each runner has equivalent privilege, argument, fetch, and staging regression coverage. New managed features should use the shared API rather than copy a legacy self-reentry implementation.