This repository contains macOS scripts to deploy Aikido Safe Chain per-user in an Iru (Kandji) environment where scripts execute at machine scope.
kandji-safe-chain-detect.sh
Kandji detection script. Enumerates local user profiles and checks Safe Chain compliance per user.kandji-safe-chain-remediate.sh
Kandji remediation script. Installs or updates Safe Chain per user when non-compliant.instructions.MD
Build plan and implementation notes.mosyle-safe-chain.sh
Mosyle (single custom command): merged detect + remediate. Seemosyle-readme.mdfor scheduling andSAFE_CHAIN_MOSYLE_MODE.mosyle-readme.md
Mosyle deployment notes (daily schedule, logs, parity with Kandji).
The Kandji scripts read the same policy variables (set them in Kandji as custom script environment variables or export them in a wrapper). Remediation-only variables apply only to kandji-safe-chain-remediate.sh. The Mosyle script supports the same variables; see mosyle-readme.md for SAFE_CHAIN_MOSYLE_MODE.
| Variable | Default | Meaning |
|---|---|---|
SAFE_CHAIN_VERSION_POLICY |
latest |
How strictly version is enforced. |
SAFE_CHAIN_MINIMUM_VERSION |
(empty) | Required when SAFE_CHAIN_VERSION_POLICY=minimum. Must be a single version token (e.g. 1.4.6, 2.5.0-rc.1); the scripts validate the normalized value and reject stray characters so normalize_version cannot silently truncate input. |
SAFE_CHAIN_RELEASE_TAG |
1.4.6 |
Pinned GitHub release tag for download URLs (use the exact tag from releases). When non-empty, skips the GitHub API for latest policy (detect/remediate compare against this tag). With minimum policy, installs use this tag when set; if empty, the API resolves latest at install time. To track GitHub latest instead of pinning, set an explicit empty value: export SAFE_CHAIN_RELEASE_TAG="" (the scripts use bash ${VAR-default} so empty overrides the default). |
SAFE_CHAIN_INSTALLER_SHA256 |
(empty) | Remediate only. Optional 64-character hex SHA-256 of install-safe-chain.sh for the pinned release. When set, the installer is downloaded to a temp file, verified with sha256sum -c or shasum -a 256 -c, then executed. Requires SAFE_CHAIN_RELEASE_TAG so the artifact is fixed. If unset, the script uses curl … | sh (no checksum). |
- Detect: If
SAFE_CHAIN_RELEASE_TAGis empty, calls the GitHub API for the current release tag and requires each user’s installed version to match that tag (after normalizing a leadingv). IfSAFE_CHAIN_RELEASE_TAGis non-empty (default1.4.6), skips the API and requires a match to the pinned tag. Shell integration markers are always required. - Remediate: Resolves the target tag once at startup (API or pin), then installs or upgrades anyone who is not on that version or is missing shell integration.
- Detect: Does not call GitHub. Each user must have a binary whose version is greater than or equal to
SAFE_CHAIN_MINIMUM_VERSION(dotted numbers such as1.2.2vs1.4.6), plus shell integration markers. - Remediate: Only runs an install when the user has no binary, is below the minimum version, or lacks shell integration. The install uses
SAFE_CHAIN_RELEASE_TAGif set; otherwise it resolves latest from the API when an install is needed.
Invalid SAFE_CHAIN_VERSION_POLICY, minimum without a valid SAFE_CHAIN_MINIMUM_VERSION, unsafe/invalid release tags, or SAFE_CHAIN_INSTALLER_SHA256 without SAFE_CHAIN_RELEASE_TAG causes a logged error and a non-zero exit.
A user is compliant when all of the following hold:
~/.safe-chain/bin/safe-chainexists and is executable.- Version: Under
latest, installed version equals the resolved tag (GitHub latest orSAFE_CHAIN_RELEASE_TAG). Underminimum, installed version is not less thanSAFE_CHAIN_MINIMUM_VERSION(comparison uses dotted numeric versions such as1.2.2vs1.4.6). - Shell startup files contain Safe Chain integration markers:
~/.zshrc,~/.zprofile,~/.bashrc,~/.bash_profile,~/.profile, plus Fish~/.config/fish/config.fishand~/.config/fish/conf.d/*.fish.
kandji-safe-chain-detect.sh:
- Enumerates local user accounts via
dscl. - Filters to real profiles:
- UID >= 500
- valid home directory
- shell is not
false/nologin
- If
SAFE_CHAIN_VERSION_POLICY=latestandSAFE_CHAIN_RELEASE_TAGis empty, fetches latest version from:https://api.github.com/repos/AikidoSec/safe-chain/releases/latest
- If
SAFE_CHAIN_VERSION_POLICY=latestandSAFE_CHAIN_RELEASE_TAGis non-empty (default1.4.6), uses that tag only (no API call). - Compares installed vs required version per user (see version policy above).
- Returns:
0if all eligible users are compliant (or no eligible users are found)1if any user needs remediation, or iflatestmode cannot fetch the latest version
kandji-safe-chain-remediate.sh:
- Uses the same user enumeration and compliance rules as detect.
- For non-compliant users, runs install as that user using:
https://github.com/AikidoSec/safe-chain/releases/download/<tag>/install-safe-chain.sh
With optional SHA-256: download to a temp file, verify, thensh(seeSAFE_CHAIN_INSTALLER_SHA256). Without a checksum,curl … | shis used.
Unpinned latest is equivalent to the latest installer redirect once<tag>is resolved.
- In
minimummode, the GitHub API is called only when at least one user actually needs an install. - Ensures user context includes correct
HOMEand a PATH with~/.safe-chain/binfirst. - Includes a fallback direct-binary install path if the upstream installer fails due to local package-manager cleanup edge cases.
- Re-validates version and shell markers after install (
latest: must match resolved tag;minimum: must be at least the configured minimum). - Returns:
0if all required remediations succeed1if any remediation fails
Installed version is read from safe-chain --version, falling back to safe-chain -v, parsing the line:
Current safe-chain version: <version>
Versions are compared as dotted numbers (leading v on tags or variables is stripped). Suffixes such as 2.5.0a or prerelease strings like 2.5.0-rc.1 are preserved so 2.5.0a and 2.5.0b are not treated as the same version. Ordering uses sort -V (not full semver: ordering of pre-releases vs stable may differ from strict semver rules).
Logs are written to:
- Detect:
/var/log/safe-chain-kandji/detect.log - Remediate (root):
/var/log/safe-chain-kandji/remediate_root.log - Remediate (user execution output):
/var/log/safe-chain-kandji/remediate_user.log
- Upload
kandji-safe-chain-detect.shas the Detection script. - Upload
kandji-safe-chain-remediate.shas the Remediation script. - Ensure scripts run as root (standard Kandji custom script flow).
- Optionally set policy and pin variables (
SAFE_CHAIN_VERSION_POLICY,SAFE_CHAIN_MINIMUM_VERSION,SAFE_CHAIN_RELEASE_TAG,SAFE_CHAIN_INSTALLER_SHA256) so detect and remediate stay aligned.
Aikido Safe Chain is created and maintained by AikidoSec. These scripts download and manage that upstream product; they are not part of Safe Chain itself.
- Repository: AikidoSec/safe-chain