Certificates
Give the appliance an HTTPS certificate your machines already trust, distribute its root CA, and trust a TLS-inspecting proxy on the way out.
- Version: 0.4
- Role: superadmin, admin_user
- Type: task
Settings → Certificates governs one thing: what this appliance presents on https:// — for the
dashboard, the API and the LLM proxy. You either let the appliance sign its own certificate from a
root CA you distribute once, or you install a certificate from a CA your machines already trust.
The same page holds the outbound half: the CA to trust when your network re-signs HTTPS on the way
out.
What do I need?
- Licence
- Any
- Role
superadminadmin_user
Prerequisites
- The appliance has completed the first-boot setup wizard. Until it does, every /admin route answers 423 SETUP_REQUIRED.
- You sign in as superadmin. The dashboard lists this pane only for superadmin and for the read-only viewer role, although the API accepts admin_user.
- Every DNS name your users will reach the appliance by already resolves to it — see Naming, DNS and TLS.
- For your own certificate: a PEM leaf certificate, its intermediates, and either the matching unencrypted private key or a pending CSR from this page.
- For outbound trust: the issuing CA certificate of your TLS-inspecting proxy, as PEM.
/assets/screenshots/settings-certificates@0.4.pngThree trust anchors, one page
The page says this itself under Which certificate do I need?, and it is the distinction that saves the most time:
- This page — the appliance's HTTPS certificate. Needed by every browser opening the dashboard, by the browser extension, and by Claude Code and API clients pointed at the box.
- Browser Inspection — the inline proxy's root CA. Needed only on devices whose traffic is routed through the inline proxy.
- Endpoint agent — its own per-device CA. Generated and installed by the agent; nothing to download here.
The Mode field names which of five states the appliance is in.
| Mode | What the page shows | What it means |
|---|---|---|
appliance-ca | Issued by this appliance's own CA | The default for a new install. A box-local root CA, valid ten years, signs a leaf valid 825 days. |
custom | Customer certificate | Your own certificate, uploaded with a key or issued against a CSR from this page. |
legacy-self-signed | Self-signed (installed by the original setup) | The bare self-signed leaf from the 0.3.48 installer. Every browser warns, on every device. |
none | No certificate yet | A status file exists and names no certificate. |
unavailable | Not managed on this deployment | No host reconciler has reported. TLS is terminated by something in front of the API, and the rest of the page is hidden. |
Only the leaf changes when you rename or renew. The root CA is preserved, so trust you have already distributed survives both.
Set the names the appliance is reached by
Open the certificate settings
Select Settings → Certificates.
The page shows Mode, Subject, Issuer, Valid until with the days remaining, Names (SAN), Fingerprint (SHA-256) and Measured — how long ago the host last reported. It refreshes every ten seconds.
Enter the extra names
In Names, comma-separated, list every hostname and IPv4 address the appliance is reached
by, besides its own address and hostname — for example
app.cid.example.com, api.cid.example.com.
The helper line under the field shows Wanted: — the SAN set the host will issue for, including the names it derives itself. Each entry must be a hostname or an IPv4 address, at most 253 characters, and there is a limit of 32 names.
Reissue the leaf
Select Save names and reissue.
A toast reports Names saved — the certificate is being reissued, and the page shows Applying on the host: ensure until the host reconciler finishes. Names (SAN) then matches Wanted:.
Note
One host reconcile runs at a time. A second change while one is pending is refused with a host reconcile is already pending (tls/ensure); wait for it to finish.
Give the appliance a certificate
Use this when you can distribute one root certificate to your fleet — by Group Policy, MDM or by hand — and never think about it again.
Switch to the appliance CA
Select Use the appliance CA for HTTPS.
The button appears only when the mode is not already appliance-ca. A toast reports
Switching to the appliance CA, and the mode changes once the host reconcile completes.
Download the root certificate
Under Appliance root CA, select cid222-appliance-root.crt.
The download needs no sign-in, so a machine that does not yet trust the dashboard can fetch
it — the page prints the curl command for exactly that case. The same certificate is
offered in PEM form beside it.
Check the fingerprint before you distribute it
Compare the downloaded file's SHA-256 fingerprint with CA fingerprint (SHA-256) on the page.
The page labels this step itself: verify this before trusting a downloaded file.
Install it as a trusted root on your machines
Distribute the file to the machine trust stores of every device that opens the dashboard, runs the browser extension, or points Claude Code and API clients at the appliance.
Browsers stop warning. Because only the leaf is reissued on a rename or renewal, this is a one-time distribution.
Warning
A custom certificate never renews itself. Thirty days out the page shows
Certificate expires in N days with the note Renew it with your PKI and upload the new
certificate. Past expiry the appliance keeps serving the expired certificate until you upload a
replacement or switch back to the appliance CA.
Trust a TLS-inspecting proxy on the way out
If your network re-signs HTTPS with its own CA, the appliance cannot reach the LLM providers or the update channel until it trusts that CA.
Paste the proxy's issuing CA
Under Outbound trust (TLS-inspecting networks), paste the proxy's issuing CA certificate in PEM form.
Paste the issuing CA, not a leaf: a certificate without CA:TRUE is refused.
Install it
Select Trust this CA for outbound connections.
A toast reports Site CA accepted: with the certificate's subject. The host adds the CA to its trust store and recreates the gateway container with that bundle, so outbound HTTPS from the appliance validates against it.
Fields reference
Values the status block shows, from GET /admin/certificates/status.
| Field on the page | Response field | What it holds |
|---|---|---|
| Mode | mode | One of the five states above. |
| Subject | subject | Subject of the certificate currently served. |
| Issuer | issuer | Who signed it. |
| Valid until | not_after, days_left | Leaf expiry, and the days remaining. |
| Names (SAN) | sans | The SANs the served leaf actually carries. |
| Fingerprint (SHA-256) | fingerprint_sha256 | SHA-256 over the leaf, colon-separated hex. |
| Measured | measured_at | When the host last wrote its status, as a relative time. |
| CA subject, CA valid until, CA fingerprint (SHA-256) | ca_subject, ca_not_after, ca_fingerprint_sha256 | The appliance root CA. |
| Wanted: | wanted_sans | The SAN set the host will issue for on the next reconcile. |
| — | names | The operator-set extra names, as saved. |
| Applying on the host | pending_intent | The reconcile in flight, and its action. |
| — | csr_pending | True only when a request was asked for and the file exists on the host. |
No private key is ever readable through this page or its API.
Verify
-
Settings → Certificates shows the Mode you chose, an Applying on the host notice that has cleared, and Names (SAN) equal to Wanted:.
-
Valid until is in the future and Measured is recent — a stale Measured means the host reconciler is not running, whatever the rest of the page says.
-
Open the dashboard from a machine that trusts your CA, over the appliance's own name. The browser shows no certificate warning.
-
From an untrusted machine, fetch the root certificate anonymously and confirm it downloads as a certificate rather than an HTML page:
The fingerprint matches CA fingerprint (SHA-256) on the page.
Note
Certificate changes write no row to Event logs — the module logs to the container log only.
The page's own status, and the last_apply record the host writes back, are the audit trail
available to you.
If it fails
The appliance is not reachable on 443, or the certificate has expired:
The certificate has expired, or nothing is serving 443
You might see: ERR_CERT_DATE_INVALID · NET::ERR_CERT_AUTHORITY_INVALID · connection refused on 443 · the dashboard cannot be reached over HTTPS
Before you start
Checks
- 1
Verify something is actually holding port 443 on the appliance host
diagnostics · appliance.tls.servedBy
- 2
Verify the appliance has a certificate to serve
diagnostics · appliance.tls.mode = "none"
- 3
Read how many days the served certificate has left
diagnostics · appliance.tls.daysLeft = 0
- 4
Check whether the certificate is inside the renewal window — 30 days, the `CID_TLS_RENEW_DAYS` default `appliance/tls/ensure-tls.sh` renews an appliance-issued leaf at and warns about a customer certificate at
diagnostics · appliance.tls.daysLeft = 30
- 5
Verify the appliance root CA is present, so clients can be made to trust an appliance-issued certificate
diagnostics · appliance.tls.caPresent = true
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | nothing-on-443 (critical) | No process holds 443, so the client's connection is refused before any TLS handshake begins. The browser reports a connection error rather than a certificate error, which is why this does not look like a TLS problem at first. It follows a failed certificate rollout: the front-end refuses to start with an unusable certificate rather than serving a broken one. Fix: Run the host repair from Settings → System Updates, which re-establishes the HTTPS front end and re-applies the appliance's certificate intent. Then reload the dashboard. If repair reports a certificate problem, resolve that first — the front end will keep refusing to start until the certificate it is told to serve is valid. Verify: diagnostics · appliance.tls.servedBy If that does not apply: Attach the support bundle, which carries the host HTTPS inventory. |
| check 2 fails | no-certificate (critical) | The appliance has no certificate configured at all — neither one issued by its own CA nor an uploaded one. Nothing can be served on 443 until it does. Fix: Issue a certificate from the appliance CA under Settings → Certificates, or upload one your organisation's CA signed against a CSR generated there. See Naming, DNS and TLS. Verify: diagnostics · appliance.tls.mode = "none" |
| check 3 fails | certificate-expired (critical) | The served certificate is past its expiry. Browsers refuse the connection outright, and clients that were configured to trust the appliance — the inline proxy's own callers, the browser extension, the endpoint agent — fail closed rather than degrading. Fix: Issue a replacement now. An appliance-CA certificate is reissued in place from Settings → Certificates; an externally signed one needs a fresh CSR, signature and upload. Check the host clock before blaming the certificate: a skewed clock makes a valid certificate look expired. Verify: diagnostics · appliance.tls.daysLeft = 0 |
| check 4 fails | certificate-expiring-soon (warning) | The certificate is still valid but inside the renewal window. Renewing now avoids an outage that would otherwise arrive without warning, and gives time for an external CA to turn a CSR around. Fix: Renew the certificate before it expires, following Naming, DNS and TLS. If the certificate is externally signed, start now — the signing turnaround, not the appliance, is the long pole. Verify: diagnostics · appliance.tls.daysLeft = 30 |
| check 5 fails | ca-missing (warning) | The appliance root CA is absent, so clients cannot be made to trust an appliance-issued certificate and the public root-CA download has nothing to serve. Every client then reports an untrusted-authority error even when the certificate itself is current. Fix: Re-create the appliance CA under Settings → Certificates, redistribute the root certificate from the appliance's public download path, and reissue the server certificate. Note that reissuing invalidates trust that clients had already established, so plan the redistribution before you reissue. Verify: diagnostics · appliance.tls.caPresent = true |
Everything the gateway refuses surfaces verbatim in a red toast. The ones worth knowing:
- a host reconcile is already pending (tls/install); wait for it to finish → one reconcile runs at a time. Wait for Applying on the host to clear, then repeat the action. Note that the files you submitted are already staged on the host, so you do not need to re-paste them for the validation to have happened — but the action itself did not queue.
- cert is a CA certificate; upload the server (leaf) certificate, with the CA in chain → you pasted your CA into Certificate (PEM). It belongs in Chain.
- chain link 1 (…) did not issue … — order the chain leaf-side first → the intermediates are in the wrong order. The certificate that signed your leaf comes first.
- key is not a PEM private key (encrypted keys are not accepted — decrypt it first) → the key is passphrase-protected.
- the private key does not match the certificate → the two halves are from different issuances.
- no key uploaded and no CSR is pending — generate a CSR first, or upload the key with the certificate → you left the key box empty without a pending request on this appliance.
- the certificate has no subjectAltName; browsers reject certificates without one → the CA issued on the common name alone. Re-issue with SANs.
- this certificate is not a CA (basicConstraints CA:TRUE); upload the proxy's issuing CA, not a leaf → outbound trust was given a server certificate.
- not a hostname or IPv4 address: … or at most 32 names → the names field. Only hostnames and IPv4 addresses are accepted, and no more than 32 of them.
- no CSR is pending — generate one first (404) → the request was consumed by an install, or never generated.
- this appliance has no root CA yet (404 on the download) → the host has not created the CA. Switch to the appliance CA to make it.
Next steps
- Naming, DNS and TLS — the names and records to have in place before you issue anything.
- SIEM and webhook integrations — if outbound trust was the blocker, this is the next outbound path to check.
- Settings — the other panes, and which role reaches them.
Last updated on