# Roll out to a fleet

> Push the same installer through Jamf, Intune or Kandji, watch before you enforce, and fix risky agent settings with the MDM you already run.

A fleet rollout uses the same installer as a single machine, described in [Install Golem](/install). What changes is who runs it, how machines land in the right group, and the order in which you turn things on. Most teams go in three steps: find out what is there, see how it is set up, then enforce.

```mermaid
flowchart TD
    A["Push the installer<br/>as each user"] --> B["Watch: rule packs in Shadow"]
    B --> C["Read Agent settings<br/>in Security Inventory"]
    C --> D["Lock risky modes<br/>through your MDM"]
    D --> E["Enforce: packs and rules<br/>to Ask or Block"]
```

## Install as the user on macOS and Linux

On macOS and Linux the Golem agent is a per-user service, and the machine's identity is created for that user. MDM scripts usually run as root, and a root install produces a machine that looks enrolled but never reports, because the service is registered for an account nobody uses. `golem join` refuses to enroll when it is run as root through `sudo`.

Run the installer as the logged-in user. On a Mac, from a Jamf, Kandji or Intune script:

```bash
uid=$(stat -f %u /dev/console)
user=$(id -un "$uid")
launchctl asuser "$uid" sudo -u "$user" /bin/sh -c \
  'curl -fsSL https://app.golem.team/install.sh | GOLEM_WORKSPACE_KEY=glk_... GOLEM_NO_SANDBOX=1 sh -s -- --with-editor-ext'
```

`GOLEM_NO_SANDBOX=1` stops the installer asking the user for their password to set up [the sandbox](/shield#the-sandbox). To give your Macs the sandbox without that prompt, run the sandbox step as root in a second payload. As root it installs Apple's `container` package silently, and the Golem agent fetches the rest as the signed-in user the first time it is used:

```bash
user=$(id -un "$(stat -f %u /dev/console)")
bin=/usr/local/bin/golem
[ -x "$bin" ] || bin="/Users/$user/.local/bin/golem"
"$bin" install sandbox
```

It does nothing on an Intel Mac or one older than macOS 26. `--with-editor-ext` installs the editor extension into every VS Code-family editor on the user's `PATH`. The [browser extension](/agents/browser-extension) is deployed through Chrome policy instead.

On Linux, run the installer as the target user in a session where `systemctl --user` works.

## Install as SYSTEM on Windows

Windows inverts the rule. The agent is a machine service, so `install.ps1` must run elevated, and an Intune script running as SYSTEM is correct:

```powershell
$env:GOLEM_WORKSPACE_KEY='glk_...'
$env:GOLEM_LABELS='env=prod,team=platform'
iwr -useb https://app.golem.team/install.ps1 | iex
```

The machine's identity lives in `C:\ProgramData\Golem`, not in a user profile, so it does not matter which user is signed in.

Connecting the coding agents is still per user. Run as SYSTEM, the installer connects SYSTEM's clients, which nobody uses, and says so. Push a second script that runs in the signed-in user's context:

```powershell
golem setup-client
```

Until it runs, the machine is enrolled and healthy while none of its coding agents talk to it.

## Enrollment keys in a payload

The key in `GOLEM_WORKSPACE_KEY` is used once, by `golem join`, and is not written anywhere on the machine. It is visible in your MDM script, though. Create one key per deployment wave under **Deploy → Install**, named for the wave, so a key that leaks can be revoked without touching the next wave, and the machines it enrolled can be found. A revoked key cannot enroll another machine.

## Put machines in the right group

Groups carry settings down to the machines in them, including which profile, rules and storage apply. You can place machines by hand, or let a **label rule** claim them:

1. Tag machines at install with `GOLEM_LABELS=env=prod,team=platform` (at most 10 labels; keys are lowercase letters, digits, `_` and `-`, up to 32 characters; values up to 64).
2. In the console, open **Hosts, Groups & Profiles**, pick the group, and add a label rule under **Label rules**.

A machine whose labels match is claimed on the next pass, with no work per machine. A machine cannot name its own group: a label is something the machine says about itself, and the rule that maps it to a group is written by an admin.

> [!WARNING]
> Labels set at install reach the Golem agent on Linux and Windows. On macOS the installer does not yet pass them to the service, so a label rule will not claim a Mac. Add Macs to their group by hand for now.

## Watch first

Each profile's rule packs have an **Enforce** or **Shadow** switch. In Shadow, every rule in the pack records what it would have done, such as "would have blocked", and stops nothing. Start new packs in Shadow, look at what they would have stopped on real work, then switch them to Enforce.

Two things enforce from the first minute, by design:

- Golem's built-in rule that blocks catastrophic shell commands (`rm -rf /`, writing to a raw disk, a fork bomb). Built-in packs cannot be put in Shadow; each built-in rule acts at its own step, and this one blocks.
- On macOS, [the shell jail](/shield#the-shell-jail) around Claude Code's shell commands.

## The posture report

Within minutes of installing, each machine reports how its coding agents are set up to run. Open **Security Inventory → Agent settings**. Each row is one risky setting on one client, with how many machines have it and when it was last seen. **Needs attention** narrows the list to what alerts; **Show fixed** adds settings that have since been fixed. Open a row for the machines, the file and the value, and, under **Lock it with**, the managed setting that prevents it.

The Golem agent reads each client's own settings files every 10 minutes. A launch flag leaves nothing in a file, so it also reads the command lines of running agents, and the hooks report each session's permission mode. A session run with prompts off is listed for 7 days.

| Client | Flagged when | Lock it with |
|---|---|---|
| Claude Code | `permissions.defaultMode` is `bypassPermissions`; the bypass warning was accepted; a session ran with `--dangerously-skip-permissions`; an allow rule covers every Bash command | `permissions.disableBypassPermissionsMode: "disable"` in managed settings |
| Cursor | Run Everything, in the app or as `approvalMode: "unrestricted"`; a session ran with `--yolo` or `--force` | `approvalMode: "allowlist"` |
| Codex | `sandbox_mode = "danger-full-access"`; a session ran with `--yolo` or `--dangerously-bypass-approvals-and-sandbox` | `allowed_sandbox_modes` and `allowed_approval_policies` in Codex's `requirements.toml` |
| Gemini CLI | every shell command allowed; folder trust off; started with `--yolo` | `security.disableYoloMode: true`, `security.folderTrust.enabled: true` |
| Copilot CLI | started with `--allow-all-tools`, `--allow-all` or `--yolo` | your Copilot organization's policy |
| Devin | every shell command allowed; started with `--permission-mode bypass` | deny and ask rules in Devin's Team Settings |
| Antigravity | terminal auto-execution is eager | terminal auto-execution set to Off |
| VS Code and its forks | `chat.tools.global.autoApprove` on; workspace trust off | `chat.tools.global.autoApprove: false`, `security.workspace.trust.enabled: true` |

Lower-risk settings, such as Claude Code without its sandbox or Codex set never to ask, are listed as information and do not alert. A repository's own settings file counts too, since a cloned repo can switch prompts off for everyone who opens it.

A setting that appears is a signed entry in the record and an alert, which reaches your alert destinations like any other. When the machine's setting is fixed, the alert resolves itself. If a setting is deliberate, for example Codex never asking on CI runners like `ci-runner-01`, choose **Accept the risk**: it stays listed, for the whole workspace, and stops alerting. Only the setting's name and value leave the machine.

Turn the checks off with **Agent settings** in the Security Inventory's settings. Off, machines read and report nothing.

## Lock risky modes with your MDM

The posture report tells you which machines to fix; your MDM fixes them. Push the setting named under **Lock it with**. For Claude Code that is a managed settings file, such as `/Library/Application Support/ClaudeCode/managed-settings.json` on a Mac, or the managed preferences profile your MDM already writes:

```json
{
  "permissions": {
    "disableBypassPermissionsMode": "disable"
  }
}
```

Claude Code then refuses bypass mode and `--dangerously-skip-permissions`. The Golem agent reads the managed settings Claude Code actually applies, so once the lock is in place, bypass set in a settings file drops to information.

> [!WARNING]
> Do not set Claude Code's `strictPluginOnlyCustomization` managed setting on machines that run Golem. It blocks hooks and MCP servers from user settings, which is where Golem's entries live, and the result looks like a healthy install with no agents reporting.

## Then enforce

1. Switch the rule packs you watched from Shadow to Enforce.
2. Move individual rules to Ask or Block. See [Rules and CEL](/rules) and [Block, hold and contain](/response).
3. Start with the built-in rule **Shell command with permission prompts off**. It ships at Watch. At Ask it puts a person back in front of every shell command in a Claude Code, Codex or Cursor session that runs without its prompts; at Block it refuses them. The posture drawer links to it.

## Check the rollout

Do not take an installer's exit status as proof of capture. On a sample of machines:

```bash
golem status --json
golem doctor
```

`golem doctor` exits non-zero when anything is broken, which suits a compliance script. In the console, **Hosts, Groups & Profiles** lists each enrolled machine. A machine that reports healthy but shows no agent activity usually has clients that were never connected, or connected for a different user. Tools nobody connected show up under [Shadow AI](/coverage#shadow-ai).

To deploy Golem's `golem-fleet` skill where users cannot edit it, run `golem install-skill` as root. It writes the skill into Claude Code's managed skills folder, where it outranks the per-user copy. `golem install-skill --print` writes it to standard output instead, for tooling that places files itself.

## Updates across a fleet

The Golem agent never updates itself. By default it asks the coding agent, once per session, to offer the person `golem update`. A fleet that updates by MDM can turn that off with **Update notice** in a profile's **Maintenance** section and push `golem update` on its own schedule, in the same user context as the install.

## Remove it fleet-wide

On macOS and Linux, push the uninstaller in the user's context, the same way as the install:

```bash
curl -fsSL https://app.golem.team/install.sh | sh -s -- --uninstall --purge
```

On Windows, run `golem setup-client --remove` in each user's context first, then the uninstaller as SYSTEM:

```powershell
$env:GOLEM_UNINSTALL='1'; $env:GOLEM_PURGE='1'; iwr -useb https://app.golem.team/install.ps1 | iex
```

Then remove what the uninstaller does not: a managed `golem-fleet` skill you deployed as root, Claude Code's skill and command files in each user's `~/.claude`, and Apple's `container` package on Macs that had the sandbox. Each machine stays in the console with its signed history; revoke its key from its page under **Hosts, Groups & Profiles** if it should never report again. See [Uninstall](/install#uninstall).

To contain machines rather than remove Golem, see [Quarantine a machine](/response#quarantine-a-machine).

## What this does not do

- **Golem never changes an agent's settings.** The posture report reads them and tells you; the fix goes through your MDM or the client's own admin console.
- **It cannot stop a program from starting.** A risky mode is something a rule can act on while the session runs, through `entry.permission_mode`, not something Golem can prevent.
- **Some modes leave no trace.** Gemini CLI's YOLO switched on inside a session, and Devin's mode changed inside a session, are not seen. A short session of a client Golem did not connect can fall between two 10-minute scans.
- **Connecting clients is per user.** A machine with several people needs `golem setup-client` in each of their sessions.
