Trust
Single-page view · as markdown
The trust store
Peios / Using Peios / Trust
Start with trust status to inspect the machine's trust store before changing it.
Programs that use the machine's TLS trust get their certificate authorities from trustd, the trust store. A program with its own private trust store is separate; see packages and private trust stores.
Choose your task #
| What you need to do | Start here |
|---|---|
| Check store health or inspect a certificate authority | Seeing what is in force, then the trust command reference |
| Add or distrust a certificate authority | Check who may change it, then follow Adding and distrusting certificates |
| Check that a change took effect | Verify the change; a recorded registry decision alone is not proof that consumers use it |
| Investigate degraded health | When the store is degraded |
Seeing what is in force #
Start with trust status to check the store's health and whether the
compatibility files are enabled:
$ trust status
generation 7
health ok
roots 121 in force
shipped 121
added 0
distrusted 0
compat GenerateLinuxTrustFiles = 1
rendered /system/retc/ssl/certs/ca-certificates.crt
rendered /system/retc/ssl/cert.pem
generation increases every time the store changes, so a program holding
a copy can tell whether it is current without fetching it again.
health is degraded when composition or compatibility-file rendering
failed — see
when the store is degraded.
Use trust list for the roots in force and trust show <fingerprint> for
one root's full fingerprint, source and purposes. trust list --distrusted
shows the registry's distrust decisions; it does not by itself prove that
trustd has applied them. After a change, verify the effective
store.
Who may change it #
Machine\System\Trust\Certificates and its subkeys, like any registry
key, are governed by their own security descriptor. By default SYSTEM and
Administrators may write; everybody may read. That descriptor is the
only gate: trust and reg write the
same values through the same check, so there is no second permission model
to keep in step with the first.
Reading is open to everyone, because the root set is public — in the default configuration it is sitting in a world-readable file.
Where trust policy comes from #
trustd composes that set from two inputs and serves the result.
| Input | Where it lives | Who changes it |
|---|---|---|
| Mozilla's root store | /usr/share/ca-certificates/mozilla.crt, shipped by the ca-certificates package | An upgrade of that package |
| Additions and distrusts | Machine\System\Trust\Certificates in the registry | An administrator, or the domain |
The split is the whole design. The roots are package data — a hundred
and twenty-odd certificates nobody on this machine decided anything about
— and package upgrades already do the right thing with them, including
removing a root Mozilla has withdrawn. The registry holds decisions:
each entry is something somebody chose, which is what keeps an audit of
Machine\System\Trust worth reading.
What programs actually read #
The store itself is a socket, /run/trustd/trust.sock. Peios-native
programs can ask it directly and subscribe to changes. A running program
must refresh its own trust state when it receives an update; socket
publication alone does not prove that every consumer stopped using a
cached certificate.
Almost nothing does that yet, so trustd also renders the store to the files the portable software ecosystem expects:
/etc/ssl/certs/ca-certificates.crt the bundle Go and most software read
/etc/ssl/cert.pem OpenSSL's default CAfile
/etc/ssl/certs/<hash>.0 OpenSSL's default CApath
Those files are a copy of an answer, not the answer. Editing one does
not change trustd's policy, and trustd overwrites it the next time anything
changes. Use trust or Security Policy to make a lasting change. The
GenerateLinuxTrustFiles value decides whether they exist at all — see
the compat files.
Installing the root-store package alone renders nothing; the image that runs programs using machine trust must provide the service that composes and renders it. Conversely, trustd being absent or stopped does not prove that previously rendered files are absent or current. Inspect the actual consumer paths and service health rather than inferring trust from the daemon's presence alone.
Composition is all or nothing #
If the shipped bundle cannot be read, yields implausibly few roots, or would leave no usable roots, composition fails before a new set is installed. The inspected trustd 0.1.6 source leaves the previous in-memory set and rendered files untouched in that case and reports degraded health. On a first start, there may be no previous in-memory set or rendered files to retain.
Rendering is a separate step. In that source, the socket's new set is installed before the compatibility files are written, and a rendering failure does not roll it back. The files can therefore disagree with the socket or with one another. Composition's all-or-nothing check is not a transaction across every trust consumer; see the rendering limits and degraded-store diagnosis. This is a pinned source finding, not verification of an installed image.
A single bad entry is different. An addition that is not a certificate, is not a CA, or has expired is skipped with a warning and everything else proceeds — one administrator's typo does not take the machine's trust down.
What is recorded #
Each time the set of roots in force changes, trustd writes one event per root that entered or left it:
| Event | When |
|---|---|
trustd.root.added | A root entered the set: an addition, a distrust lifted, or a root a bundle upgrade brought in |
trustd.root.distrusted | A distrust took a root out of the set |
trustd.root.removed | A root left the set for any other reason: its addition was withdrawn, or a bundle upgrade dropped it |
Each carries the certificate's SHA-256 fingerprint in
object.certificate.digest, its subject name in object.certificate.name,
and what it is trusted for in object.certificate.purposes, written in
kebab-case (ServerAuth becomes server-auth). Search by the digest: it
is the only one of the three that identifies a certificate.
The records say what changed, not who changed it. An addition or a
distrust is a registry write, recorded by the registry itself where the key
is audited, and a bundle upgrade is a package operation, recorded by peipkg.
The set trustd composes when it starts is not recorded, so a change made
while trustd was not running is seen only as part of that starting set.
The events are standard, so the emission policy can switch them off.
What trustd is not #
It holds no private key. Not the machine's, not anybody's. A service that holds keys is a signing oracle if it is ever compromised, and wants almost the opposite security posture from one whose socket is open to every program on the machine. Machine identity — the certificates and keys a machine presents when it is the server — is a separate concern, and will be a separate service.
It does not fetch anything. No root is downloaded on demand and no revocation list is retrieved, in this version. What the machine trusts changes when a package is upgraded or somebody writes to the registry, and at no other time.
Adding and distrusting certificates
Peios / Using Peios / Trust
Two things can be decided about this machine's trust, and both are values
under Machine\System\Trust\Certificates. Change them with trust on a
terminal, or Security Policy on the desktop (type security in the
launcher), which takes the same steps.
Before making a change, check trust status and trust list. You need
write access to the relevant registry key; by default SYSTEM and
Administrators may write, and everybody may read. See who may change
it.
In Security Policy #
Security Policy's Certificates sections show what trust status,
trust list and trust list --distrusted show, and change it.
- Trusted lists every certificate authority in force, those shipped with Peios and those added on this machine, with a search above them. Opening one shows who issued it, when it is valid, its key, serial number and SHA-256 fingerprint, and what it is trusted for. Export… saves it as a PEM file. Distrust… stops trusting it, and Remove… takes back one this machine added; both ask first, under their row.
- Add Certificate… opens a PEM or DER file and shows what is in it before anything is trusted. A file that holds several certificates, a certificate that isn't a CA, one that has expired, or one already trusted or distrusted is shown with the reason and can't be added. Otherwise give it a name and choose its purposes, then Add Certificate.
- Distrusted lists what is refused, with the reason it was given, and names each certificate where this machine has a copy. Trust Again… restores one after asking. Distrust by Fingerprint… refuses a certificate the machine doesn't have, by its whole fingerprint, or by a prefix of one it does.
- Settings shows whether trustd is up to date, with Reload for
those trustd lets reload it; turns the files under
/etc/ssl on and off; and opens the
permissions editor on
Machine\System\Trust(Changing Trust) and on trustd'sControlSecurity(Trust Service).
The window follows trustd, so a change made elsewhere, with trust or
reg, appears in it within a few seconds. What you may not change is
shown read-only, with the reason said once.
Trusting a certificate authority #
An internal CA, a test CA, a partner's CA:
$ trust add corp-ca /path/to/corp-root.pem
added corp-ca
CN=Corp Issuing CA,O=Example Ltd,C=GB
SHA-256 4f2a…c19b
The name is yours to choose — it names the decision, not the
certificate, so corp-ca is a better name than 4f2a. PEM or DER is
accepted; a file holding more than one certificate is refused, because a
bundle added under one name would be one decision covering several
authorities.
The certificate is checked before it is written: it must parse, it must be
a CA (basicConstraints), and it must not have expired. trustd checks it
again when it reads it — it never trusts the writer — but doing it here
means a mistake is a message rather than a line in a log.
To withdraw this named addition:
$ trust remove corp-ca
Purposes #
By default an addition is trusted for ServerAuth — validating a TLS
server. Say otherwise if you mean otherwise:
$ trust add corp-ca corp-root.pem --purposes ServerAuth,CodeSigning
The purpose is recorded with the addition and can filter socket queries.
Do not assume it limits the compatibility files. The inspected
trustd 0.1.6 renderer
writes every composed root to both PEM bundles and to the
hashed directory,
including an addition whose purposes omit ServerAuth. This differs from
the intended ServerAuth-only rendering
described in the source.
Do not use a non-ServerAuth purpose as an isolation boundary for a machine-wide addition on that implementation. The certificate's DER is preserved; this finding does not establish whether a particular TLS consumer accepts it, which also depends on the certificate and that consumer's validation rules. This is source inspection, not a live TLS test or a claim about every installed version.
Distrusting a certificate authority #
This is the one that has to work in a hurry, and it works on any certificate — one Mozilla shipped or one somebody added:
$ trust distrust 018e13f0772532cf --reason "withdrawn, see CA/B incident 2026-08"
distrusted 018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05
was: CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US
After trustd observes the decision and successfully composes and renders the new set, the root is absent from its socket set, the rendered bundles and the hashed directory. Withdrawal does not require a package update. Check the result below: a composition failure retains the old set, while a rendering failure can leave the socket and files different. Neither a registry write nor socket readback proves that every running application has stopped using a cached copy.
A certificate is named by its SHA-256 fingerprint — never by subject name, which is forgeable and reused. Any of these work:
018e13f0772532cf the prefix `trust list` prints
018E13F0…C1B05 upper case
01:8e:13:f0:… colon-separated, as other tools print it
/path/to/the-certificate.pem the file itself
Distrusting a certificate this machine does not have is legitimate and is recorded rather than refused — the entry sits there, and if that root ever arrives in a bundle upgrade or an addition, it is already refused.
To see what has been decided, and to undo it:
$ trust list --distrusted
018e13f0772532cf withdrawn, see CA/B incident 2026-08
1 distrusted
$ trust restore 018e13f0772532cf
restored 018e13f0772532cf809bd1b17281867283fc48c6e13be9c69812854a490c1b05
Verify the change #
A successful write records a decision. Check that trustd has applied it:
- Run
trust status. If health isdegraded, follow the recovery steps below. If entries were skipped, read the warnings in trustd's log; a skipped entry does not establish trust. - Run
trust listto inspect the effective set. For an addition, usetrust show <fingerprint>to check its full fingerprint, source and purposes.trust list --purpose ServerAuthfilters the socket reply; it does not inventory or constrain the compatibility files. See the purpose limitation. - For a distrust, check that the certificate is absent from
trust listand that the decision appears intrust list --distrusted. The latter reads the registry, so it is not enough on its own. Aftertrust restore, check the effective set again: removing a distrust does not supply a certificate that is no longer shipped or added.
Retest the affected application. Programs with private trust stores use
their own policy; a successful trust command is not an application TLS
test.
Doing it from the registry #
trust writes registry values and nothing else, so reg does the same job
and is subject to exactly the same access check:
Machine\System\Trust\Certificates\
Add\<name>\
Certificate REG_BINARY the certificate, DER
Purposes REG_MULTI_SZ default ServerAuth
Distrust\
<fingerprint> REG_SZ why, for whoever reads it later
Write Certificate last when creating an entry by hand. Until it
exists the entry is incomplete, and trustd deliberately skips incomplete
entries rather than acting on half of one.
This is also how a domain distributes trust: pushing a value to
Trust\Certificates\Add on every member is an ordinary policy write, not
a special mechanism.
Packages may offer trust, never take it #
A package must never widen what the whole machine trusts by being
installed — peipkg install quietly adding a CA is a supply-chain hole,
and every system that allowed it eventually closed it.
A package that comes with a CA ships a seed (/usr/share/regim/…),
inert until an image names it in [registry] autoapply or an
administrator applies it. Installing the package grants nothing.
A package shipping a trust store for its own private use — a browser, a language runtime — is unaffected by any of this. Those are ordinary files that only that program reads.
When the store is degraded #
$ trust status
health degraded — cannot read /usr/share/ca-certificates/mozilla.crt: …
degraded can report either a composition failure or a rendering failure.
They need different readback. In the inspected
trustd 0.1.6 refresh path,
a composition failure leaves the previous set and files untouched. A
rendering failure happens after the socket set has changed; it can leave
old, new or missing compatibility artifacts. Do not assume all consumers
still use one last-good store.
Use the error in trust status to choose the next step:
- For a missing or damaged bundle, check the installed
package
and repair the package problem. Do not edit the generated
/etc/sslfiles. - For a distrust list that would empty the store, review
trust list --distrustedand correct unintended decisions. Do not restore an intentionally distrusted CA just to clear the health warning. - For
render failed, preserve the operation and path in the error and investigate that filesystem failure. Compare the socket set with the files the affected program actually uses; therenderedpaths can be retained from the last successful render and are not a fresh file inventory after failure. Do not hand-edit the generated files or assume a successfultrust listproves they were updated. See compatibility rendering limits.
Once the cause is fixed, ask trustd to recompose:
$ trust reload
Reload requires permission to control trustd; Security Policy exposes its
ControlSecurity permissions under Settings → Trust Service. If reload
is refused, have an authorized administrator perform it.
The inspected daemon replies successfully to an authorized trust reload
even if recomposition or rendering failed. Run trust status again and
confirm health is ok, then verify the intended
change and retest the affected application. Until
recovery succeeds, do not assume a newly recorded addition or distrust is
in force for every consumer.
Skipped entries are different and are not a degraded state: trust status
counts them and the log names each one. Correct the named entry, then check
the effective set again.
The trust command
Peios / Using Peios / Trust
trust is the trust store's operator command. It reads the effective
store over trustd's socket and writes decisions to the registry. Start
with trust status for health, trust list for the roots in force, and
trust show <fingerprint> for one root's details.
The registry holds local additions and distrusts, not the shipped roots.
trust list --distrusted reads those decisions directly. Writes made with
trust and reg pass through the same access check. For changes and
verification, follow adding and distrusting
certificates.
Verbs #
| Command | Reads | Writes |
|---|---|---|
trust list [--purpose P] | socket | — |
trust list --distrusted | registry | — |
trust show <fingerprint> [--pem] | socket | — |
trust status | socket | — |
trust add <name> <file|-> [--purposes A,B] | the file | registry |
trust remove <name> | — | registry |
trust distrust <fingerprint|file> [--reason R] | socket, then | registry |
trust restore <fingerprint> | registry | registry |
trust reload | — | — (asks trustd to recompose) |
Listing #
$ trust list
018e13f0772532cf shipped CN=DigiCert TLS ECC P384 Root G5,O=DigiCert\, Inc.,C=US
0a81ec5a929777f1 shipped CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R3
4f2ab19c8e10dd77 added:corp-ca CN=Corp Issuing CA,O=Example Ltd,C=GB
121 root(s)
The first column is the first sixteen characters of the SHA-256 fingerprint, and every verb that takes a fingerprint accepts that prefix — what is printed can be pasted back in. An ambiguous prefix is refused rather than guessed at.
--purpose ServerAuth narrows the socket listing to roots carrying that
purpose. It is not an inventory of the compatibility files: the inspected
renderer does not filter them by purpose. See the source-qualified
limitation.
Showing one root #
$ trust show 4f2ab19c
subject CN=Corp Issuing CA,O=Example Ltd,C=GB
fingerprint 4f2ab19c8e10dd77…
source added
added as corp-ca
purposes ServerAuth
expires 2074003199
-----BEGIN CERTIFICATE-----
…
--pem prints the certificate alone, for piping into a file or another
tool.
Exit statuses #
| Status | Meaning |
|---|---|
0 | Done. |
2 | Asked about something that is not there — no root matches, no such addition, not distrusted. |
1 | It went wrong: the daemon is unreachable, the file is not a certificate, the registry refused the write. |
64 | The command line was not understood. |
The separation of 2 from 1 is what lets a script tell "this machine
does not trust that CA" from "I could not find out". A successful write
means the decision was recorded; verify the effective
store before treating
the change as applied. Likewise, a successful trust reload means the
request was accepted: the inspected daemon
returns success even when refresh left degraded health. Read trust status afterwards.
When a write is refused #
$ trust distrust 018e13f0772532cf
trust: not permitted to change Machine\System\Trust\Certificates\Distrust —
changing what this machine trusts is governed by that key's descriptor
trust has no privilege of its own to grant. Changing the machine's trust
is a registry write, and whether you may make it is decided by the key's
security descriptor — the same answer reg would get. Ask an administrator
with write access to make the intended change; editing a generated
certificate file is not a substitute.
trust reload is a service-control request, not a registry write. Its
permission is separate from permission to change trust decisions; Security
Policy exposes trustd's ControlSecurity under Settings → Trust Service.
Reloading through the service manager #
svctl --wait reload trustd and the service manager's Reload action send
SIGHUP to trustd. The daemon consumes it in its normal event loop and uses
the same recomposition path as trust reload, without replacing its main
process. Repeated pending signals may be coalesced into one refresh.
Check trust status for the resulting health. A service reload completing
does not prove that recomposition succeeded; on failure, trustd retains its
last rendered store and reports degraded health. Older daemon versions that
do not handle SIGHUP can terminate on service reload; use trust reload
directly until the updated daemon is installed.
The files under /etc/ssl
Peios / Using Peios / Trust
The machine's trust store is a socket. The files under /etc/ssl are a
compatibility rendering of it, for software that reads trust from a path —
which today is very nearly all software.
They are generated. A manual edit lasts only until the next change, and a
header at the top of the bundle says so. Use adding and
distrusting to change trust policy.
Use trust status to check whether these files are enabled and where they
were rendered.
Three artifacts, because the ecosystem does not agree #
| Path | Who reads it |
|---|---|
/etc/ssl/certs/ca-certificates.crt | Go probes this first; most software is configured with it |
/etc/ssl/cert.pem | OpenSSL's default CAfile on this system |
/etc/ssl/certs/<subject-hash>.0 | OpenSSL's default CApath |
The hashed directory is not decoration. OpenSSL looks a certificate up
there by a hash of its subject, so updating a PEM bundle alone does not
withdraw a root from that lookup path. Roots whose subject hashes collide
use successive suffixes, .0, .1 and so on.
The inspected trustd 0.1.6 renderer
builds a new certs directory, installs it, and then replaces cert.pem
separately. It first tries an atomic directory exchange. Its
fallback path
uses separate renames, which can temporarily leave certs absent; failure
after the first rename can leave it absent until repaired. There is no
single atomic switch covering the socket and all three file paths.
A rendering error can occur after some artifacts changed, and the socket set is updated before rendering starts. Follow degraded-store diagnosis rather than assuming a last-good store everywhere. These are pinned source limits, not live filesystem or installed-image verification. Also see the purpose-filtering limitation: that renderer includes all composed roots, regardless of their recorded purposes.
Turning them off #
Machine\System\Trust GenerateLinuxTrustFiles requests the compatibility
mode. These effects require successful rendering or removal:
| Value | Effect |
|---|---|
1 (default) | Rendered and kept current. |
0 | No files. Programs that read a path find nothing and fail with certificate errors. |
2 | Reserved. Treated as 1. |
0 is the machine whose entire software set asks the socket — an
appliance image, in practice, where you know what is installed. On a
general-purpose system it breaks curl, Python, Go binaries and anything
else that reads a bundle, and the breakage looks like a certificate
problem rather than a configuration one, so set it deliberately.
Switching to 0 asks trustd to remove the rendered files. Successful
removal leaves the directory itself empty. The inspected renderer removes
cert.pem and then the contents of certs in separate operations; a
failure can leave some files behind. Check health and the actual paths
before treating compatibility trust as disabled. A saved 0 or a socket
reply alone does not prove removal succeeded.
$ reg set Machine/System/Trust GenerateLinuxTrustFiles dword:0
$ trust status
compat GenerateLinuxTrustFiles = 0 (no files; the socket is the only store)
The socket keeps serving throughout — trust list still answers, and so
does any program that speaks to trustd directly.
In Security Policy, the Certificate Files switch under Settings writes the same value. Turning it off asks first because programs that rely on these paths can lose their trust store. Allow trustd to apply the change, then verify health and the affected application.
To restore compatibility files after disabling them:
$ reg set Machine/System/Trust GenerateLinuxTrustFiles dword:1
$ trust status
Confirm GenerateLinuxTrustFiles = 1, the rendered paths, and health of
ok, then retry the affected program. If the store is degraded, follow
the recovery steps
before relying on the rendered trust.
Why a copy, when name resolution gets a pointer #
/etc/resolv.conf on Peios is a constant file naming the resolver, and
nothing is ever generated into it. Trust cannot work that way: there is no
"ask over here" a certificate bundle can express. A file that lists
authorities has to be the list.
That is the whole difference between the two, and the reason this one carries a knob and a warning while the other does not.