Deploy discovery
Requires @openlatch/client 0.11.12 or later
Deploy discovery once to the machines your fleet tool manages. Each machine then scans in every user’s own session and, when it finds an AI agent OpenLatch knows, installs and enrolls the client for that user. Discovery explains what a scan does and what it records.
Before you start, check these items:
| # | Item | Where it comes from |
|---|---|---|
| 1 | An organization key created from Host enrollment, held in your secret store | Settings › API keys, or Generate key and fill on Settings › Discovery, as an admin or owner |
| 2 | Your organization’s slug or id | Settings › Discovery shows it when it fills the command |
| 3 | A fleet tool that runs a script with system privilege: Intune, Configuration Manager, Jamf Pro, Ansible, Salt or similar | Your endpoint team |
| 4 | macOS, Linux with systemd, or Windows with PowerShell 5.1 or later | The machines |
| 5 | Egress approved for the client | Network requirements |
| 6 | A notice to your employees, where your works council or your data protection rules ask for one | The notice for your employees |
Create the discovery key
Section titled “Create the discovery key”-
Open Settings › Discovery as an admin or owner, and press Generate key and fill.
Expected: the command shows a new organization key and your organization, once. The key is named after today’s date and can do nothing but enroll hosts.
-
Copy the key into your secret store before you leave the page. The page forgets it when you leave; the key stays listed in Settings › API keys, where you can delete it.
You can also create the key yourself in Settings › API keys, from the Host enrollment preset. A machine needs the key only to enroll: the client trades it for the machine’s own host credential and never stores it, so rotating or deleting the key later disconnects no enrolled machine.
The key on each machine
Section titled “The key on each machine”Discovery writes the key and the organization to a key file on each machine, so that each user’s scan can enroll that user’s client. Every user of the machine can read that file, because each scan runs as its user. Whoever reads it can enroll hosts into your organization, and nothing else: the key holds no other permission, reads no data and cannot act on an agent. Treat it as shared within the machine, give discovery a key of its own, and rotate it when someone who held it leaves.
Try it on one machine
Section titled “Try it on one machine”Run the command from Settings › Discovery on one machine of each operating system before you deploy to a wave. With sudo, or in an elevated PowerShell, the command deploys discovery for every user of that machine.
On macOS and Linux:
curl -LsSf https://openlatch.ai/discover.sh | sudo OPENLATCH_API_KEY=<api-key> OPENLATCH_ORG=<org> shOn Windows, in an elevated PowerShell, type the key and the organization into the session, then run the script:
$env:OPENLATCH_API_KEY='<api-key>'; $env:OPENLATCH_ORG='<org>'irm https://openlatch.ai/discover.ps1 | iexWithout sudo or elevation, the same command deploys discovery for the current user only, with no admin rights. That suits a trial on your own machine:
curl -LsSf https://openlatch.ai/discover.sh | OPENLATCH_API_KEY=<api-key> OPENLATCH_ORG=<org> sh$env:OPENLATCH_API_KEY='<api-key>'; $env:OPENLATCH_ORG='<org>'powershell -ExecutionPolicy Bypass -c "irm https://openlatch.ai/discover.ps1 | iex"A piped deploy downloads its own release file again and installs that copy. On macOS and Linux it first checks the copy against the published SHA-256 and installs nothing when they differ. To see what the scan would find without installing anything, add —dry-run to the script, or -DryRun on Windows.
Check the script before you deploy it
Section titled “Check the script before you deploy it”Both scripts are plain text, served to be read. For the macOS and Linux script, OpenLatch publishes the SHA-256 of each release. Check the file you deploy against it:
curl -LsSfO https://openlatch.ai/discover.shcurl -LsSf https://openlatch.ai/discover.sh.sha256 | shasum -a 256 -c -On Linux, sha256sum -c works the same way. A file that does not match is not the file OpenLatch published: do not deploy it. No digest is published for the Windows script.
What the deploy changes on a machine
Section titled “What the deploy changes on a machine”The deploy copies the script, writes the key file and registers one schedule. It never scans and never writes in a user’s home folder.
| Item | Linux | macOS | Windows |
|---|---|---|---|
| Local copy of the script, in a folder only administrators can write | /usr/local/lib/openlatch/discovery/discover.sh | /Library/Application Support/OpenLatch/Discovery/discover.sh | C:\Program Files\OpenLatch\Discovery\discover.ps1 |
| Key file, readable by the machine’s users | /etc/openlatch/discovery.key | /Library/Application Support/OpenLatch/Discovery/discovery.key | C:\ProgramData\OpenLatch\Discovery\discovery.key |
| Schedule, one for every user | The systemd user timer openlatch-discovery.timer, enabled for every user | The LaunchAgent ai.openlatch.discovery | The scheduled task OpenLatch Discovery, run for the Users group |
| Each user’s scan results and log | ~/.local/share/openlatch-discovery/ | ~/Library/Application Support/OpenLatch Discovery/ | %LOCALAPPDATA%\OpenLatch\Discovery\ |
A user-scope deploy keeps its copy, its key file and its schedule in that user’s own discovery folder instead.
The script takes these options. The fleet forms pass the key and the organization through the environment or a key file, never on the command line:
sh discover.sh [--api-key-file <path>] [--interval <seconds>] [--dry-run] [--uninstall] [--scan-once]powershell.exe -NoProfile -ExecutionPolicy Bypass -File discover.ps1 [-ApiKeyFile <path>] [-Interval <seconds>] [-DryRun] [-Uninstall] [-ScanOnce]| Option | Effect |
|---|---|
| —api-key-file, -ApiKeyFile | A file holding the key and the organization, one line each: OPENLATCH_API_KEY=… and OPENLATCH_ORG=… |
| —interval, -Interval | The scan interval in seconds, 86400 by default. Change it by deploying again with another value |
| —dry-run, -DryRun | Detect and log only: install, enroll and register nothing |
| —uninstall, -Uninstall | Remove the schedule, the local copy and the key file. Every enrolled client stays |
| —scan-once, -ScanOnce | One scan in the current context, with no schedule: for containers and tests |
Deploy to a fleet
Section titled “Deploy to a fleet”These recipes deploy discovery. To install the client on every machine yourself instead, use the recipes of Deploy the client.
Every recipe follows the same rules:
| Rule | What it means |
|---|---|
| Run with system privilege | As root on macOS and Linux, as SYSTEM on Windows. The deploy registers the schedule for every user; the scans themselves run as each user |
| Deploy the script file | Keep it exactly as published, and never wrap or edit it |
| Take the key from a secret channel | The key reaches the script through the environment or a key file, never on a command line or in a script body your tool shows to others |
| Read the run report | Your fleet tool’s report of each run is where you see machines that ran the script and found nothing |
Try each recipe on one device before the wave. Then deploy it again whenever you want to change the interval or deliver a new release of the script: the schedule keeps running the copy already on the machine until then.
Microsoft Intune
Section titled “Microsoft Intune”Add a platform script that runs as SYSTEM. It downloads the script, writes a key file from your secret store, and deploys:
# Intune platform script, run as SYSTEM.$ErrorActionPreference = "Stop"
$dir = Join-Path $env:ProgramData "OpenLatch\Deploy"New-Item -ItemType Directory -Path $dir -Force | Out-Null$script = Join-Path $dir "discover.ps1"Invoke-WebRequest -UseBasicParsing -Uri "https://openlatch.ai/discover.ps1" -OutFile $script
# Replace this call with the one your secret store provides.$key = Get-DiscoveryKeyFromSecretStore$keyFile = Join-Path $dir "discovery.key"Set-Content -Path $keyFile -Value @("OPENLATCH_API_KEY=$key", "OPENLATCH_ORG=<org>")
powershell.exe -NoProfile -ExecutionPolicy Bypass -File $script -ApiKeyFile $keyFile$status = $LASTEXITCODERemove-Item $keyFile -ErrorAction SilentlyContinueexit $status| Setting | Value |
|---|---|
| Run this script using the logged on credentials | No, so that it runs as SYSTEM |
| Enforce script signature check | No |
| Run script in 64 bit PowerShell Host | Yes |
| Assignment | A device group |
The script’s body is readable by anyone who can read the policy, which is why it fetches the key rather than holding it.
Microsoft Configuration Manager
Section titled “Microsoft Configuration Manager”Save the Intune script above as deploy-discovery.ps1, make it the content of a package or an application, and deploy it with these settings:
| Setting | Value |
|---|---|
| Command line | powershell.exe -NoProfile -ExecutionPolicy Bypass -File deploy-discovery.ps1 |
| Run | Whether or not a user is logged on, with administrative rights |
| Deployment | Required, to a device collection |
The deployment status of each device is your run report.
Jamf Pro
Section titled “Jamf Pro”A Jamf policy runs its scripts as root, which is what the deploy needs. The script fetches the key from your secret store and writes it to a key file only root can read. Never pass the key as a policy parameter: Jamf hands parameters to the script on its command line.
#!/bin/zsh# Jamf Pro policy script, run as root.set -eu
work=$(mktemp -d /tmp/openlatch-discovery.XXXXXX)trap 'rm -rf "$work"' EXITcd "$work"
curl -LsSfO https://openlatch.ai/discover.shcurl -LsSf https://openlatch.ai/discover.sh.sha256 | shasum -a 256 -c -
# Replace this call with the one your secret store provides.key=$(get-discovery-key-from-secret-store)
umask 077print -r -- "OPENLATCH_API_KEY=$key" > discovery.keyprint -r -- "OPENLATCH_ORG=<org>" >> discovery.key
sh discover.sh --api-key-file "$work/discovery.key"Set the policy’s Execution Frequency to Once per computer, and run it again when you change the interval or the script.
On macOS 13 and later, a new background item makes the system ask the user to approve it. To avoid the prompt, add a configuration profile with a Managed Login Items payload holding one rule: Rule Type Label, Rule Value ai.openlatch.discovery. With the payload, the item is approved and the user may see one notice that managed items were added; whether to deploy the payload is your choice. Without it, the user sees the system’s Background Items Added notice.
The catalog’s folders sit outside the folders macOS privacy controls protect, so the scan needs no privacy preferences (PPPC) profile. A probe macOS refuses counts as inconclusive, never as absent.
Ansible, Salt and other tools
Section titled “Ansible, Salt and other tools”Run the same steps as the Jamf script with root privilege: download the script, check its SHA-256, write the key file from your vault with permissions only root can read, and run it with —api-key-file.
Containers
Section titled “Containers”| Stage | Command | Effect |
|---|---|---|
| Image build | sh discover.sh —scan-once —dry-run | Detects and records, never enrolls |
| Container start | sh discover.sh —scan-once | Detects and enrolls, with the key and the organization from a runtime secret |
Never bake the key into an image or pass it as a build argument. Each container start that enrolls is a new host for licensing. On musl or BusyBox images the scan detects only, and records that the install is not supported there.
Windows: endpoint detection rules
Section titled “Windows: endpoint detection rules”The scheduled task runs this action, with the path of the local copy:
conhost.exe --headless powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "C:\Program Files\OpenLatch\Discovery\discover.ps1"conhost.exe —headless keeps the scan from showing a window. Microsoft documents the flag as unsupported, and stock detection rules for hidden windows, such as the Sigma rules for MITRE ATT&CK T1564.003, can match it. If your endpoint detection flags the task, allow that command line for the local copy’s path. Because the flag hides the exit code, the scan writes its own result to its log and to its last scan file.
When the schedule starts
Section titled “When the schedule starts”| Situation | When the first scan runs |
|---|---|
| A user logs in after the deploy | At that login, then at each interval |
| A session already open at the deploy, on macOS, on Windows, or on Linux with systemd 248 or later | Right away |
| A session already open, on Linux below systemd 248 | At that user’s next login |
| A session already open on Windows that the deploy cannot reach | At that user’s next logon |
| Linux without systemd | Never: the deploy stops with OL-2203 |
Remove discovery or opt a machine out
Section titled “Remove discovery or opt a machine out”To remove discovery from a machine, deploy the script again with —uninstall, or -Uninstall on Windows. The schedule, the local copy and the key file go; every enrolled client stays, with its enrollment. To also remove the client, use its own uninstall: see Uninstall.
To keep discovery scanning on a machine without enrolling anything, place an empty file named opt-out in the discovery folder. Scans then detect and record only.
| Deploy | Where the opt-out file goes |
|---|---|
| For every user, on Linux | /usr/local/lib/openlatch/discovery/opt-out |
| For every user, on macOS | /Library/Application Support/OpenLatch/Discovery/opt-out |
| For every user, on Windows | C:\Program Files\OpenLatch\Discovery\opt-out |
| For the current user only | opt-out at the top of that user’s discovery folder, beside its key file, not in its bin folder |
To keep a machine out of your organization, de-enroll it in the console: see Manage discovered hosts.
Rotate the discovery key
Section titled “Rotate the discovery key”Rotate the key when someone who held it leaves, or on your own schedule. Enrolled machines hold their own host credential, so a rotation disconnects none of them.
- Create a new key from Host enrollment in Settings › API keys, or with Generate key and fill, and store it in your secret store.
- Deploy the script again with the new key, so each machine’s key file holds it.
- Delete the old key in Settings › API keys once the wave has run. A machine still holding the old key file cannot enroll a new user until it receives the new one.
Codes the scripts print
Section titled “Codes the scripts print”The scripts print a code beside each failure, then the next step on the following line. The install the scan runs on a hit keeps the install scripts’ OL-21xx codes.
| Code | What happened | What to do next |
|---|---|---|
| OL-2200 | No key or no organization was given, in the environment or a key file. At a deploy nothing changes; at a scan the key file is missing and nothing installs | Deliver the key and the organization from your secret store and deploy again |
| OL-2201 | An option was not recognized, or the interval is not a whole number of seconds above zero | Use the options listed above; the interval is in seconds |
| OL-2202 | The script does not run on this operating system | Use discover.sh on macOS and Linux, discover.ps1 on Windows |
| OL-2203 | This Linux machine does not run systemd, which the schedule needs | Run the script with —scan-once from the machine’s own job runner, or use the install script |
| OL-2204 | The step needs system privilege, such as removing a deploy made for every user | Run the same command through your fleet tool, or with sudo or an elevated PowerShell |
| OL-2205 | A piped deploy could not download its release file or its digest | Check that the machine reaches openlatch.ai and deploy again, or deploy the script file |
| OL-2206 | A piped deploy downloaded its release file and its SHA-256 does not match the published digest | Deploy again; if it keeps failing, deploy the script file after checking its digest |
| OL-2207 | A piped deploy downloaded the Windows script and it carries a signature that is not valid | Deploy again; if it keeps failing, deploy the script file |
| OL-2208 | The local copy, its digest or the key file could not be written | Check that the discovery folder is writable by the account the deploy runs as, then deploy again |
| OL-2209 | The schedule could not be registered | Deploy again; the message names the step the operating system refused |
| OL-2210 | The schedule is registered, but starting it in one open session failed | Nothing: that user’s scans start at their next login |
| OL-2211 | Removing discovery left the schedule, the local copy or the key file | Run the removal again through your fleet tool; the message names what is left |
| OL-2212 | An enrollment was already running for this user, so this scan skipped its own | Nothing: the next scan tries again |
When a scan enrolls a client, the client’s own enrollment answers, and the scan acts on the answer:
| Code | What it means | What the scan does |
|---|---|---|
| OL-1201 | The platform refused the key | Stops enrolling after three refusals in a row, until the key file changes. Scans keep detecting. Replace the key file with a valid key |
| OL-1612 | The key belongs to another organization than the one named | Stops enrolling after three refusals in a row, until the key file changes. Scans keep detecting |
| OL-1613 | The platform could not be reached to confirm the organization | Tries again at the next scan |
| OL-1614 | This user’s installation is enrolled in another organization | Leaves that installation unchanged |
| OL-1624 | An admin de-enrolled this host | Detects and records only, until an admin presses Re-enrol |
| OL-1626 | An admin cut this host off | Detects and records only. A person who runs openlatch init with a key brings the host back |
| OL-1625 | Another enrollment was running | Skips this scan’s enrollment |