SHELL
Runs a shell script on the device. The general-purpose action for when no specialised type fits. Add a detection script and SHELL becomes idempotent: the agent only runs the remediation script if the detection script reports drift.
For one-off commands that don't need idempotency, use SCRIPT_RUN. Same parameters, different lifecycle: SCRIPT_RUN runs once per dispatch and is never stored for scheduled re-runs.
Parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
script | string | conditional | — | The remediation script body. Max 1 MiB. At least one of script or detection_script is required — server-side validation and the agent both reject a SHELL action with neither set. |
interpreter | string | no | /bin/sh | Path to the interpreter to invoke. Max 255 chars. |
run_as_root | bool | no | false | true escalates through the privilege backend (sudo / doas / direct root). false runs the script once per active graphical desktop session, as that user — with nobody signed in it's a success no-op that retries on the next reconciliation tick. |
working_directory | string | no | agent's cwd | Absolute path to run in. Must start with /. |
environment | map<string,string> | no | — | Environment variables for the run. Screened by the SDK's env-hijack blocklist — PATH, LD_*, LANG/LC_*, and similar are rejected. |
detection_script | string | no | — | Optional idempotency check. Exit 0 means "compliant, skip the remediation". Max 1 MiB. |
is_compliance | bool | no | false | If true, run only the detection script and report status. Never run the remediation. |
How it decides what to run
- If
detection_scriptis set, run it. - Exit 0 means compliant. Skip remediation, report
changed=false. - Non-zero exit and no
scriptset means non-compliant. Report it. - Non-zero exit with
scriptset means run the remediation. - Re-run the detection script to verify the remediation worked — if it still exits non-zero, the action fails with "remediation did not resolve the issue".
If detection_script is unset, the remediation script runs every time and the action always reports changed=true.
is_compliance=true makes the agent run only the detection step. The remediation is ignored even when present. That's the mode for read-only audit policies.
Examples
Idempotent: ensure /etc/motd has a custom banner.
type: SHELL
detection_script: |
grep -q "Property of ACME" /etc/motd
script: |
echo "Property of ACME. Authorised use only." | sudo tee /etc/motd > /dev/null
run_as_root: true
One-off: report disk usage on every dispatch.
type: SCRIPT_RUN
script: df -h
Compliance-only check: report whether SELinux is enforcing.
type: SHELL
detection_script: |
test "$(getenforce 2>/dev/null)" = "Enforcing"
is_compliance: true
Gotchas
- The exit code of the remediation script doesn't gate idempotency — only the detection script does. But a non-zero exit from either script fails the action (
script exited with code <n>). It doesn't auto-retry; the next reconciliation tick handles that. run_as_root: falseis not "run as the agent's user". It fans out to every active desktop session and runs as each signed-in user, prefixing output lines with[user=<name>]. One user's failure doesn't stop the rest; the first failure decides the action result.- The script body is passed as an argument (
<interpreter> -c <script>), not on stdin./bin/bash,/usr/bin/python3,/usr/bin/perlall work asinterpreteras long as they accept-c. - The child environment is a curated baseline (
HOME,USER) plus your validatedenvironmententries — the agent's own environment does not leak through, and the SDK runner forcesLC_ALL=Cand a sanitizedPATH. - Secrets in
scriptordetection_scriptget redacted from the audit log, but they're sent to the agent in cleartext over mTLS. For credentials at rest, useLPS,ENCRYPTION, or the IdP credential store instead. - The detection-verify-retry sequence runs detection twice if the remediation script ran. Budget for that.