macOS Agent

The Hermes agent runs on Apple Silicon Macs as well as on Windows and Linux. On a Mac it installs as a system LaunchDaemon called com.athena.hermes-agent, connects to the server over the same secured agent connection as the rest of your fleet, and takes part in inventory, software deployment, remote commands, compliance and agent updates. This page covers what you need before you start, how to install it (interactively or unattended), where it keeps its files, how to manage it with launchctl, what works on macOS and what doesn't, and how to troubleshoot and remove it.

Apple Silicon only

Macs with Apple Silicon (M1 or later) are supported. Macs with an Intel processor are not: the server refuses an Intel agent file, and the installer stops on an Intel Mac before it changes anything. To check a Mac, open Apple menu → About This Mac — Apple Silicon Macs show Chip (for example Apple M1), Intel Macs show Processor.

Requirements#

Upload the macOS agent binary#

Open Settings → Agents → Updates, set Operating System to macOS, choose the agent file and upload it, then make it active. See Agent Binaries & Updates for the general upload and activation flow.

Install the agent#

In the console, open Settings → Agents → Configuration and, in the Agent Installer card (shown to Admins only), click macOS. If the button is disabled, no active macOS binary exists yet; the hint reads No macOS agent binary uploaded yet — upload one in Agent Binaries first. Copy the one-liner and run it in Terminal on the Mac. Like the Linux installer, it downloads first and then runs, so its prompts work:

curl -fsSLk https://<server>:8443/api/agent-binaries/install-script/macos -o /tmp/athena-macos-agent-install.sh && sudo bash /tmp/athena-macos-agent-install.sh

The installer then:

  1. Asks for the server host (pre-filled), the API port (default 8443), the Agent (gRPC) port (default 8444), and your console Username and Password. The password is masked and is never written to the install log.
  2. Checks that the certificate on the agent port belongs to this server and names the host you typed — before it sends your password. If it doesn't, it stops. Type a host name the server certificate lists rather than an IP address. See Server TLS Certificate.
  3. Signs in, finds the latest active macOS agent, and creates a single-use registration token valid for 10 minutes, labelled bootstrap: <hostname>.
  4. Downloads the agent and checks its SHA-256 hash against the value the server reports; a mismatch or a file that isn't a Mac executable stops the install.
  5. Installs the agent and the server's CA certificate, signs the agent on the Mac (see below), writes its settings, installs the LaunchDaemon and starts it, then waits up to 20 seconds for it to be running.

Look for Agent installed - running in green. That message means the service is running on the Mac — it doesn't ask the server — so confirm the Mac appears under Agents. If a step fails, the script prints FAILED: <step> - <reason>, for example FAILED: login - Login rejected …. A transcript is written to /Library/Logs/Hermes/athena-install.log.

You can run the one-liner again on the same Mac: it stops and replaces the existing service and rewrites its settings with a fresh token.

The agent is signed on the Mac

The agent file is not signed by Apple. macOS will only start it once it carries a signature, so the installer removes the "downloaded from the internet" (quarantine) mark and gives the agent a local (ad-hoc) signature on that Mac. Every self-update signs the new file the same way.

Unattended install (no terminal)#

The one-liner above expects someone at a Terminal to answer its prompts. To install or reinstall the agent where nobody is at the keyboard — from a script runner that executes as root, a one-shot LaunchDaemon, or an Athena command sent to a Mac that is already enrolled — run the same installer script in unattended mode. It turns on when the ATHENA_REGISTRATION_TOKEN environment variable is set. The script then asks nothing, doesn't need a terminal, doesn't sign in with a username and password, and doesn't create a registration token of its own.

Environment variables#

VariableRequiredWhat it is
ATHENA_REGISTRATION_TOKENYesAn agent registration token. Setting it is what switches the script to unattended mode. The agent uses it to enroll. Create a short-lived one with a usage limit that matches the number of Macs. See Registration Tokens.
ATHENA_API_TOKENYesAn API token (ath_…). The script needs it to look up the latest macOS agent and download it, because a registration token can't do that. The token's owner must hold binaries.view and binaries.manage. Limit the token to those two permissions.
ATHENA_HOSTNoServer host name. Defaults to the host built into the script when the server serves it — the same value the interactive prompt pre-fills.
ATHENA_API_PORTNoAPI port. Default 8443.
ATHENA_AGENT_PORTNoAgent (gRPC) port. Default 8444.

If the host or the API token is missing, the script stops before it contacts the server. Otherwise it runs the same checks as an interactive install. It checks the API-port certificate and the agent-port certificate before it sends the API token, and it checks the SHA-256 hash of the downloaded agent. Then it installs and signs the agent, writes your registration token into the agent's settings, and starts the LaunchDaemon. The transcript goes to /Library/Logs/Hermes/athena-install.log, as in an interactive run. Without ATHENA_REGISTRATION_TOKEN, the script behaves exactly as described in Install the agent.

Prepare the two tokens#

In PowerShell, connected to the server with an account that may create both kinds of token:

# Registration token: one use, valid for one hour
$reg = New-AthenaRegistrationToken -Description "mac unattended: mac-042" -ValidityHours 1 -MaxUsages 1

# API token for the binary download: one day, only the two permissions it needs
$api = New-AthenaApiToken -Name "mac unattended install" -ExpiresInDays 1 -Permission binaries.view, binaries.manage

You can also create them in the console: a registration token under Settings → Security → Token, and an API token from your Profile. For several Macs, raise the registration token's usage limit. A single API token can serve the whole batch until it expires.

Run it#

From any process that already runs as root, download the script and run it with the variables set. To download it, use the fetch part of the console one-liner. When the server can pin its certificate, that fetch pins it with --pinnedpubkey, so the script body is protected too.

curl -fsSLk https://<server>:8443/api/agent-binaries/install-script/macos -o /tmp/athena-macos-agent-install.sh
ATHENA_REGISTRATION_TOKEN='<registration token>' \
ATHENA_API_TOKEN='ath_<api token>' \
  /bin/bash /tmp/athena-macos-agent-install.sh

Reinstalling from an Athena command#

The installer stops and replaces the agent service that is already running on the Mac. If you start the installer straight from a command sent to that agent, it stops the agent that is running it. Instead, let the command start a one-shot LaunchDaemon that runs the installer on its own, and return straight away. Send this as a command to the Mac. Commands run as root, under /bin/zsh:

cat > /tmp/com.athena.reinstall.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>com.athena.reinstall</string>
  <key>ProgramArguments</key><array><string>/bin/bash</string><string>-c</string>
    <string>curl -fsS --insecure -o /tmp/athena-install.sh https://athena.example.com:8443/api/agent-binaries/install-script/macos &amp;&amp; /bin/bash /tmp/athena-install.sh</string></array>
  <key>EnvironmentVariables</key><dict>
    <key>ATHENA_REGISTRATION_TOKEN</key><string>REG_TOKEN_HERE</string>
    <key>ATHENA_API_TOKEN</key><string>ath_API_TOKEN_HERE</string>
  </dict>
  <key>RunAtLoad</key><true/>
  <key>StandardOutPath</key><string>/tmp/athena-reinstall.log</string>
  <key>StandardErrorPath</key><string>/tmp/athena-reinstall.log</string>
</dict></plist>
EOF
chown root:wheel /tmp/com.athena.reinstall.plist && chmod 600 /tmp/com.athena.reinstall.plist
launchctl bootout system/com.athena.reinstall 2>/dev/null; launchctl bootstrap system /tmp/com.athena.reinstall.plist

Replace the host and the two token placeholders. In the plist, && must be written as &amp;&amp;, as shown. Because --insecure skips certificate checks on this download, the script body itself isn't verified. The script then pins the API-port certificate before it sends the API token. To protect the download too, use -k --pinnedpubkey sha256//… from the console one-liner in its place. Progress is written to /tmp/athena-reinstall.log and to the installer transcript.

Clean up the tokens afterwards

Both tokens appear in plain text in the command and in the plist. After the Mac shows up under Agents, unload and delete the one-shot job, and revoke the API token (for example with Revoke-AthenaApiToken):

sudo launchctl bootout system/com.athena.reinstall
sudo rm -f /tmp/com.athena.reinstall.plist /tmp/athena-install.sh
macOS only

Only the macOS installer script has unattended mode. The Linux and Windows one-line installers still prompt for their settings. On Windows, use the install bundle for installs where nobody is at the keyboard.

Files & service layout#

PathWhat it holds
/Library/Application Support/Hermes/Program and data folder — the agent (Hermes.Agent), its appsettings.json, local state, and the per-server certificates under servers/.
/Library/Application Support/Hermes/deployments/Working area where deployment packages are staged and run.
/Library/Logs/Hermes/Daily agent logs (hermes-YYYYMMDD.log, 30 days kept), plus stdout.log and stderr.log from launchd.
/Library/Logs/Hermes/athena-install.logTranscript from the one-line installer.
/Library/LaunchDaemons/com.athena.hermes-agent.plistThe LaunchDaemon that runs the agent as root.

appsettings.json is created readable by root only, because it holds the registration token until the Mac has enrolled. The agent keeps its connection certificate as a file in its data folder, not in the macOS keychain. For the settings inside appsettings.json, see Agent Configuration File.

Managing the service#

The LaunchDaemon starts at boot and is kept alive: launchd starts the agent again whenever it exits, waiting at least 10 seconds between starts. Manage it with launchctl in the system domain:

# Status (look for "state = running" and the pid)
sudo launchctl print system/com.athena.hermes-agent | grep -E "state =|pid ="

# Restart
sudo launchctl kickstart -k system/com.athena.hermes-agent

# Stop and unload (stays stopped until loaded again or the Mac restarts)
sudo launchctl bootout system/com.athena.hermes-agent

# Load and start again
sudo launchctl bootstrap system /Library/LaunchDaemons/com.athena.hermes-agent.plist

Viewing logs#

# Follow the agent log
tail -f /Library/Logs/Hermes/hermes-*.log

# If the service keeps restarting, read launchd's error output too
tail -50 /Library/Logs/Hermes/stderr.log

What works on macOS#

CapabilityOn a Mac
InventoryOperating system, hardware, processors, memory, storage and physical disks, network interfaces and shares, installed software, services (launchd jobs), environment variables, local groups, containers, boot & security, and pending software updates. See Inventory on a Mac.
Software UpdatesThe agent's updates section is titled Software Updates on a Mac and lists pending updates reported by macOS. See below.
Software deploymentRuns deployment packages with the step types listed in Deployments on a Mac.
Remote commandsRuns ad-hoc commands and scripts. See Shells and Remote Command Execution.
ServicesLists launchd jobs (including disabled ones) and starts, stops, restarts, enables and disables them. See Remote Services Control.
File browserStarts at /. Some private user folders need Full Disk Access (see below). See Remote File Browser.
ComplianceEvaluates assigned baselines and reports results. See Compliance & Baselines.
FileVault recovery keyWith agent 1.2.123 or later, escrow, rotate and reveal the Mac's FileVault personal recovery key from its FileVault tab. Licensed with BitLocker. See FileVault Recovery-Key Escrow.
Agent updatesPush a newer macOS binary or let automatic updates do it, as on the other systems. See Updates & signing.
Not availableThe built-in screen share is Windows-only, so its button isn't shown for a Mac. BitLocker, Sysmon and Power Management are Windows-only policies and don't apply to Macs.

Inventory on a Mac#

Mac inventory comes from the standard macOS tools — for example system_profiler for hardware and applications, pkgutil for installed packages, diskutil for disks, launchctl for services, and dscl for local groups. It feeds the same Inventory views, Collections and Reports as your Windows and Linux machines. On a Mac, the Boot & Security view adds Mac rows such as System Integrity Protection, FileVault, Gatekeeper, the firewall, and MDM enrollment.

Software Updates#

The agent asks macOS for available updates (softwareupdate --list) and caches the answer for 6 hours, because the check contacts Apple's servers. The Software Updates section shows pending updates only: there is no installed-update history, no KB column and no release date, and a Details column shows the version and restart note macOS reports. Linux agents have no updates section. For Windows update management, see Windows Update Management.

Deployments on a Mac#

Step typeOn a Mac
Install ApplicationInstalls a .pkg or .mpkg with macOS's installer. For a .dmg it mounts the image read-only, installs the first .pkg inside — or, if there is none, copies the first .app to /Applications — and always unmounts it. Any other file type fails. A package that asks for a restart is not restarted for you; add a Reboot step.
UninstallNot supported — macOS has no package manager that removes a package's files. The step fails with that reason; use a Run Command or Shell step with the vendor's uninstaller instead.
Run Command, ShellRun under /bin/bash.
Start / Stop / Restart ServiceAct on launchd jobs.
Copy Files, Reboot, Wait, Trigger InventorySupported.
PowerShell, BatchNot supported — the step fails with a message to use a Shell step instead.

Shells#

Ad-hoc commands from the console run under /bin/zsh, the default macOS shell (falling back to bash, then sh). Python commands run with python3 (or python if /usr/bin/python3 is missing). Deployment Shell and Run Command steps run under /bin/bash, which on macOS is bash 3.2 — keep scripts for those steps compatible with it.

Updates & signing#

Upload a newer macOS binary in Settings → Agents → Updates and push it, or let automatic updates and staged rollouts deliver it. On the Mac, the agent stages the new file, removes its quarantine mark and signs it locally, keeps a copy of the old agent as Hermes.Agent.backup, swaps the new file in, and restarts itself through launchd.

Full Disk Access (optional)#

Inventory, commands, package installs and updates work without Full Disk Access. Without it the agent can't read some private user folders — such as Mail, Messages, Safari data and parts of Desktop, Documents and Downloads — so the file browser is denied there. To allow it, open System Settings → Privacy & Security → Full Disk Access, click + and add /Library/Application Support/Hermes/Hermes.Agent.

Because the agent is signed on the Mac, macOS treats each updated agent as a new program and drops the Full Disk Access grant after every update. Add it again if you rely on it.

Troubleshooting#

"This Mac has an Intel processor. Athena supports Apple Silicon Macs only."#

The installer stopped at its platform check before changing anything. The agent can't be installed on an Intel Mac.

"Run with sudo."#

The script was not run as root. Run the one-liner as shown (it includes sudo) from an administrator account.

The macOS button in Agent Installer is disabled#

No active macOS agent binary exists. Upload one and make it active.

The agent stops with "Killed: 9"#

macOS refused to start the agent because its signature is missing or broken — usually because the file was copied or changed by hand. Re-sign it and restart the service:

sudo codesign --force -s - "/Library/Application Support/Hermes/Hermes.Agent"
sudo launchctl kickstart -k system/com.athena.hermes-agent

A Mac agent stopped connecting after an update#

Mac agents before version 1.2.116 kept their connection key in the System keychain, tied to that exact agent file. After a self-update the new file can't use the key, every connection fails, and the agent log shows AppleCrypto+SslException: Internal error. Because the agent can't reach the server, repair it on the Mac:

  1. List the identities in the System keychain and note the SHA-1 thumbprint of the agent's identity:
    sudo security find-identity -v /Library/Keychains/System.keychain
  2. Delete that identity and restart the agent:
    sudo security delete-identity -Z <thumbprint> /Library/Keychains/System.keychain
    sudo launchctl kickstart -k system/com.athena.hermes-agent
  3. When the agent is back online, push version 1.2.116 or later to it straight away, then repeat step 2 once more. From 1.2.116 the agent no longer uses the System keychain.

Unattended install fails at "unattended settings"#

The service runs but the Mac never appears#

Removing the agent#

Run these in Terminal on the Mac. They stop the service and remove the agent; the logs in /Library/Logs/Hermes are kept.

sudo launchctl bootout system/com.athena.hermes-agent
sudo rm -f /Library/LaunchDaemons/com.athena.hermes-agent.plist
sudo rm -rf "/Library/Application Support/Hermes"

# Remove the hermescli link, but only if it points to the agent's CLI
[ "$(readlink /usr/local/bin/hermescli)" = "/Library/Application Support/Hermes/HermesCLI" ] && sudo rm -f /usr/local/bin/hermescli

The Mac stays listed in Agents until you remove it there. See Retiring & Removing Agents. To enroll it again later, run the installer again — it registers the Mac as a new agent.