Skip to main content
CID222 Docs

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

PortProtocolNeeded for
80TCPThe 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.
443TCP, and UDP where the front supports itThe dashboard, the gateway API and the model proxy. UDP carries HTTP/3; blocking it costs nothing but the newer transport.
1344TCPThe 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.
8443TCPThe explicit HTTPS forward proxy on cid-inline-proxy, which client browsers connect to. Only in an inline-proxy deployment.
8084TCPTransparent 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.
8090TCPActive 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 to 127.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:

    ss -ltn '( sport = :80 or sport = :443 or sport = :1344 or sport = :3000 or sport = :8443 )'

    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.

PurposeDestinationPortWhen
Release image pull during ISO provisioningapi.cid222.live443Installation only, on the online installer ISO. Nothing else is required during the install
Online update channelThe channel host in SYSTEM_UPDATE_CHANNEL_URL443Only when an operator presses check or install. The appliance never polls
Name resolutionYour resolvers53Continuous
Time synchronisationYour time source123Continuous

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.

PurposeDestinationPortWhen
Cloud model providersThe hosts the provider clients dial — for example api.openai.com and api.anthropic.com443Every chat request that reaches a provider
LAN or self-hosted modelYour own model endpointAs your endpoint servesEvery chat request that reaches it. No internet egress
Context summarisationThe OpenAI and Anthropic clouds, at fixed addresses443When 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:

# Reaches the provider you configured. A 401 or 403 is a pass — it proves the path.
curl -sS -o /dev/null -w '%{http_code}\n' https://api.openai.com/v1/models
# A LAN model instead:
curl -sS -o /dev/null -w '%{http_code}\n' http://<model-host>:<port>/v1/models

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.

DirectionEndpointPortNotes
Secure web gateway → applianceThe ICAP listener1344Publish it off loopback with ICAP_PUBLISH, then firewall the port to the gateway's address
Secure web gateway → applianceThe HTTP callout endpoint443For a gateway that calls out over HTTP rather than ICAP
Appliance → anywhereNothing 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.

# On the appliance, and again on the gateway — the two must agree.
timedatectl status
# From the gateway's own shell, prove it can open the listener:
timeout 5 bash -c '</dev/tcp/<appliance-ip>/1344' && echo "1344 open" || echo "1344 blocked"

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.

PurposeDestinationPortWhen
The sites being re-proxiedThe model vendors' web front ends and APIs your policy covers443Every intercepted request
Proxy auto-configuration deliveryInbound from clients, not egress80 or 443Every client, at policy refresh
# From the appliance. A 403 is a pass — anti-bot, not a blocked path.
curl -sS -o /dev/null -w '%{http_code}\n' https://chat.openai.com/ https://claude.ai/
# And confirm the names resolve to real addresses rather than a block page:
dig +short chat.openai.com claude.ai

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

PurposeDestinationPortWhen
Active Directory or LDAP bind, sync and just-in-time provisioningYour domain controllers636 for LDAPS, 389 for plain LDAPOn sign-in, on sync, and on test
Active Directory user attributionYour domain controllers, from cid-userid-agent636Continuously, while that profile runs
SIEM exportThe syslog host and port you configure, or your HTTPS webhook endpointAs configuredOn 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_PROXY and NO_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.

# From the appliance, with the proxy in force. Both should answer.
https_proxy=<proxy-url> curl -sS -o /dev/null -w '%{http_code}\n' https://api.cid222.live/v2/
curl -sS -o /dev/null -w '%{http_code}\n' http://<model-host>:<port>/v1/models

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 itWhy
The online update channelThe updater verifies the channel host's certificate before it fetches a manifest
Cloud model providersThe provider clients verify the vendor's certificate
The ISO install itselfProvisioning 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-certificates on the machine, run update-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.
# Proves trust, not just reachability: a TLS error here is the missing CA.
curl -sS -o /dev/null -w '%{http_code}\n' https://api.cid222.live/v2/

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.

Runbookinstall

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. 1

    Read whether the host clock is disciplined by a time service

    diagnostics · host.timeSynced = true

  2. 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. 3

    Check whether the skew has already aged the licence out

    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:

  1. 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.
  2. A successful ping proves little. It tests ICMP. Image pulls are TCP on port 443, and a firewall that permits ICMP and blocks 443 is an ordinary configuration.
  3. 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:

# 1. Name resolution, from the appliance's own resolver
nslookup <registry-host>
 
# 2. The TCP path on 443, without DNS in the way
timeout 5 bash -c '</dev/tcp/<registry-ip>/443' && echo "443 open" || echo "443 blocked"
 
# 3. The authenticated pull path, end to end
sudo bash -c 'set -a; . /opt/cid/.env; set +a; printf "%s" "$REGISTRY_PULL_PASSWORD" \
  | docker login "$REGISTRY_PULL_HOST" -u "$REGISTRY_PULL_USER" --password-stdin'

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.

Runbookinstall

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. 1

    Verify the appliance actually ran its outbound probes

    diagnostics · appliance.egress

  2. 2

    Read the name-resolution results for the hosts this appliance must reach

    diagnostics · appliance.dns

  3. 3

    Read whether an outbound proxy is configured on the appliance

    diagnostics · appliance.proxy.configured = true

  4. 4

    Read whether the release channel answered on the last attempt

    diagnostics · appliance.update.channelReachable = true

  5. 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.

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 .cidupd bundle 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.

Last updated on