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

# Install the Deployed Sensor

> Install the visiq-discovery sensor persistently on a host or across a managed fleet, with an authenticated download and a verified checksum.

The **Deployed Sensor** is the `visiq-discovery` binary installed to stay: it lives on the host, re-scans on a schedule, and reports what it finds to your Discover dashboard. It is the recommended way to run [Discovery](/discovery/introduction) — on a single machine or across a managed fleet.

It is distinct from an **On-Demand Scan**, which runs the same binary once through a device-management tool's own execution channel and leaves nothing installed. The mnemonic: *the sensor stays, the scan vanishes.*

***

## Before you start

* A **harness key** for your tenant (**Settings → Harness Keys**, or the Connectors page). The same key both authorizes the download and links the scan's findings to your tenant. Without a key the sensor still runs, but only prints a local report.
* Administrative access on the target host — root on Linux and macOS, an elevated shell or SYSTEM on Windows.

<Warning>
  **The binary is not code-signed yet.** Apple notarization and Windows Authenticode signing are being provisioned. Until they land the **SHA-256 checksum is the integrity control**, which is why every command below verifies the digest *before* executing the binary — never skip that step. On Defender-hardened Windows fleets you may also need to allowlist the binary by SHA-256 or path, or grant it managed-installer trust: the opt-in ASR prevalence rule and Defender cloud reputation can block a fresh binary regardless of signature. (This is not SmartScreen, which does not fire for a SYSTEM-context download — and Authenticode signing alone would not satisfy the prevalence rule either.)
</Warning>

***

## Install on one host

The dashboard generates this command with your key already spliced in — copy it from **Connectors → Discovery Agent** rather than transcribing it. The shape is:

```bash Linux / macOS theme={null}
# Register what this host discovers to your VisIQ tenant
export VISIQ_API_KEY="<your harness key>"

# Resolve a short-lived download URL + its integrity digest (your tenant key authorizes it)
hdr="$(mktemp)"
url=$(curl -fsSL -D "$hdr" -H "Authorization: Bearer $VISIQ_API_KEY" "https://api.visiqlabs.com/api/discovery/download/linux-x64")
curl -fsSL -o visiq-discovery "$url"
sha=$(awk 'tolower($1)=="x-visiq-sha256:"{gsub(/\r/,"",$2);print $2}' "$hdr"); rm -f "$hdr"
# Verify the binary BEFORE running it; abort on a checksum mismatch
if [ -n "$sha" ]; then
  ( command -v sha256sum >/dev/null 2>&1 && printf '%s  %s\n' "$sha" visiq-discovery | sha256sum -c - \
      || printf '%s  %s\n' "$sha" visiq-discovery | shasum -a 256 -c - ) \
    || { echo "VisIQ: checksum verification FAILED — aborting" >&2; exit 1; }
else echo "VisIQ: no integrity digest returned; skipping checksum verification" >&2; fi
chmod +x visiq-discovery
./visiq-discovery scan
```

On **macOS** add `xattr -d com.apple.quarantine visiq-discovery 2>/dev/null || true` before `chmod`. A binary fetched with `curl` never carries the quarantine attribute, so Gatekeeper does not block it — the command strips the attribute defensively anyway, which matters if the file ever arrives by another route.

On **Windows**, use the PowerShell form the dashboard generates. It enables TLS 1.2 first (Windows PowerShell 5.1 on older images omits it and every HTTPS call fails before anything else), passes `-UseBasicParsing` (required under the SYSTEM/Server Core hosts that device-management tools run), verifies the digest with `Get-FileHash`, and runs `Unblock-File` before the scan.

<Warning>
  **Pass the key as an environment variable, never as a command-line flag.** Anything on the command line is visible in the process list and lands in shell history. Every command VisIQ generates exports `VISIQ_API_KEY`; there is no `--key` option, on purpose.
</Warning>

Confirm it worked: `./visiq-discovery --version` prints the sensor version, and a successful tenant report ends with `visiq-discovery: reported to tenant (id …)`. If that line is absent the scan stayed local — the key did not reach the process.

***

## Pick the right platform

The download path ends in a platform slug. Requesting one VisIQ does not publish returns `400` with the valid list.

| Slug           | Binary                            |
| -------------- | --------------------------------- |
| `linux-x64`    | `visiq-discovery-linux-x64`       |
| `linux-arm64`  | `visiq-discovery-linux-arm64`     |
| `darwin-x64`   | `visiq-discovery-darwin-x64`      |
| `darwin-arm64` | `visiq-discovery-darwin-arm64`    |
| `windows-x64`  | `visiq-discovery-windows-x64.exe` |

***

## Roll it out to a fleet

The same command hardens into a script your device-management console pushes. It installs to a stable path, runs as root or SYSTEM, and is **idempotent** — safe to re-run on every check-in, because it downloads only when the binary is absent and re-scans every time.

The dashboard carries console-specific walkthroughs for Microsoft Intune, Jamf Pro, Kandji, Group Policy, Configuration Manager, Workspace ONE, Ansible, JumpCloud and NinjaOne, each with the exact field to paste into and that console's own pitfalls. Two rules hold across all of them:

* **Assign to a device group, never a user group.** A root/SYSTEM install targeted at users silently no-ops.
* **Exit 0 and stay idempotent.** Consoles read a non-zero exit as a failed deployment and will retry.

### Microsoft Intune, the persistent way

On Windows the robust path is a **Win32 managed app** — the app model is what makes the sensor persistent and self-healing, rather than a one-shot script:

1. **Package it.** Wrap `install.ps1` and `uninstall.ps1` with the Microsoft Win32 Content Prep Tool (`IntuneWinAppUtil.exe`) into a single `.intunewin`.
2. **Create the Win32 app** under **Apps → Windows → Add → Windows app (Win32)**. Install command `powershell.exe -ExecutionPolicy Bypass -File install.ps1`, uninstall command the same with `uninstall.ps1`, and on the Program page set **Install behavior = System**.
3. **Add a detection rule** — for example the presence of the run-scan wrapper under `%ProgramData%\VisIQ\discovery`, or the *VisIQ Discovery Sensor* scheduled task. Without one, Intune re-offers the installer in a roughly 24-hour loop.
4. **Assign it to an Entra device security group as Required**, starting with a pilot ring. The sensor installs on the next device check-in and self-heals on every scheduled scan after that.

The `.intunewin` should be Authenticode-signed or managed-installer-trusted before a broad rollout — see the signing caveat above.

For **macOS and Linux**, Intune runs the bootstrap script instead, and the details differ per platform in ways that bite:

| Platform                  | Where it lives in Intune                                                                      | Run context                                                                                                | Cadence                                                                             |
| ------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **Windows** (script path) | Devices → Manage devices → Scripts and remediations → Platform scripts → Windows 10 and later | Set *Run this script using the logged on credentials* = **No**. The default is Yes, so you must change it. | Runs once, with retries — not on every check-in.                                    |
| **macOS**                 | Devices → macOS → Manage devices → Scripts (its own node, **not** under Platform scripts)     | *Run script as signed-in user* = No is already the default, so it runs as root.                            | Runs once unless you set a script frequency. Check-in is roughly every 8 hours.     |
| **Linux**                 | Devices → Manage devices → Scripts and remediations → Platform scripts → Linux                | Set *Execution context* = **Root**. The default is User, which only runs when somebody signs in.           | Recurring only — the default frequency is every 15 minutes, so idempotency matters. |

Both script forms must begin with a `#!` shebang on macOS and Linux.

<Note>
  Intune's Linux support is desktop-only and narrow — corporate-enrolled Ubuntu Desktop and RHEL on x86-64, GNOME, no servers and no ARM. For Linux servers or an agentic-AI fleet, install the Deployed Sensor directly with a configuration-management tool instead.
</Note>

***

## How the authenticated download works

The binary lives in a **private bucket**. There is no public download and no public GitHub release — the same pattern commercial endpoint vendors use.

```http theme={null}
GET /api/discovery/download/<platform>
Authorization: Bearer <harness key>
```

A successful call returns **`200` with a short-lived pre-signed URL as the plain-text body**, plus these headers:

| Header                    | Meaning                                                                            |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `X-Visiq-Sha256`          | The digest of the binary the URL will serve. Verify against this before executing. |
| `X-Visiq-Filename`        | The released asset name for the platform.                                          |
| `X-Visiq-Version`         | The exact version being served.                                                    |
| `X-Visiq-Release-Channel` | The channel the version was resolved from.                                         |
| `X-Visiq-Url-Ttl-Seconds` | How long the pre-signed URL stays valid.                                           |

The heavy bytes come straight from object storage and are never proxied through VisIQ, and your `Authorization` header is never forwarded to the storage host — the pre-signed URL is the only credential it sees.

**The digest and the bytes cannot disagree.** VisIQ resolves the channel pointer to a version, then reads both the checksum manifest and the binary from that *immutable* version prefix. A release published mid-download cannot leave you verifying one build and running another.

Two optional, validated query parameters let a self-updating agent fetch exactly what its policy targets:

* `?version=<version>` — pin an immutable version.
* `?channel=<channel>` — take a specific channel's head instead of the default.

Anything path-unsafe is rejected with a `400` rather than reaching storage.

| Status | Meaning                                                                                                    |
| ------ | ---------------------------------------------------------------------------------------------------------- |
| `200`  | Pre-signed URL in the body, digest in the headers.                                                         |
| `400`  | Unknown platform, or an invalid `version` / `channel`.                                                     |
| `401`  | Missing or invalid harness key.                                                                            |
| `503`  | The release could not be resolved. VisIQ fails closed here — it never returns a fabricated or partial URL. |

***

## What happens after the scan

The sensor performs a read-only scan and posts one structured report per host to your tenant, where it appears under **Discover → Findings**. Re-run `visiq-discovery scan` any time to refresh a host's inventory; a fleet install does this on its own schedule.

See [Discovery](/discovery/introduction) for what each scanner detects and the read-only guarantees the sensor holds to.
