Network prerequisites
The ports that must reach the appliance, an egress matrix per deployment mode, what a corporate or TLS-inspecting proxy each require, and the verification that proves every path is really open.
- Version: 0.4
- Role: admin_user
- Type: reference
What the network team has to agree before the virtual machine exists: which ports reach the appliance, what the appliance reaches outward in the mode you are deploying, and how it behaves behind a corporate proxy. Settle this first — a blocked path is the single most common reason an ISO installation stops halfway.
Read the egress matrix for your deployment mode. They are not cumulative, and opening more than the mode needs is the usual cause of a firewall change that has to be argued twice.
Inbound — what must reach the appliance
| Port | Protocol | Needed for |
|---|---|---|
| 80 | TCP | The dashboard and the proxy auto-configuration file, on a deployment with no HTTPS front. An appliance installed from the ISO serves HTTPS only and publishes nothing on 80. |
| 443 | TCP, and UDP where the front supports it | The dashboard, the gateway API and the model proxy. UDP carries HTTP/3; blocking it costs nothing but the newer transport. |
| 1344 | TCP | The Internet Content Adaptation Protocol (ICAP) listener on nestjs-core, which a third-party secure web gateway dials into. Only when you integrate an external gateway. |
| 8443 | TCP | The explicit HTTPS forward proxy on cid-inline-proxy, which client browsers connect to. Only in an inline-proxy deployment. |
| 8084 | TCP | Transparent interception on cid-inline-proxy, for a deployment that redirects traffic with firewall rules instead of a proxy setting. Only in an inline-proxy deployment. |
| 8090 | TCP | Active Directory user attribution on cid-userid-agent. Only when that profile runs, and loopback-bound by default. |
Which process answers on 80 and 443 depends on how the appliance was delivered: the source and production compose files put a Caddy reverse proxy there, while an appliance installed from the ISO is fronted by a separate nginx container outside the compose project. The ports and their purpose are the same either way.
Two rules govern this table.
-
Only 80 and 443 are open in a standard deployment. Everything else is either loopback-bound or off. The ICAP listener publishes on the interface named by
ICAP_PUBLISH, which defaults to127.0.0.1; exposing it to a LAN gateway is a deliberate change, and the port must then be firewalled to that gateway's address. -
The inline proxy binds on the host network namespace. It runs with host networking so that client addresses arrive as themselves, which means a port already occupied on the host is not reported as a failure — the container still shows as running. Survey the ports before first boot:
A collision found here is a line in the appliance's environment file before first boot. Found later, it is a port change, a regenerated proxy auto-configuration file, and a re-push of that file to every endpoint.
The full per-service table, with the compose profile and declared memory limit of each container,
is generated in services, ports and profiles. The variables that move
these ports — ICAP_PORT, ICAP_PUBLISH, ICAP_BIND, INLINE_PROXY_HTTPS_PORT,
INLINE_PROXY_TPROXY_PORT, INLINE_PROXY_PUBLIC_PORT, USERID_AGENT_PORT — are in
environment variables.
Note
INLINE_PROXY_HTTPS_PORT is where the proxy listens; INLINE_PROXY_PUBLIC_PORT is the port the
generated proxy auto-configuration file advertises to browsers. The shipped compose files derive
the second from the first, so changing the listening port in the environment file moves the
advertised port with it.
Egress — every deployment
These four apply whatever mode you run, including an air-gapped one, where the last two are internal and the first two are absent.
| Purpose | Destination | Port | When |
|---|---|---|---|
| Release image pull during ISO provisioning | api.cid222.live | 443 | Installation only, on the online installer ISO. Nothing else is required during the install |
| Online update channel | The channel host in SYSTEM_UPDATE_CHANNEL_URL | 443 | Only when an operator presses check or install. The appliance never polls |
| Name resolution | Your resolvers | 53 | Continuous |
| Time synchronisation | Your time source | 123 | Continuous |
There is no licensing egress. A licence is a signed file bound to the appliance's installation
id. You export an activation request, receive a .lic, and upload it. Expiry is a local clock
comparison. The appliance never contacts a licence server, online or air-gapped.
CID222 itself reports nothing home. No analytics, error-reporting or usage-reporting client runs in the gateway, the dashboard or the detection services. One exception is worth naming: the red-team simulator container bundles its upstream framework's own telemetry module, and the appliance sets no opt-out for it. On a network with no egress the question is moot; on a connected one, block that container's outbound access if the possibility matters to you.
Egress — AI Gateway mode
Users and tools call the appliance, and the appliance calls the model. That is the whole matrix.
| Purpose | Destination | Port | When |
|---|---|---|---|
| Cloud model providers | The hosts the provider clients dial — for example api.openai.com and api.anthropic.com | 443 | Every chat request that reaches a provider |
| LAN or self-hosted model | Your own model endpoint | As your endpoint serves | Every chat request that reaches it. No internet egress |
| Context summarisation | The OpenAI and Anthropic clouds, at fixed addresses | 443 | When a session is summarised |
Warning
Every provider connector can be pointed at a LAN endpoint, but the credentials dialog offers the endpoint field only for Azure OpenAI — so an Anthropic or Google credential created in the dashboard dials the vendor cloud. On a network that cannot reach it, the key saves successfully, the models appear in the picker, and requests fail only when a user sends one.
Verify it, from the appliance, before you call the mode ready:
A timeout, a 000, or a proxy error is the failure. A block page that answers 200 is the failure
that looks like a pass — read the body before believing it.
Egress — inline inspection beside a secure web gateway
The gateway dials in to the appliance; the appliance dials nothing new. This is the mode with the smallest firewall change and no endpoint work at all.
| Direction | Endpoint | Port | Notes |
|---|---|---|---|
| Secure web gateway → appliance | The ICAP listener | 1344 | Publish it off loopback with ICAP_PUBLISH, then firewall the port to the gateway's address |
| Secure web gateway → appliance | The HTTP callout endpoint | 443 | For a gateway that calls out over HTTP rather than ICAP |
| Appliance → anywhere | Nothing additional | — | The traffic has already been decrypted by the gateway |
The clocks matter more than the ports here: keep the appliance and the gateway synchronised against the same time source, or the inspection events and the gateway's access log cannot be correlated after the fact.
Warning
The ICAP listener cannot authenticate its peer. Admission is by source address alone, and an admitted peer is believed down to the username it claims. A gateway running as a virtual machine on the same host as the appliance arrives at the listener as the container bridge address, not its own, which defeats the allowlist — see Every ICAP caller arrives as the same docker bridge address.
Egress — the appliance's own inline proxy
Here the appliance is the thing that re-signs browser traffic, so it needs to reach every site the browsers do.
| Purpose | Destination | Port | When |
|---|---|---|---|
| The sites being re-proxied | The model vendors' web front ends and APIs your policy covers | 443 | Every intercepted request |
| Proxy auto-configuration delivery | Inbound from clients, not egress | 80 or 443 | Every client, at policy refresh |
Egress — endpoint agents and the browser extension
Neither reaches the internet on the appliance's behalf. Both talk to the appliance on 443, so this mode adds inbound reachability from your client estate rather than egress. A firewall that pulls the appliance's external dynamic list does the same: it fetches from the appliance.
Egress — directory, SIEM and the other integrations
| Purpose | Destination | Port | When |
|---|---|---|---|
| Active Directory or LDAP bind, sync and just-in-time provisioning | Your domain controllers | 636 for LDAPS, 389 for plain LDAP | On sign-in, on sync, and on test |
| Active Directory user attribution | Your domain controllers, from cid-userid-agent | 636 | Continuously, while that profile runs |
| SIEM export | The syslog host and port you configure, or your HTTPS webhook endpoint | As configured | On the export cycle |
None of these leaves your network. An appliance with no internet at all still binds to a domain controller and still exports to a collector.
Corporate proxy
A site proxy is supported on every path, but it has to be given to the appliance rather than inferred.
- During the setup wizard. The Connectivity step takes an HTTPS proxy URL and persists it; it is applied when you select Finish.
- During ISO provisioning. A proxy the installer was given is copied into Docker's own configuration before the first image pull, because Docker reads neither the package manager's configuration nor the shell environment. The fully unattended boot entry never asks for one, so on a proxy-only segment provisioning stops and you set the proxy from the console before rebooting.
- Afterwards. The same values live in the appliance's environment as
HTTP_PROXY,HTTPS_PROXYandNO_PROXY, documented in environment variables.
Put every destination that must not go through the proxy into NO_PROXY before you finish the
wizard: your LAN model endpoint, your domain controllers, and your SIEM collector. A LAN endpoint
routed through an internet proxy fails in a way that reads like a dead credential.
The first returns 401 when the path is open — reachable, and asking for credentials. A timeout is
a blocked path.
TLS-inspecting proxy
A proxy that re-signs outbound HTTPS with its own authority works, and it needs one thing the ordinary proxy does not: the appliance has to trust that authority, in both of the places that make outbound connections.
| What breaks without it | Why |
|---|---|
| The online update channel | The updater verifies the channel host's certificate before it fetches a manifest |
| Cloud model providers | The provider clients verify the vendor's certificate |
| The ISO install itself | Provisioning stops at the image pull, reporting that the certificate presented for the registry is not trusted |
Two places to install it, depending on when you are:
- Before the install. Installation media built for your site can carry the certificate; the
installer installs it at stage 0/8, before it contacts the registry. Otherwise place the
certificate in
/usr/local/share/ca-certificateson the machine, runupdate-ca-certificates, and reboot. - After the install. Select Settings → Certificates, paste the proxy's issuing CA under Outbound trust (TLS-inspecting networks) and select Trust this CA for outbound connections. The host adds it to the trust stores the gateway and the updater use. This is the outbound direction only and does not change what the appliance presents on 443.
Name resolution
The appliance uses the resolvers the network gives it. Two consequences are worth stating before anyone argues about whether DNS works.
- The installer repairs its own resolution, within limits. If your resolver cannot answer for the registry, the installer routes that one domain to public resolvers and makes the change permanent. If those are blocked too, it falls back to the address the media was built with, and removes that pin if it turns out not to work.
- A browser proving DNS works proves nothing about the appliance. Browsers resolve over DNS-over-HTTPS and keep working on a network whose ordinary DNS is broken.
Time
The appliance's clock has to be right for three independent reasons: TLS rejects a certificate that is not yet valid, licence expiry is a local clock comparison with no network time source to correct it, and an inspection deployment correlates its events with a third-party gateway's logs.
On an ISO-installed appliance the first-boot pass restarts the system time-synchronisation service and waits up to 20 seconds for it to converge before it contacts the registry, warning rather than stopping if it does not.
Warning
No time source is provisioned as part of appliance packaging. An air-gapped appliance with a drifting hardware clock can flip a valid licence to invalid, with nothing on the network to correct it. Point it at an internal time source.
The host clock is not synchronised
You might see: The host clock is not synchronised · clock is not synchronised — if the registry login fails on a certificate error, this is why · a valid licence is reported as expired · tokens are rejected as expired immediately after sign-in
Before you start
Checks
- 1
Read whether the host clock is disciplined by a time service
diagnostics · host.timeSynced = true
- 2
Verify the appliance can reach a time source at all
expected · The status reports the clock as synchronised, or names a server the network permits.
- 3
Check whether the skew has already aged the licence out
diagnostics · appliance.license.state = "active|trial"
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | no-time-source (warning) | Licence expiry, certificate validity, session-token lifetimes and every detection timestamp are decided by this clock. A drifting clock therefore expires licences early, rejects tokens that were just issued, and makes a current certificate look not-yet-valid — three failures that each look like something else. During an install it also breaks the registry login with a certificate error that reads like a network fault. Fix: Enable time synchronisation on the appliance host and point it at a time source this network permits. Restart the gateway container afterwards so anything it cached at boot is re-evaluated against the corrected clock. Verify: diagnostics · host.timeSynced = true |
| check 2 fails | ntp-blocked (warning) | A time client that cannot reach a server leaves the clock free-running, and the failure is silent: nothing on the appliance reports that time is wrong until something dated fails. Many sites block outbound time traffic and provide an internal server instead. Fix: Point the appliance at the site's own time server, or open the time protocol to a permitted external one. See Network prerequisites. Verify: expected · The clock reports as synchronised. |
| check 3 fails | licence-mis-aged (critical) | The skew has already pushed the licence out of its validity window, so the product blocks exactly as it would for a real expiry. Requesting a renewal at this point produces a licence that will look expired too. Fix: Correct the clock first, then re-read the licence state. Request a renewal only if the state is still expired once time is right. See The licence has expired. Verify: diagnostics · appliance.license.state = "active|trial" |
Proving the path is really open
"This network has full internet access" is usually said in good faith and is usually about a different path than the one the appliance uses. Each of the following passes routinely while an installation still fails, so test the exact thing the installer needs, on the appliance itself:
- A browser reaching the registry proves little. Browsers resolve names over DNS-over-HTTPS, so they keep working on a network whose ordinary DNS is broken — and the browser is usually on a different machine than the appliance.
- A successful
pingproves little. It tests ICMP. Image pulls are TCP on port 443, and a firewall that permits ICMP and blocks 443 is an ordinary configuration. - A working package install proves little. The Ubuntu mirrors are served over port 80, so package installs succeed on a network that blocks outbound 443 entirely.
The three tests that do prove it isolate name resolution, the TCP path and the authenticated pull, in that order. Run them from the appliance console:
They are not interchangeable because each one can pass while the next fails: resolution can succeed against a resolver that answers with a block page, the TCP path can open to a TLS-inspecting device that then presents its own certificate, and the login can still be refused by credentials that the media carries but the registry no longer honours.
The appliance cannot get out — one or more egress tests failed
You might see: egress tests failed · the appliance cannot reach the registry · ETIMEDOUT after 5000ms · proxy returned 407 Proxy Authentication Required
Before you start
Checks
- 1
Verify the appliance actually ran its outbound probes
diagnostics · appliance.egress
- 2
Read the name-resolution results for the hosts this appliance must reach
diagnostics · appliance.dns
- 3
Read whether an outbound proxy is configured on the appliance
diagnostics · appliance.proxy.configured = true
- 4
Read whether the release channel answered on the last attempt
diagnostics · appliance.update.channelReachable = true
- 5
Check whether a TLS-inspection device is presenting its own certificate to the appliance
expected · The issuer is a public certificate authority, not your organisation's inspection CA.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | probes-disabled (info) | The outbound probes are switched off for this deployment, so the snapshot carries no evidence either way and no egress finding can fire. This is a deliberate setting for a box that must make no outbound request at all. Fix: Re-enable the diagnostics egress probes on the gateway container and collect the snapshot again, or run the three tests by hand from the appliance host. The probes are the only outbound requests the appliance makes on its own. Verify: diagnostics · appliance.egress |
| check 2 fails | dns-broken (critical) | The appliance resolves with this network's resolver. A resolver that cannot answer for the provider or channel hosts fails every outbound call identically, and a browser on a laptop keeps working because it resolves over DNS-over-HTTPS. Nothing downstream of resolution can be diagnosed until this is fixed. Fix: Set a resolver that answers for the hosts in the DNS section of the snapshot, then collect the snapshot again. See Network prerequisites. Verify: diagnostics · appliance.dns |
| check 3 fails | proxy-not-configured (critical) | This site requires an outbound proxy and the appliance has none, so every outbound connection is attempted directly and times out. A timeout reads as a dead network rather than as a missing setting, which is why the network is reported as open. Fix: Configure the outbound proxy on the appliance and restart the gateway container so it re-reads the setting. Docker itself reads neither the shell environment nor apt's configuration, so a proxy set for the shell alone does not reach image pulls. Verify: diagnostics · appliance.proxy.configured = true |
| check 4 fails | https-path-blocked (critical) | Names resolve and the HTTPS path does not complete. DNS, a TCP connection on 443, and an authenticated HTTPS request fail independently: a firewall that permits ICMP and blocks 443 is ordinary, and Ubuntu's mirrors are served over port 80, so ping and apt both succeed while this fails. The three tests are not interchangeable. Fix: Allow direct outbound HTTPS from the appliance to the hosts the snapshot names as failing, or route them through the site proxy and configure it on the appliance. Read each failing target's detail line in the snapshot before changing a rule — a 407 is a proxy credential problem, a timeout is a blocked path, and a TLS error is inspection. Verify: diagnostics · appliance.update.channelReachable = true |
| check 5 fails | tls-inspection-untrusted (critical) | A middlebox terminates TLS and presents a certificate the appliance does not trust, so the connection fails at the handshake. The detail line reports a TLS error rather than a timeout, which is the only way to tell this apart from a blocked port. Fix: Install your site's CA certificate on the appliance so it trusts the inspection device, or exempt the appliance's outbound destinations from inspection. Both are legitimate; pick the one your security team already operates. Verify: expected · The failing egress targets report ok, or the issuer is now trusted by the appliance. |
Air-gapped operation
An appliance with no internet access is a supported deployment, not a degraded one.
- Installation — use the air-gap installer ISO or the OVA. Both carry the release images and download nothing.
- Setup — the first-boot wizard needs no network at all.
- Detection — every detection model is baked into its image at build time. PII, toxicity, jailbreak and injection detection, language detection, document parsing, redaction and the default OCR engine all run with no egress.
- Licensing — export the activation request to removable media, receive the signed
.lic, and upload it. No network is involved at either end. - Updates — obtain the signed
.cidupdbundle out of band and upload it. The appliance never phones home. - Chat — works only against a model the appliance can actually reach, which on an isolated network means a self-hosted or LAN endpoint.
Three things degrade offline and are worth knowing in advance: the highest-accuracy OCR engine downloads its model at first use unless the release you install baked it in, the red-team simulator needs a model of its own, and context summarisation posts to the OpenAI and Anthropic clouds at hard-coded addresses, so it cannot be pointed at a LAN model and fails on an isolated network.
Related
- Choose your install path — the deployment mode this matrix follows from.
- Requirements and sizing — the host the network is being opened to.
- Install from the ISO — where these egress rules are exercised for the first and only time.
- Services, ports and profiles — the generated per-container port and profile table.
Last updated on