Host provider
The Host provider runs jobs directly on the local machine shell during loom run --local. It is the fastest path to execution — no containers, no image pulls, no startup overhead.
When Loom uses host
Loom selects the Host provider when a job has no image: value in its resolved definition:
lint:
stage: ci
target: linux
script:
- task lint
If a job sets image:, Loom uses the Docker provider instead.
Prerequisites
- A POSIX shell available as
shinPATH. - Required tools installed in the local environment.
- Linux local execution target (current MVP scope).
Runtime behavior
The Host provider:
- Executes via
sh— runs each script entry assh -lc "<instrumented script>"(orsh -x -cwhen debug tracing is enabled viaLOOM_DEBUG_TRACE). - Inherits the local environment — the process environment of
loom run --localis inherited, then jobvariables:are applied on top. - Captures output — stdout, stderr, and exit code are recorded into structured runtime events.
- Sets
LOOM_PROVIDER=host— this variable is available in every host job.
Shell rules and gotchas
Host jobs execute under POSIX sh. Each script: entry is a single command string — embedded newlines are not allowed. For the full syntax rules, see Syntax (v1) → script.
Multiline YAML blocks are rejected
Using | produces embedded newlines and fails validation:
# BAD — will not validate
bad:
stage: ci
target: linux
script:
- |
echo "this has a newline"
echo "and will not validate"
Instead, use separate list entries:
# GOOD — each entry is a single command
good:
stage: ci
target: linux
script:
- echo "first command"
- echo "second command"
Variable expansion
Host uses sh, not bash. Prefer portable constructs:
script:
- printf '%s\n' "$MY_VAR"
Avoid bash-specific features like [[ ]], arrays, or process substitution.
Environment inheritance and security
Host jobs inherit the full environment of the loom run --local process. This can unintentionally expose local credentials, proxy settings, or secrets exported in your shell.
Best practices:
- Use explicit
variables:for every value the job needs. Don't rely on ambient environment. - If sharing logs or receipts for support, follow What to share — avoid pasting raw environment dumps.
- For stronger isolation, consider the Docker provider which starts with a clean container environment.
Deterministic toolchains
Host execution depends on local machine state. Two developers with different tool versions will get different results.
Strategies to reduce drift:
| Strategy | Example |
|---|---|
| Pin tool versions | Use lockfiles (pnpm-lock.yaml, go.sum) and explicit version specs |
| Use a task runner | task lint ensures the same commands run everywhere |
| Wrap with Nix | Running tools inside a Nix shell pins exact versions across machines |
Confirming the Host provider ran
- Check the resolved workflow: the job must have no
image:. If you use templates or defaults, inspect the output ofloom compile --workflow .loom/workflow.yml. - Check runtime logs: follow the pointer chain from
pipeline/manifest.json→jobs/<job_id>/manifest.json→system/provider/events.jsonl. The provider events recordhostas the selected provider.
For the full log structure, see the Runtime logs contract.
Troubleshooting
| Error / symptom | Cause | Fix |
|---|---|---|
shell "sh" not found in PATH | sh not available | Install a POSIX shell or fix PATH |
| Tool not found errors | Missing dependencies | Install the required tools in your local environment |
| Different behavior than Docker job | Environment mismatch | Confirm the job does not set image: — host inherits local state while Docker uses a clean image |
| Script fails with "newline in command" | Multiline YAML block | Use separate script: list entries instead of | blocks |
Limitations
- Host runs depend on the local machine environment — no built-in isolation.
- Host provider selection is automatic when no
image:is set; there is no separate flag to force it. - Weaker reproducibility compared to Docker — results vary with local tool versions and configuration.