Skip to main content
CID222 Docs

Diagnostic snapshot and support bundle

What the appliance reports about itself, key by key, which parts need the host agent, and how to send a support bundle.

  • Version: 0.4
  • Role: admin_user, viewer
  • Type: reference

The diagnostic snapshot is one typed JSON document describing this appliance: the machine it runs on, the state of every container, the health of every service it calls, the datastores, the licence, the certificate, what the network lets out, the security posture, and a list of findings evaluated over all of it. Every runbook check of the diagnostics kind reads a key from this document.

Endpoints

CallWhat it returns
GET /admin/diagnostics/snapshotThe snapshot. Served from cache while it is under 60 seconds old.
POST /admin/diagnostics/refreshDrops the cache, asks the host agent for a fresh pass, and collects again.
GET /admin/diagnostics/bundleThe snapshot, the effective configuration with secrets redacted, and the host agent's last status documents.

Both GET calls need the admin_user role. The viewer role may read them and is refused the refresh with READ_ONLY_ROLE, like every other mutating call.

Warning

A refresh returns before the host agent's next pass completes. The agent polls every few seconds and runs a full pass every five minutes, so the snapshot you get back usually still carries the previous host numbers. Compare the collection timestamp to see the new ones land.

What the snapshot contains

Top level

KeyMeaning
versionSchema version of the snapshot document, independent of the product version.
generatedAtWhen the snapshot was collected. A cached snapshot is still valid; you decide whether it is stale.
hostFacts about the machine, or null when the host agent has never reported.
containersOne entry per container in the deployment, running or not.
servicesApplication-level health of every service the gateway calls.
datastoresPostgreSQL and Redis.
applianceVersion, setup state, licence, updates, TLS, proxy, DNS and egress.
postureSecurity-relevant settings, read as the code that enforces them reads them.
findingsRules that fired, each naming a runbook.

host

Null when the host agent has never run or its output cannot be read. The gateway container cannot see the machine by itself, so this section is a file the agent writes and the gateway reads.

KeyMeaning
hypervisorNormalised platform: vmware, proxmox, kvm, hyperv, none or unknown. none means bare metal was positively detected; unknown means detection failed and no conclusion may be drawn.
hypervisorRawThe raw detector and hardware strings, kept so a mis-normalisation is diagnosable.
cpuCount, cpuModelLogical CPUs and the model string.
memTotalMbTotal RAM. Compared against the licensed package's minimum by the sizing rule.
memFreeMbAvailable RAM — the number that predicts the next out-of-memory kill, not free RAM.
swapTotalMbTotal swap. Zero is normal and is not a finding on its own.
disksOne entry per filesystem that matters, each with its mount point, total and free space.
kernel, dockerVersionKernel release and Docker Engine version.
timeSyncedWhether the clock is disciplined. False is a finding: licence verification, certificate validation and the update channel all fail confusingly on a skewed clock.
gpuWhether an adapter is present and its name. A GPU is recommended for document and image work and never required.

Note

A default Proxmox guest is indistinguishable from any other QEMU guest, so it is reported as kvm unless an operator has filled in the virtual machine's own hardware identity strings. hypervisorRaw always carries what was actually read.

containers

One entry per container, whether running or not. A service that the release declares and that is absent here is itself the finding.

KeyMeaning
name, serviceThe container name, and the service name runbooks and findings use.
staterunning, restarting, exited, created, paused, dead, removing or unknown.
restartsRestart count since creation. A sustained climb is a crash loop, not recovery from one bad start.
oomKilledWhether the last termination was an out-of-memory kill. The single most useful bit on an undersized machine.
memUsageMb, memLimitMbCurrent usage, and the declared limit or null where the service declares none.
imageDigestThe resolved image digest, compared against the release manifest to detect a half-applied update. Null for a locally built image.
healthThe container healthcheck verdict. none means the service declares no healthcheck, which is not a fault.

services

Keyed by service name. This is each service's own health endpoint, not the container healthcheck — a container can be running while its model failed to load.

KeyMeaning
reachableThe health endpoint answered at all. False is a network or container problem, not a model problem.
healthyThe service reported itself healthy. Reachable and unhealthy is the interesting case, and usually means a model that did not load.
latencyMsRound-trip time of the health call, including the timeout value when it timed out. Every row is measured with the same three-second cap, so the numbers are comparable.
versionThe version the service reports, where it reports one.
errorFailure detail: the connection error, the HTTP status, or the body's own message.

A service that is not deployed is absent from this map rather than reported unhealthy — the opt-in services are simply not there when they are not configured. See the health matrix for what each one does and what breaks while it is down.

datastores

KeyMeaning
postgres.reachableWhether the gateway can reach the database.
postgres.migrationsPendingMigration names present in the compiled chain and absent from the database. Empty means the schema is current; non-empty on a booted appliance means the boot-time run failed.
postgres.dbSizeMbOn-disk size of the CID database.
redis.reachableWhether the gateway can reach Redis.
redis.usedMemoryMbMemory Redis reports in use.

appliance

KeyMeaning
versionThe installed product version. A placeholder value means the box cannot say what it runs.
channelThe update channel it is pinned to.
setupCompleteFalse while the first-boot wizard is unfinished; the setup guard then answers 423 on the locked routes.
deploymentModeappliance, docker, development or unknown. Several findings apply only to an appliance.
license.stateThe verification result for the installed licence.
license.tier, license.expiresAtThe licensed tier, and the expiry or null for a perpetual licence.
license.installationIdThe id the licence is bound to. Null is itself a known field issue — no licence can be issued for a box that reports no id.
license.trustAnchorPresentWhether the licence-signing public key exists on the host. Without it no licence can verify and every upload fails.
license.requireLicenseThe effective value of CID_REQUIRE_LICENSE. True with an unverifiable licence is the 402 failure.
update.lastCheckWhen the channel was last polled successfully, or null for never.
update.pendingIntentThe update or host-repair intent outstanding. One stuck in the queued state means the host updater is not picking work up.
update.channelReachableWhether the channel answered on the last attempt. Always false, and expected, on an air-gapped appliance.
tls.modeWhich certificate is served: one from the appliance CA, an uploaded one, a bootstrap self-signed one, or none.
tls.expiresAt, tls.daysLeftExpiry, and whole days remaining — negative when already expired.
tls.servedByWhat actually holds 443 right now. Nothing listening is the other half of the certificate runbook.
tls.caPresentWhether the appliance root CA exists, so clients can be made to trust the appliance.
proxy.configured, proxy.urlWhether an outbound proxy is configured, and its URL with credentials removed.
dnsOne entry per hostname the appliance must resolve, with the result.
egressOutbound probes, each with its target, verdict and detail.

Danger

The three egress probes are not interchangeable. Name resolution, a TCP connection on 443, and an authenticated HTTPS request through the configured proxy fail independently, and treating one as evidence of another is what makes "the network is open" wrong in good faith. Read each target's own detail line.

These probes are the only outbound requests the appliance makes on its own; it otherwise never phones home. They are bounded and can be switched off entirely, in which case the DNS and egress arrays come back empty and no egress finding fires:

# on the gateway container — disables every outbound probe
CID_DIAGNOSTICS_EGRESS=0

posture

Facts read from the effective configuration. The judgement lives in the findings, not here.

KeyMeaning
dbSynchronizeThe effective DB_SYNCHRONIZE. True outside development deletes the migrations' own indexes on every boot.
jwtSecretPlaceholderTrue when JWT_SECRET is a known placeholder or unset. Startup refuses known placeholders, so true here usually means unset — and sessions that die at every restart.
corsOriginsSetWhether CORS_ALLOWED_ORIGINS is set. False is correct for a single-origin appliance and broken when the dashboard or the extension lives elsewhere.
inspectionSourceIpModeHow the ICAP listener admits callers. An explicit allowlist replaces the private-range default rather than extending it.
resumableUploadPolicyThe effective CID_RESUMABLE_UPLOAD_POLICY. Chunked uploads cannot be inspected; block refuses them and is the safe default.
seedSampleAccountsTrue when the seeded sample tenants and their published passwords still exist. On a customer appliance this is a finding.

findings

Each entry carries an id, a severity, one sentence in the operator's language, the id of the runbook that resolves it, and the snapshot values the rule fired on.

FindingSeverityFires whenRunbook
license-trust-anchor-missingcritical or warningNo licence-signing public key on the host. Critical when licence enforcement is on.license-trust-anchor-missing
license-expired-or-402critical or warningThe licence is expired or invalid, or is inside its grace or expiry window.license-expired-or-402
setup-state-dir-not-writablecriticalThe gateway cannot write the state directory setup, licensing and updates all persist into.setup-state-dir-not-writable
providers-catalog-emptycriticalNo provider rows exist, so chat cannot resolve a model.providers-catalog-empty
update-version-unknownwarningThe appliance cannot say which release it runs.update-version-unknown
update-channel-unreachablewarningThe channel host did not answer. Expected on an air-gapped appliance.update-channel-unreachable
tls-cert-expiringwarning or criticalThe certificate expires within the 30-day renewal window appliance/tls/ensure-tls.sh renews at, or has expired.tls-cert-expiring
tls-443-not-servedcriticalNothing holds port 443.tls-443-not-served
service-unhealthy-<service>critical or warningOne service is unreachable, or answers and reports itself unhealthy. One finding per service.service-unhealthy
container-restartingcriticalA container has restarted more than three times.container-restarting
container-oomcriticalA container's last termination was an out-of-memory kill.container-oom
ram-below-package-minimumcriticalTotal RAM is below the licensed package's minimum, with 5% slack.ram-below-package-minimum
disk-lowwarning or criticalA filesystem of at least 2 GB is under 10% free, or under 3%.disk-low
time-not-syncedwarningThe host clock is not disciplined.time-not-synced
db-synchronize-oncriticalSchema auto-synchronise is enabled.db-synchronize-on
jwt-placeholdercriticalThe token signing secret is a known placeholder.jwt-placeholder
egress-blockedwarningAt least one outbound probe failed.egress-blocked
migrations-pendingcriticalCompiled migrations are not recorded in the database.migrations-pending
datastore-unreachablecritical or warningPostgreSQL is unreachable, or Redis is.datastore-unreachable
sample-accounts-seededinfoThe seeded sample tenants and their published passwords still exist.sample-accounts-seeded

A rule fires on a value and never on the absence of one, so a deployment that cannot measure something reports nothing about it rather than a critical it cannot act on.

What needs the host agent, and what does not

SectionCompose deploymentAppliance
version, generatedAt, posture, datastores, servicesAlwaysAlways
containersYes, when the host agent container is runningYes
hostNull, with the reason stated. The host probe is off by default outside an applianceYes
appliance.version, channel, updatePartial — the version falls back to the build-time valueYes
appliance.licenseThe trust-anchor bit is always answerable; the state reads as disabled without an anchorYes
appliance.tlsUnavailable, and nothing is reported as serving 443Yes
appliance.dns, egressYes, unless the probes are switched offYes

The host agent measures the machine and writes two files under the shared state directory; the gateway reads them. A file is a one-way channel: the gateway learns a host fact without gaining the ability to run a host command, which is why the Docker socket is not mounted into the internet-facing API.

The agent runs a full pass every five minutes, and immediately when a refresh is requested. Past thirty minutes — six missed passes — both readers drop the data and report how old it was.

The support bundle

The bundle is the snapshot plus the effective configuration and the host agent's own status documents. Redaction works on the key, not the value: a secret becomes a redaction marker, an unset secret is marked as empty because "unset" is itself a diagnosis, and a key that matches neither the secret list nor the allowlist is marked unclassified rather than emitted — a token added next year is safe the day it appears.

To send one to support:

  1. Reproduce the problem, then call GET /admin/diagnostics/bundle and save the response.
  2. Note the installation id and the appliance version from Settings → License and Settings → System Updates.
  3. Say what you changed between the working state and now. The bundle shows the current state; it cannot show the previous one.

Warning

Collect the bundle before you start changing things. A bundle taken after three attempted fixes describes a state nobody has diagnosed, including you.

Last updated on

On this page

Download PDF