SaltStack deploys AI Watch to Linux fleets the config-management way: Salt
installs the runlayer-aiwatch package on each minion and manages the two
config files, so a state.apply both rolls out and continuously asserts the
Detect-only contract. This is the same Linux (Detect only)
package — see that page for supported distributions, package internals, and the
scan cadence. This page covers only the Salt-specific delivery.
An example formula ships in the repo at cli/packaging/linux/salt/
(runlayer-aiwatch/init.sls, the config templates, and pillar.example.sls).
It is a starting point, not a supported product — copy it into your own Salt
tree and adapt it to your conventions.
What it does
- Installs the
.deb / .rpm from your own Salt fileserver
(salt://runlayer-aiwatch/), selecting the right filename by OS family.
- Manages
/etc/runlayer/aiwatch/config.json (0644, non-secret) and
/etc/runlayer/aiwatch/credentials (0600, root-only) from pillar, so Salt is
the source of truth for both.
- Ensures the cron daemon is enabled. Cron re-reads the packaged
/etc/cron.d/runlayer-aiwatch on its own; the config files trigger nothing.
Linux is Detect-only: cron-scheduled scans across all users, no Enforce,
Sessions, or browser extension. In the dashboard the device shows Detect =
Active and Enforce / Sessions = Disabled — the expected state, not an error.
Prerequisites
-
A Salt master with the minions enrolled, plus a cron daemon on each minion
(the package pulls one in automatically — see Linux).
-
The
.deb and .rpm on your Salt fileserver under salt://runlayer-aiwatch/.
The Runlayer CDN is token-gated, so fetch the packages once with your
download token and serve them from salt:// — Salt itself never talks to the
CDN:
VER=1.2.3
DEST=/srv/salt/runlayer-aiwatch
mkdir -p "$DEST"
# -f: fail on HTTP >= 400 — a wrong/expired token must fail HERE, not write
# an error page into the package file (which would only surface later as a
# confusing "not a Debian/RPM package" error from pkg.installed).
for f in runlayer-aiwatch_${VER}_amd64.deb runlayer-aiwatch-${VER}-1.x86_64.rpm SHA256SUMS; do
curl -fsS -H "x-runlayer-download-token: <token>" -o "$DEST/$f" \
"https://downloads.runlayer.com/ai-watch/${VER}/$f"
done
# Verify integrity before serving via salt:// (SHA256SUMS also covers the
# tarball, which isn't downloaded here — check just the two packages).
(cd "$DEST" && grep -E '\.(deb|rpm)$' SHA256SUMS | sha256sum -c -)
Contact your Runlayer account team if you don’t have a download token yet.
Pillar keys
The formula reads a single runlayer_aiwatch pillar. Copy pillar.example.sls,
fill it in, and target it at the right minions:
| Key | Goes to | Notes |
|---|
host | config.json → Host | Tenant URL. World-readable, non-secret. |
api_key | credentials (0600) | Org rl_org_... key, secret — never in config.json. |
version | package filename | Version to install (no leading v); must match the file on your fileserver. |
host_override | credentials → RUNLAYER_HOST | Optional backend-host override. |
Never put the org API key in config.json, and never commit it in plaintext.
config.json is world-readable by design so unprivileged per-user scan children
can read managed settings; the key belongs only in the 0600 credentials file.
Keep the plaintext key in a GPG-encrypted pillar (Salt’s #!gpg|yaml
renderer decrypts it on the master at compile time) or your existing secrets
backend. The formula writes the credentials file with show_changes: False so
the key is never echoed into Salt job returns or master logs.
config.json always renders "Sessions": false and "Enforcement": false —
the formula hard-sets them, so the Detect-only contract holds no matter what the
pillar says.
Apply
salt '*' state.apply runlayer-aiwatch
The first apply installs the package and writes both config files; cron fires
the first scan on the next 15-minute tick.
Verify
In the Runlayer dashboard, go to Shadow AI → Devices: the device appears
with OS = Linux, Detect = Active, and Enforce / Sessions = Disabled
within about 15 minutes. Disabled Enforce/Sessions is the correct Detect-only
state on Linux. For on-host verification (sudo /usr/lib/runlayer/run-aiwatch-scan.sh),
see Linux (Detect only).
Idempotency
A second state.apply is a no-op once the installed package and the two
config files match the pillar — Salt reports no changes. To upgrade, bump
version in the pillar (and place the new .deb / .rpm on your fileserver);
the next apply installs the newer package in place. Your managed config.json
and credentials are re-asserted from pillar on every run.
Other config managers
This generalizes directly: install the .deb / .rpm, manage config.json
(0644) and credentials (0600), and ensure cron runs — the same three steps
port to Ansible, Puppet, or Chef.