# Air-gapped deployment (https://docs.vitrinka.ai/security/airgap)



## The switch [#switch]

Set exactly `AIR_GAP=1` to disable the configured public integrations at startup: the email provider, Telegram, GitHub, the AI channel and the AI backend (`EVE_*`, `AI_BACKEND_*`), managed AI, Plane, Stripe, web push, Sentry error reports and the OTLP trace exporter. The breached-password check, link-card previews and the MCP sign-in's client-ID metadata documents are switched off too, and single sign-on loses the public internet. The server clears their settings and logs what it disabled. Other values leave this switch off; a typo does not stop startup. Network isolation also requires your firewall and correctly configured internal endpoints.

## What stays on purpose [#stays]

| Reachable                                                                                      | Why                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| S3 and the screenshot sidecar                                                                  | They point at your own endpoints on your own network.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| SMTP                                                                                           | An internal relay remains available for messages such as password resets. Use `SMTP_*` instead of an external provider key.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| The issue connectors (Jira, Plane), a workspace's AI backend and an organization's AI endpoint | Configured against your own tracker or model — but only one that deployment authority saved reaches anything ([Outbound targets](/operator/connectors#outbound)), and then only an internal address: loopback, private (RFC 1918, ULA), link-local, CGNAT or class E, never a cloud metadata or credential endpoint such as `169.254.169.254`. Its HTTP client checks every address a host resolves to, so an `atlassian.net` URL in an air-gapped deployment gets a refusal, not a firewall timeout; a target an ordinary admin saved is refused outright, and so are the vendor-hosted AI providers, the ChatGPT and Claude subscriptions and a base URL naming a vendor's host. |
| Single sign-on (OIDC)                                                                          | `AIR_GAP=1` opens nothing by itself and takes the public internet away: SSO reaches only an identity provider inside the internal ranges `VITRINKA_SSO_PRIVATE_NETWORKS` lists, on a connection deployment authority saved, and any other connection reaches nothing ([an identity provider on your network](/security/identity#sso-network)). A listed range outside the internal network (loopback, RFC 1918, ULA, CGNAT, class E) refuses the boot; a public issuer is refused, and **Check health** in the SSO settings names `AIR_GAP` and the fix.                                                                                                                           |
| The transcription sidecar                                                                      | Meeting audio goes to a whisper.cpp service on your compose network and never leaves it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| The metrics listener                                                                           | It only answers scrapes from your own network and pushes nothing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |

MCP connectors still sign in, without fetching anything: the OAuth metadata stops advertising client-ID metadata documents (CIMD) and the server never fetches one, so clients such as Claude Code register through dynamic client registration (`POST /oauth/register`) instead. A client that registered with a metadata document before the switch is refused at consent and at the token endpoint, naming the registration endpoint, and registers again there.

There is no server-side update ping to disable: `/api/v1/version` is an inbound beacon clients poll, and the update check itself lives in the CLI.

## What leaves the host [#egress]

The server sends nothing to the vendor: no telemetry, no update ping, no licence check, no usage or seat report. Outbound traffic starts from something you set up or something your people paste:

* **Integrations you configure** — each stays off until its variable or setting exists: mail, single sign-on, the trackers, GitHub, the AI channel and backends, the key store, the audit export target, S3, the sidecars, web push, Stripe, and the observability exporters ([observability settings](/reference/env#observability)).
* **Links your people paste** — off under `AIR_GAP=1`: a pasted link keeps its bare URL. Otherwise a link card fetches the page it names, its preview image and favicon from public internet addresses only, checked on the resolved address at connect time and at every redirect, with no environment proxy; a link to a private, loopback or link-local address, or to a name that does not resolve, keeps its bare clickable URL and the card says why. With a capture sidecar set, it screenshots only the page that fetch reached — a failed fetch captures nothing — on a host vetted as public just before each capture. The capture keeps the page on that host and blocks requests to IP-literal, single-label, localhost and internal-suffix hosts, and a shot whose page loaded a response from a non-public address, or from one the capture could not determine, is discarded; the card keeps its title and preview image. The sidecar resolves names itself, though: a page can still make it send a request to an internal address through a public name that resolves privately, and WebSockets and fetches a worker starts are not observed. The response never reaches the board, but restrict the sidecar's own egress if the request alone matters to you.
* **Two defaults** — setting a password runs the breached-password check, which sends five hex characters of the password's SHA-1 and compares locally, and an AI client that registers with a client-ID metadata document has that https document fetched, from public addresses only. `AIR_GAP=1` turns both off.

Plan limits are enforced only with `VITRINKA_METERED=1` or configured Stripe billing, and `AIR_GAP=1` blanks Stripe: an air-gapped install left unmetered runs every organization on Enterprise limits ([billing settings](/reference/env#billing)). Either way nothing is reported anywhere.

## The bundle [#bundle]

The enterprise bundle ships `image.tar`, `LOAD-AIRGAP.md`, `SHA256SUMS`, the six CLI binaries and `cli/latest.json`. Verify, load, point `VITRINKA_IMAGE` at the loaded image, set the switch:

```bash
shasum -a 256 -c SHA256SUMS
docker load -i image.tar
```

Workstations get the CLI from the bundle's `cli/` directory, and `VITRINKA_RELEASE_FEED` points `vitrinka update` at your internal mirror so the self-updater keeps working offline — it downloads a raw binary and its checksum, stages it beside the running one, executes the staged copy once, and only then swaps. The full install and upgrade routine is in [Operations](/operator/operations#airgap).

## Licensing offline [#licence]

The licence is a vendor-signed file verified inside the server process against the vendor key built into the release: no activation call, no licence server, no phone-home, and renewal is a file swap and a restart ([Licensing](/security/licensing)). Keep the host clock right from your own time source — expiry is read against it, and a licence that looks issued in the future refuses the boot.

<Callout type="warn">
  `AIR_GAP=1` blanks the *public* integrations. It does not blank a misconfigured internal endpoint — an SMTP relay or S3 endpoint that points at the internet is still yours to fix — and it does not switch off an organization's own AI provider. On an air-gapped install that provider answers only at an endpoint on your network that deployment authority saved ([Outbound targets](/operator/connectors#outbound)) and never at a provider's public default endpoint, so point it at a model endpoint on your network, or leave it unset.
</Callout>
