Rolter is built to run in fully air-gapped environments with no public internet at
build or run time. One outbound call is on by default, the release check, and you
turn it off with ROLTER_UPDATE_CHECK=false. Every other call goes to an endpoint
you configure, and this page lists each one with the setting that turns it on or
off. It also shows how to install Rolter behind an internal mirror and how to
verify the deployment offline.
Runtime egress#
The release check is on by default#
Left alone, two parts of Rolter ask https://api.github.com/repos/rolter-ai/rolter/releases/latest
whether a newer stable release exists:
- The control plane asks once at boot and every six hours. The answer feeds the
version hint in the dashboard footer and
GET /api/v1/version. - The
roltercommand asks beside whatever subcommand you run,gateway,control,easy-upandcheckincluded, and prints one line to stderr when a newer release exists. A successful answer is cached for 24 hours in~/.cache/rolter/update-check.json. A failed one is not cached, so a host that cannot reach GitHub tries again on every invocation.
The request is one GET with a 5-second timeout and a User-Agent: rolter/<version>
header. It carries no installation id, configuration or credentials. A failure is
logged at debug, never delays a command and never changes an exit status, so an
enclave keeps working with the check left on. The attempts still show up in a
firewall or DNS log, and a policy that treats an unexpected external lookup as an
incident will flag them.
Turn it off with one environment variable:
Set it on the control plane and on every host or container that runs the rolter
command. With the Docker image that is every container, because its default command
is rolter easy-up. The rolter-control binary carries the control plane’s check;
rolter-gateway run directly has none.
With the Helm chart, control.updateCheck: false reaches the control plane and the
gateway pods, including their preflight init containers, which also run rolter check:
With the check off, the dashboard footer shows the running version alone and
/api/v1/version reports "enabled": false. See
Update check.
Every outbound path#
The release check is the only path that is on without configuration. Each of the others stays idle until you set the setting in its row, so a deployment that never sets them makes no other outbound call.
Every operator-written URL in the table below, from a provider’s api_base to an
alert channel’s endpoint, also passes the egress policy
before it is called. To refuse internal ranges, turn on block_private and
block_loopback and list the internal hosts the deployment must still reach in
allow_hosts.
| Path | What it reaches | Setting | Default |
|---|---|---|---|
| Release check | api.github.com, described above | ROLTER_UPDATE_CHECK=false turns it off | On |
| Upstream providers | Each provider’s api_base and the provider’s egress_proxy when set. Also the embeddings provider of a semantic cache and the Test button on the Providers screen | The providers and routes you configure | None. The built-in fake-llm makes no outbound call |
| Postgres | The host in ROLTER_DATABASE_URL, from the control plane | ROLTER_DATABASE_URL | Unset: in-memory store |
| Redis | The host in ROLTER_REDIS_URL, from both planes: budgets, rate limits, the response cache, the sign-in throttle and config pub/sub | ROLTER_REDIS_URL, or --redis-url on the gateway | Unset |
| ClickHouse | Request logs, captured payloads, provider health events and the control plane’s analytics and alert queries | CLICKHOUSE_URL or [logging].clickhouse_url. A gateway with neither inherits the control plane’s | Unset: nothing is logged |
| Gateway to control plane | Config poll every 5 seconds, the log destination at boot and adaptive-routing telemetry, all to the snapshot URL’s host. The /admin/* proxy goes to the admin URL | ROLTER_SNAPSHOT_URL and ROLTER_SNAPSHOT_POLL_SECS, ROLTER_ADMIN_URL | Unset: static bootstrap config |
| Control plane to gateway | Dashboard Playground requests, sent through /gw/*. No traffic while nobody uses the Playground | ROLTER_GATEWAY_URL | http://localhost:4000 |
| Alert channels | Each enabled channel’s endpoint: one JSON POST when a rule changes state. See Alerting | Alerting → Channels | No channels |
| OTLP export | The OTLP receiver, for traces, metrics and logs from both planes. See Tracing | OTEL_EXPORTER_OTLP_ENDPOINT, or its _TRACES_, _METRICS_ and _LOGS_ variants. ROLTER_TELEMETRY_ENABLED=false blocks every exporter | Unset: no exporter is built |
| Connector test | The connector’s endpoint: one empty OTLP request when someone presses Test, whether or not the connector is enabled. Rolter sends nothing else to a connector. See Connectors | Observability → Connectors | No connectors |
| Dashboard browser tracing | The OTLP/HTTP endpoint named in the served page, called from each user’s browser | ROLTER_UI_OTEL_ENDPOINT. ROLTER_TELEMETRY_ENABLED=false drops it | Unset: the dashboard loads no tracing code |
| Active health probes | Each provider’s api_base plus [health].path, every interval_secs (10). also_track_via_llm_call sends a one-token completion. See Health checks | [health] enabled | false |
| Model catalogue refresh | Each provider’s model-list endpoint, every [health].catalogue_interval_secs (900), even with [health] off. It feeds GET /v1/models | [health] catalogue_interval_secs. Set 0 to turn it off | 900 |
| Engine metrics scrape | Each provider’s api_base plus /metrics, every interval_secs (5). See Load balancing | [metrics_scrape] enabled | false |
| Provider status pages | A provider’s status_page_url, every [health].status_page_interval_secs (60), even with [health] off | status_page_url on a provider. Leave it unset, or point it at an internal mirror | Unset |
| Cache-aware routing signals | A provider’s kv_events.endpoint (a ZeroMQ subscription such as tcp://vllm-0:5557) and its lmcache.endpoint (an HTTP poll every refresh_secs, 2 by default). See Cache-aware routing | kv_events and lmcache on a provider | Unset |
| MCP servers | The url of each registered server, when a client calls it through /mcp/<server>. See MCP servers | The registered MCP servers | None |
| MCP OAuth | For a server that uses OAuth, the control plane fetches the server’s resource metadata and its authorization server’s metadata when consent starts, posts to the token endpoint when it completes, and refreshes tokens near expiry in a sweep every 60 seconds. See MCP OAuth | MCP servers set to OAuth | None |
| Single sign-on | For each identity provider, the control plane fetches <issuer>/.well-known/openid-configuration and the jwks_uri, and posts to the token_endpoint, at each sign-in. See Single sign-on | Governance → Single Sign-On | No providers |
| Guardrail webhook | The webhook URL: one POST per request, before it goes upstream. See Guardrails | [guardrail_webhook] enabled, or activating a provider under Guardrails → Providers | Disabled |
| PII sanitizer | [pii_sanitizer].url, restore_url when restoration is on, and restore_stream_url under streaming = "restore". See PII sanitizer | [pii_sanitizer] enabled | Disabled |
| Webhook plugins | The endpoint of each enabled plugin instance, on every matching request at its stage. See Plugins | Plugins: an instance is live while it is enabled | No instances |
Several rows point at services you host inside the enclave, such as a guardrail service, a Presidio sanitizer or an OpenTelemetry Collector. Rolter opens those connections too, so they are listed.
These do not make an outbound call:
- SCIM provisioning is inbound only. Your identity provider calls
/scim/v2/*onROLTER_PUBLIC_URL, and Rolter never calls it back. - Invitations send no email. Rolter returns the link and you pass it on.
- Payload capture writes to ClickHouse and nowhere else, and only when
[logging.payload_capture]is enabled. - Prometheus metrics are scraped from
/metricsby your Prometheus. Rolter pushes nothing to it. - Built-in guardrails and the
fake-llmmodel run inside the gateway process.
rolter check --connect opens a TCP connection to the hosts in ROLTER_DATABASE_URL
and ROLTER_REDIS_URL, and to the mail relay in ROLTER_SMTP_URL when one is set. It is a command you run, not a background task.
The dashboard and the API reference#
The interactive API reference at /docs embeds the Scalar JS bundle in the binary
and sets withDefaultFonts: false, so it never reaches a CDN or fonts.scalar.com.
The docs_page_is_self_contained and scalar_bundle_is_embedded tests in
crates/rolter-gateway/src/openapi.rs enforce it.
The dashboard SPA is served as static assets by the control plane. Its fonts and
icons are vendored in the build, and it loads no third-party scripts, fonts or
styles. Its documentation links are off unless you configure a documentation host,
so an enclave with no mirror of this site simply sees no links. See
Documentation links. The footer’s repository and
bug-report buttons point at github.com. They are ordinary links that open only
when someone clicks one, and the page loads nothing from them.
Install paths through a mirroring proxy#
Air-gapped sites usually proxy public registries through an internal mirror (Sonatype Nexus, JFrog Artifactory, Harbor, …). Pick the path that matches how you ship Rolter.
Pull through a registry that proxies GHCR/Docker Hub:
Or transfer a fully offline image with docker save / docker load:
Install through a Nexus/Artifactory PyPI proxy:
Or install a downloaded wheel with no index at all:
Point Cargo at a registry mirror via .cargo/config.toml:
For a fully offline build, vendor the sources on a connected host and copy them in:
The Rust build follows the crates.io tab. The UI needs an internal npm mirror
for bun install:
Operator checklist#
Set before you deploy:
ROLTER_UPDATE_CHECK=falseon the control plane and wherever theroltercommand runs. In Helm,control.updateCheck: falsecovers both Deployments.
Must be reachable inside the enclave:
- Every configured provider
api_base(your internal model servers or a proxied provider endpoint). - Postgres, Redis, and ClickHouse hosts, only for the features you enable.
- The control plane, if the gateway runs with
--snapshot-url. - Every endpoint you chose to configure from the table above, such as an alert webhook, an identity provider, an MCP server or a collector.
Must NOT be required:
- Public package registries at run time (only at install/build time, through the mirror).
- CDNs (
cdn.jsdelivr.net,fonts.scalar.com, npm/unpkg). Rolter references none. api.github.com. WithROLTER_UPDATE_CHECK=falsethe release check is not attempted.- Telemetry endpoints, unless you deliberately set
OTEL_*to an internal collector. - Provider status pages. Leave
status_page_urlunset.
Offline smoke test#
Verify a running gateway with zero external providers using the built-in
fake-llm model (deterministic, no upstream or secrets needed):
A 200 with a lorem-ipsum completion confirms the gateway serves traffic with no
outbound calls. Open http://localhost:4000/docs and confirm the API reference
renders with no network requests leaving the host.