> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blindsight.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Devices & enrollment

> The fleet view: device status, identity binding, tamper events, and lifecycle actions for every laptop running the endpoint agent.

**Endpoint → Devices** is the operational
console for the endpoint agent fleet. Every laptop with the agent
installed shows up here, with identity status, last seen, traffic
volume, and tamper history.

Seeing the fleet needs `endpoint.devices.view`. Enrolling, revoking,
pausing, moving and removing devices, and downloading the install
bundle, need `endpoint.devices.manage`. The built-in `endpoint-viewer`
role holds the first and `endpoint-admin` holds both. See
[Endpoint permissions](https://docs.blindsight.io/endpoint/permissions.md).

## The fleet table

| Column | What it shows |
| - | - |
| **Device name** | Hostname reported by the agent. Editable for clarity (`design-lead-laptop`). |
| **OS** | `macOS 14.5`, `Windows 11 Pro 23H2`, etc. |
| **Identity** | `bound` / `auto` / `pending` / `service`. See [Identity binding](https://docs.blindsight.io/endpoint/identity.md). |
| **User** | Directory user attached to the device, when identity is `bound` or `auto`. |
| **Agent version** | Version of the installed agent (`1.0.29` at the time of writing). |
| **Status** | `online`, `idle`, `offline`, `tampered`. |
| **Protection** | Whether the device is actually inspecting. `protected`, `degraded` (intercepting, but not everything), or `bypassed`. A paused, revoked, or silent device is never reported as protected. |
| **Last seen** | Most recent ping from the agent. Agents heartbeat every 60 seconds; a device is counted offline after five minutes of silence. |
| **Verdict mix** | Counts of `allow` / `redact` / `block` for the device in the last 7 days. |
| **Tamper events** | Number of high‑severity events in the last 30 days. Click to drill in. |

### Filters

Filter the table by:

* **Identity state** (focus on every `pending` device).
* **OS** (push a Windows‑only policy change).
* **User or directory group** (cross‑check coverage for a team).
* **Tamper status** (find devices where the agent isn't running clean).
* **Last‑seen window** (find dormant laptops the agent isn't reaching).

## Enrollment tokens

The same page houses the workspace's enrollment tokens. Click
**Tokens** to see:

| Field | Meaning |
| - | - |
| **Name** | Free‑form label (`fleet-mac-pilot`, `staging`), up to 128 characters. |
| **Token** | The plaintext value, shown once at creation. Only its hash is stored. |
| **Prefix** | The first characters of the token, so you can tell rows apart afterwards. |
| **Last used** | When a device last enrolled with it. |
| **Status** | `active` or `revoked`. |

> [!WARNING]
>
> An enrollment token has **no expiry and no activation cap**. It is a
> workspace-wide bootstrap secret that stays valid until you revoke
> it, so treat it like a credential: store it in your MDM's secret
> vault and rotate it on a schedule.

### Lifecycle

**1. Create**

**Tokens → New.** Give it a name. The plaintext is shown once.

**2. Distribute**

Embed in your MDM payload (see [Fleet rollout](https://docs.blindsight.io/endpoint/fleet-rollout.md)).

**3. Rotate**

Mint a new token, update MDM, then revoke the previous token.
Existing devices are unaffected by token revocation; rotation only
matters for new enrollments.

**4. Revoke**

From the token row, **Revoke**. New enrollments fail; existing
devices keep working.

### Minting one from the install page

You no longer have to leave the install instructions to get a token.
The **Install** tab has a button that mints one and drops the real
value straight into the command it is showing you, in place of the
placeholder, so you can copy a line that actually works.

The token it creates is named `Install page · <date> <time>` and is
an ordinary token: it appears in the list, and you revoke it the same
way.

| Route | Permission |
| - | - |
| `POST /api/runtime-security/agent/enrollment-tokens` | `endpoint.devices.manage` |
| `GET /api/runtime-security/agent/enrollment-tokens` | `endpoint.devices.manage` |
| `POST /api/runtime-security/agent/enrollment-tokens/{uuid}/revoke` | `endpoint.devices.manage` |

```bash theme={null}
curl -X POST $API_BASE/api/runtime-security/agent/enrollment-tokens \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"fleet-2026-q3"}'
```

```json theme={null}
{
  "uuid": "…",
  "name": "fleet-2026-q3",
  "enrollment_token": "antde_xxxxxxxxxxxxxxxxxxxx",
  "token_prefix": "antde_xxxx",
  "created_at": "2026-08-05T09:12:00Z"
}
```

`enrollment_token` is the only time you see the plaintext. The
listing route never returns it.

## Per‑device actions

Click any device row to open the detail panel.

| Action | What it does |
| - | - |
| **Force re-bind** | Triggers a new SSO device‑code flow on the agent. Also clears any pause. |
| **Revoke device** | Marks the device revoked. It cannot enrol again until you say so. See [Revocation](#revocation-and-re-enrollment). |
| **Allow re-enrollment** | Opens a 24‑hour, single‑use window for a revoked device to enrol again. |
| **Resume protection** | Lifts a pause the person set on their own machine. See [Pausing protection](#pausing-protection). |
| **Push policy** | Forces the agent to re‑fetch `/agent/policy` immediately, useful for urgent rollouts. |
| **Reveal CA** | Downloads the org root CA bundle the agent installed, in case IT needs to verify it. |
| **Confirm owner** | States that this machine belongs to a named person in your workspace, so a device without SSO can have a proved owner. It is recorded against the admin who confirmed it. |
| **Release** | Lets a Mac pinned to this workspace join another one. See [Which workspace a Mac answers to](#which-workspace-a-mac-answers-to). |
| **Keep pinned** | Withdraws a release that is still standing. |
| **Move to another client** | MSP operators only: moves the device to another client of the same account. See below. |

## Which workspace a Mac answers to

Enforcement on a Mac answers to one workspace at a time. The device
page says which, in a state next to the device:

| State | What it means |
| - | - |
| **Pinned to this workspace** | Enforcement on the device answers to this workspace only. |
| **Not pinned yet** | It pins to the first workspace that answers. Until then nothing here stops it joining another. |
| **Held by another workspace** | The device answers to a different workspace, so this workspace's pause does not reach it. |
| **Signed in at another address** | The device is signed in somewhere other than where it is managed from, so it is not asking this workspace for enforcement. |
| **Released · until `<date>`** | An admin released it. See below. |
| **Moving in** | It is moving here from another client. |

### Release a device

**Release** lets a pinned Mac join another workspace. It needs
`endpoint.devices.manage`. The release lasts seven days; after that
it reads as no release at all. **Keep pinned** withdraws it sooner.
Release is on the fleet table and on the device page.

> [!WARNING]
>
> While a release stands, the Mac is open to being pinned by whichever
> workspace it signs in to next. Release it only when it is meant to
> leave. Releasing does not lift a pause: this workspace's pause still
> applies until the device actually joins elsewhere. Every release and
> withdrawal is written to the audit trail.

### Move a device between clients

An MSP operator working inside a client can use **Move to another
client** on the device page to send the device to another client of the
same MSP account. It appears only inside an open client session, and
lists only the account's other clients that have not switched off its
access.

The device leaves this client and joins the one chosen, with nobody at
the machine. Its history here stays, marked as moved. Open asks and
requests stay behind. The new client's policy applies, and the owner has
to be confirmed again. A move is refused if the device is revoked, if the
other client already has this machine (a revoked copy left there by an
earlier move is brought back instead), is not licensed for the Endpoint
agent, or is at its device limit. See [Opening a
client](https://docs.blindsight.io/msp/opening-a-client.md).

## Pausing protection

A person can turn inspection off on **their own machine** from the
desktop app, and turn it back on. It is not a fleet-wide switch and
it is not an admin action: it exists so somebody can stop inspection
on a machine they are responsible for, on the record.

> [!WARNING]
>
> While a device is paused it is **not inspected at all**. This is not
> observe‑only mode: prompts pass through untouched and no prompt text
> is stored. Nobody holds `endpoint.protection.pause` by default, and every
> pause and resume is written to the audit trail, at high severity for
> the pause.

### Only a proved identity may pause

The agent runs as the person being governed, so its own opinion about
their permissions would be worth nothing. It asks the platform, and
the platform decides. A refusal comes back as `403` with a code:

| Code | Why |
| - | - |
| `device_identity_unverified` | The device's identity is not `bound`. Only the SSO device-code flow, or an admin choosing **Confirm owner**, sets that. |
| `identity_binding_unavailable` | The workspace has no SSO, so there is no identity to bind. Use **Confirm owner**. |
| `device_unattributed` | The device is not attached to a directory user. |
| `owner_inactive` | That user's account is deactivated. |
| `permission_denied` | That user does not hold `endpoint.protection.pause`. |

An `auto` identity is **not** enough. `auto` means the agent asserted
an OS user name that happened to match a directory account, and
anybody holding the workspace-wide enrollment token could assert an
admin's address. Good enough to attribute traffic, useless as
authority.

If the platform is unreachable, the desktop app does not offer the
toggle at all rather than guessing yes.

### The routes

| Route | Auth |
| - | - |
| `GET /api/runtime-security/agent/protection/pause` | Device token |
| `POST /api/runtime-security/agent/protection/pause` | Device token |
| `POST /api/runtime-security/agent/devices/{uuid}/resume` | Dashboard, `endpoint.devices.manage` |

`POST` takes `{"paused": true | false}` plus an optional `reason`
(free text, up to 280 characters, recorded in the audit trail) and
returns `{"paused", "since", "changed"}`. `GET` also returns
`allowed`, and the platform's own `reason` when it is `false`, which
is what the desktop app renders next to the toggle.

There is **no expiry**. A pause lasts until the person resumes, an
admin resumes it or re-binds the device, or the owning account is
deactivated. It survives a restart, and it travels in the policy
snapshot, so it holds even if the person paused from another machine.

The fleet figures ask the server whether a device is protected rather
than assuming. A device counts as unprotected when it is paused,
revoked, silent for more than five minutes, or reporting a `bypassed`
or `degraded` protection state.

## Revocation and re-enrollment

Revoking a device used to be advisory. The enrollment token is
workspace-wide, so the same machine reappearing with the same
hardware id was quietly reactivated on its next enrolment, and the
revocation evaporated.

Now a revoked device stays revoked. Enrolment is refused until an
admin opens a window for it.

| Route | Permission |
| - | - |
| `POST /api/runtime-security/agent/devices/{uuid}/revoke` | `endpoint.devices.manage` |
| `POST /api/runtime-security/agent/devices/{uuid}/allow-reenrollment` | `endpoint.devices.manage` **and** `endpoint.manage` |

The asymmetry is deliberate: revoking only ever takes a credential
away, so the broader key is enough. Handing one back is a bigger
decision and needs `endpoint.manage` as well. Without it you get `403
reenrollment_not_permitted`.

Authorising re-enrollment on a device that is not revoked returns
`400 device_not_revoked`: you cannot pre-arm one.

> [!NOTE]
>
> The window is **24 hours and single-use**. It is consumed by the
> enrolment that uses it, and revoking the device again clears any
> window still open. Refused attempts are counted on the device row
> and audited (at most one audit record per device per hour).

The fleet listing carries `revoked_at`, `reenrollment_allowed_at`,
`reenrollment_allowed_by_email`, `blocked_reenroll_at`, and
`blocked_reenroll_count`, so you can see a machine trying to come
back.

**1. Reissue a laptop to somebody else**

Revoke the device. The old enrolment is dead and stays dead.

**2. Wipe and re-image**

Push the agent as usual. It enrols as a new device.

**3. Or keep the same machine**

If you want the same hardware back under its old row, open the
re-enrollment window first, then let the agent enrol inside the
24 hours.

## Tamper events

Every tamper attempt is captured. The agent's guardian process and
the service report integrity events to `POST /agent/tamper-events`.

The guardian both records the event **and repairs the layer**, rate
limited so it does not fight a tamper loop faster than once a minute.

| Event kind | What triggered it |
| - | - |
| `ca_removed` | The org root CA was deleted from the machine trust store. The guardian re-imports it. |
| `system_config` | The OS proxy configuration drifted away from what the agent set. The `detail` field names which layers drifted. |
| `pac_restarted` | The PAC server had stopped and was restarted. |
| `mitm_restarted` | The local inspection proxy had stopped and was restarted. `detail` carries the failure message when the restart did not work. |

Each event carries a `kind`, a free-text `detail`, and a timestamp.

> [!NOTE]
>
> Repair is deliberately conditional. If the OS proxy configuration
> has drifted but nothing is listening on the local proxy yet, the
> drift is recorded and **not** repaired: re-pointing the machine at a
> proxy that is not accepting connections would turn a tampered
> machine into one that cannot browse at all. The proxy is brought
> back first.

Tamper events appear in the audit trail with severity `CRITICAL` and
can be pushed to your SIEM via the
[audit webhook](https://docs.blindsight.io/data-security/integrations.md).

### Investigating a tamper event

**1. Open the device**

Click the row in the fleet table. The tamper history is the
bottom panel.

**2. Read the event**

Each event has a `kind`, `detail` (free‑text), and a timestamp.

**3. Take action**

For accidental tampering (a user uninstalled the agent), push
the agent again via MDM. For malicious attempts, follow your IR
runbook and consider locking the device with EDR.

## Bulk operations

From the fleet table:

* **Select rows** with checkboxes.
* **Push policy** to every selected device.
* **Export to CSV** for offline reporting.

Pausing is not a bulk action. A pause is set by the person on their
own machine, and lifted one device at a time, on purpose. To stop
inspecting across the fleet, change the policy rather than pausing
devices.

Bulk actions are audited. The audit row records every device id touched.

## Common workflows

> **Identify devices the agent isn't reaching**
>
> 1. Filter the fleet table by **Last seen > 7 days**.
> 2. Cross‑check against your asset list. Anyone still active in
>    SCIM but absent here probably has a broken agent push.
> 3. Re‑push the MDM payload from your console.

> **Audit endpoint coverage for a team**
>
> 1. Filter by **User → directory group**.
> 2. Confirm every team member has at least one `bound` device.
> 3. Anyone with zero bound devices is either a non‑agent user or
>    has dropped off, escalate to IT.

> **Roll out a policy change progressively**
>
> 1. Change the policy in **Runtime Security → Apps**.
> 2. **Push policy** to a pilot group from the fleet table.
> 3. Watch verdict drift over the next 24h.
> 4. Roll out to the rest of the fleet from the same screen.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.