> For the complete documentation index, see [llms.txt](https://docs.xygeni.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/airgapped-scanning.md).

# Airgapped (Offline) Scanning

In airgapped mode a scan completes **without any interaction with the Xygeni cloud platform**: nothing is uploaded, and the scan needs no reply from the backend to finish. Findings stay inside your network and reports are written **encrypted at rest** — the console shows only statistics (counts, files, duration). To read a report you run a small, **metered online unlock** from a connected host; the report body itself never crosses the boundary.

How much other outbound traffic the scan may use (internal mirrors, public registries, nothing at all) is up to you — see [Isolation modes](#isolation-modes). The default is a fully isolated run with **no network calls**.

{% hint style="info" %}
**Nothing leaves the enclave.** Only a few-KB, findings-free *key request* is carried out of the air gap, and a few-KB *key response* is carried back in. No source code, dependency manifests, secret values, vulnerability details, or plaintext repository names are ever transmitted.
{% endhint %}

Airgapped scanning is a **licensed capability**. It is enabled per customer and delivered through your standard scanner package — there is no separate download or Docker image. The same licence can still run normal, online scans; airgap is engaged **per run** with the `--airgap-mode` option.

## Before you start

You need:

* An **air-gapped (offline) licence** issued by Xygeni for your account.
* The scanner installed on the isolated host (see [Xygeni CLI Installation](/xygeni-scanner-cli/xygeni-cli-overview/xygeni-cli-installation.md)).
* A **connected host** with the scanner and your Xygeni API token, used only for the read/unlock step ([The Unlock Ceremony](/xygeni-scanner-cli/xygeni-cli-overview/airgapped-scanning/the-unlock-ceremony.md)).

## Setup

### 1. Request an air-gapped licence

Ask Xygeni (sales or support) to enable air-gapped scanning for your account. The licence is short-lived and rolling-renewed; it carries only public key material — **no secret** is placed on your host.

### 2. Send your host fingerprint(s)

An air-gapped licence is bound to one or more **approved enclave hosts**. On each host that will run scans, print its fingerprint and send the values to Xygeni:

```bash
xygeni util airgap fingerprint
```

This prints a value like `v1:9f3c…` (and the hostname, as a label). It needs no licence or token and makes no network call. Xygeni binds the licence to the fingerprints you supply.

{% hint style="info" %}
The fingerprint suits fixed enclave hosts (bare metal or pinned VMs). On containers or autoscaled hosts the value may be weak or change; run `xygeni util airgap fingerprint` with enough privileges to read the hardware serials and confirm the value with Xygeni. Re-imaging a host means requesting a refreshed licence.
{% endhint %}

### 3. Install the licence

Place the licence file at `conf/xygeni.lic` on the isolated host, exactly as for a normal Xygeni licence.

### 4. Run a scan

Add the top-level `--airgap-mode` option **before** the scan type:

```bash
xygeni --airgap-mode isolated sast -n my-project /path/to/code
```

The scanner validates the licence locally (signature, host fingerprint, expiry), runs the offline engines, and writes for each scan type an encrypted report plus a small key request:

```
my-project.sast.json.enc        <- encrypted report (stays in the enclave)
sast.unlock-req                 <- key request (carry this out; no findings inside)
```

The console prints counts by severity, files scanned, and duration — never the findings. `--fail-on` exit codes still work (they derive from the counts).

{% hint style="warning" %}
`--airgap-mode` is a **top-level** option: it must come before the scan type (`xygeni --airgap-mode <mode> sast …`), not after it. If the option is present but the level is missing or invalid, the scanner fails **closed** to `isolated` rather than silently going online.
{% endhint %}

To read the reports, continue with [The Unlock Ceremony](/xygeni-scanner-cli/xygeni-cli-overview/airgapped-scanning/the-unlock-ceremony.md).

## Isolation modes

`--airgap-mode` takes one of three levels. All three **encrypt reports and require the unlock**; they differ only in how much outbound traffic the scan is allowed during enrichment.

| Mode        | Outbound traffic                                                                                                     | Use it when                                                                |
| ----------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `isolated`  | None (default, fail-safe).                                                                                           | A strict enclave with no reachable services.                               |
| `filtered`  | Only hosts you list in `conf/airgap-allowlist.txt`.                                                                  | You have internal mirrors/registries/SCM to reach.                         |
| `no-upload` | All outbound **allowed**, including the Xygeni backend for data **retrieval** — but findings are **never uploaded**. | A connected network where you still require findings to stay confidential. |

**Loopback** (`localhost`, `127.0.0.0/8`, `::1`) is always allowed and never counts as outbound traffic.

### The `filtered` allowlist

In `filtered` mode the scanner reaches only the hosts listed in `conf/airgap-allowlist.txt` (one per line):

```
# conf/airgap-allowlist.txt
registry.npmjs.org
search.maven.org
*.github.com
artifactory.internal
```

* One host, or a `*.suffix` wildcard that matches any sub-domain (`*.github.com` matches `api.github.com`, but **not** the apex `github.com` — list it separately).
* Blank lines and `#` comments are ignored; matching is case-insensitive.
* An **empty or missing file** means *block everything* — identical to `isolated`. It is never inverted to "allow all".

{% hint style="info" %}
**Finding what to allow.** Run once under `isolated` (or `filtered` with an empty list). Blocked lookups don't fail the scan — they are skipped, and the summary reports the hosts it skipped:

`Airgap: reduced coverage - 2 outbound lookup host(s) were skipped offline (registry.npmjs.org, search.maven.org)`

Add the hosts you want to reach to `conf/airgap-allowlist.txt` and re-run in `filtered` mode.
{% endhint %}

## What runs under air-gap

Local analysis runs offline; checks that need Xygeni's online data or your SCM/CI **degrade** or are **skipped** in the offline modes. `no-upload` allows retrieval from Xygeni — so SCA and enrichment run — and withholds only the *upload* of findings. A blocked lookup never aborts the scan: it is skipped and reported as *reduced coverage*.

| Scan                       | `isolated` / `filtered`                   | `no-upload` |
| -------------------------- | ----------------------------------------- | ----------- |
| SAST (code security)       | ✅ Full                                    | ✅ Full      |
| IaC                        | ✅ Full                                    | ✅ Full      |
| Misconfigurations          | ✅ Full                                    | ✅ Full      |
| API Security               | ✅ Full                                    | ✅ Full      |
| Secrets                    | ⚠️ Full — no live verification            | ✅ Full      |
| Dependencies               | ⚠️ Degraded — registry enrichment skipped | ✅ Full      |
| Malware                    | ⚠️ Degraded — reputation checks skipped   | ✅ Full      |
| Inventory                  | ⚠️ Degraded — SCM/CI enrichment skipped   | ✅ Full      |
| SuspectDeps                | ❌ Not run — needs registry data           | ✅ Full      |
| Code Tampering             | ❌ Not run — needs SCM/CI                  | ✅ Full      |
| Compliance                 | ❌ Not run — needs SCM/CI posture          | ✅ Full      |
| SCA (dependency CVEs)      | ❌ Not run — needs Xygeni's vuln database  | ✅ Full      |
| AI Security (early access) | Local / self-hosted model only            | ✅ Full      |

**Legend:** ✅ Full — runs completely · ⚠️ degraded — runs with offline gaps · ❌ not run — unavailable in that mode.

{% hint style="warning" %}
**SCA requires `no-upload`.** Software Composition Analysis correlates your dependencies against the Xygeni vulnerability database, so it runs only in `no-upload` mode. Under `isolated`/`filtered`, SCA and the other wholesale-online scans (SuspectDeps, Code Tampering, Compliance) are skipped up front with a single notice. Under `no-upload` every scan runs — only the *upload* of findings is withheld, and reports stay encrypted.
{% endhint %}

## Enforcing a policy offline (optional)

Detector policy and CI/CD gating work without the server:

* **Guardrails / `--fail-on`.** Local guardrails — `--fail-on 'critical'`, `--fail-on 'file:my.xyflow'`, or inline expressions — run offline and produce a pass/fail exit code from the same counts shown on the console. Server-reference forms (`--fail-on '#name'`, `--audit-project`) are not available offline.
* **Detector policy.** Apply an exported policy with `--policy <dir>` (one `<scantype>.json` per scan type). Export it on a connected host from a named server policy:

  ```bash
  xygeni util airgap policy-export --policy-name "My Policy" -o ./policy
  ```

  Carry the `./policy` directory into the enclave and pass it with `--policy ./policy`. Re-export after upgrading the scanner so newly-shipped detectors are not left disabled.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/airgapped-scanning.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
