Trust store workflow
First contact with a secured server fails until you trust its certificate. Run trust, then re-run your command. The CLI prints the exact follow-up needed.
The CLI's trust store is a directory of accepted server
certificates, identified by SHA-1 fingerprint (hex pairs
joined by :). Most secured OPC UA servers will be rejected on
first connect — their cert isn't in the store yet. This page is
the canonical workflow to get past that.
Trust store has to be opted in. The CLI does not install a default trust store on its own. Pass
--trust-store=<path>(or--trust-policy=...) on the command line; only then will the underlyingFileTrustStoreapply its default path resolution (~/.opcua/on POSIX,%APPDATA%\opcua\on Windows).
The path
1. Connect with security on → Fails: UntrustedCertificateException
2. CLI prints follow-up commands → "opcua-cli trust <endpoint>"
3. Run trust → Server certificate stored
4. Re-run the original command → Succeeds
The CLI puts the follow-up commands directly in the error output — you don't memorise the sequence.
In action
Step 1 — first connect fails
$ opcua-cli browse opc.tcp://plc.local:4840 \
-s Basic256Sha256 -m SignAndEncrypt \
--cert=/etc/opcua/client.pem --key=/etc/opcua/client.key
Error: Server certificate not trusted.
Fingerprint: a1b2c3d4e5f6...
To trust this certificate, run:
opcua-cli trust opc.tcp://plc.local:4840
To list trusted certificates:
opcua-cli trust:list
To skip trust validation for this command:
opcua-cli browse ... --no-trust-policy
Exit code: 1. The CLI exits non-zero with a non-empty stderr
— suitable for CI failure detection.
Step 2 — trust the cert
$ opcua-cli trust opc.tcp://plc.local:4840 --trust-store=/etc/opcua/trust
Status: Trusted
Fingerprint: a1:b2:c3:d4:e5:f6:78:90:12:34:56:78:90:12:34:56:78:90:ab:cd
Subject: PLC-Server
Expires: 2027-01-01T00:00:00+00:00
Verify the fingerprint matches the one you expect (per device documentation, vendor email, out-of-band channel). On a hostile network, the download itself is the attack surface; see Securing the bootstrap below.
Step 3 — retry
$ opcua-cli browse opc.tcp://plc.local:4840 \
-s Basic256Sha256 -m SignAndEncrypt \
--cert=/etc/opcua/client.pem --key=/etc/opcua/client.key
Server (Object)
DeviceSet (Object)
Aliases (Object)
…
The trust store has the cert; subsequent connects validate successfully.
Trust policies
The CLI sends --trust-policy=... to control how strict the
validation is:
| Policy | Validation |
|---|---|
| (default — not set) | Accept anything (insecure; equivalent to --no-trust-policy) |
fingerprint |
Cert's SHA-1 fingerprint must be in the trust store |
fingerprint+expiry |
Fingerprint match and cert within its validity window |
full |
Full X.509 chain validation against the CA bundle in the trust store |
Default is no trust policy — the CLI does not validate the cert
unless you set --trust-policy. This is convenient for dev but
unsafe for production:
opcua-cli browse opc.tcp://plc.local:4840 \
-s Basic256Sha256 -m SignAndEncrypt \
--cert=/etc/opcua/client.pem --key=/etc/opcua/client.key \
--trust-policy=fingerprint+expiry
fingerprint+expiry is the production default — strict
fingerprint matching plus expiry check.
Custom trust store location
Both trust and the connect-time validation accept
--trust-store=PATH:
opcua-cli trust opc.tcp://plc.local:4840 --trust-store=/etc/opcua/trust
opcua-cli browse opc.tcp://plc.local:4840 ... --trust-store=/etc/opcua/trust
Use a system-wide path (/etc/opcua/trust) for shared deployments;
use --trust-store="$HOME/.opcua/trust" (or any per-user path) for
per-user installs. Remember the CLI does not install a default
store for you — at least one trust flag must be on the command
line for any trust-related behaviour.
Skipping validation temporarily
--no-trust-policy disables trust validation for one command —
useful for one-off diagnostic invocations against a server you
don't intend to trust permanently:
opcua-cli endpoints opc.tcp://unknown-server:4840 --no-trust-policy
The endpoints command runs over a transient unsecured channel
anyway, so this is fine for discovery. Do not ship
--no-trust-policy to production for non-discovery commands —
it eliminates the protection that trust store provides.
Securing the bootstrap
opcua-cli trust downloads the cert over an unsecured
connection. On a trusted network this is fine; on a hostile
one, an attacker between you and the server can substitute their
cert and you record the attacker's identity as trusted.
Two safer alternatives:
-
Out-of-band cert delivery. Have the vendor / operator bring you the server cert via USB / signed email / encrypted bundle. Verify the fingerprint matches their documentation. Then
trust:addit directly (currently library-only — for the CLI workflow, copy the.derinto the trust-store directory by hand). -
Bootstrap over a known-good network. Run
trustfrom a commissioning workstation on a physically-controlled segment. Lock down the trust store afterwards.
For production deployments, treat the trust step like a package signing key — pinned out-of-band, audited.
Listing and removing
| Command | What |
|---|---|
opcua-cli trust:list |
Print every trusted cert |
opcua-cli trust:list --json |
Same, machine-readable |
opcua-cli trust:remove <fingerprint> |
Drop a cert by fingerprint |
Use trust:remove when:
- The server rotates its certificate (the new fingerprint arrives; the old one is dead weight).
- A device is decommissioned.
- A cert was added by mistake.
See Commands · trust for the full reference.
CI pattern
Trust rollouts in CI are typically scripted:
# In the CI setup step:
mkdir -p ~/.opcua/trusted
# Drop the pre-vetted cert files into ~/.opcua/trusted/
# Test runs without needing online trust calls
opcua-cli browse opc.tcp://test-server:4840 \
-s Basic256Sha256 -m SignAndEncrypt \
--cert=$CLIENT_CERT --key=$CLIENT_KEY \
--trust-policy=fingerprint+expiry
The CI runner gets the trusted certs pre-installed; the test
runs in --trust-policy=fingerprint+expiry posture. No live
trust call needed — and no hostile-network exposure during
bootstrap.
See Recipes · Batch trust rollout for the operator-side script.