Listing and verification
Every tier has a list subcommand that reports expiry and verifies the chain of trust, as a table or as JSON.
The list commands
Every tier has a list subcommand (aliases ls and l). Each row reports the expiry date, the days left, and whether the certificate verifies.
$ homepki root-ca list # every root CA in the workdir
$ homepki intermediate-ca list -d runlocal.dev
$ homepki server-cert list -d runlocal.dev -i bu1
$ homepki client-cert list -d runlocal.dev -i bu1
root-ca list takes no --domain. The intermediate list needs --domain, and the leaf lists also need --intermediate.
What each list verifies
Verification runs in-process with Go's crypto/x509.
| Command | A row is ✓ OK when |
|---|---|
root-ca list | the root is a CA certificate, signed by its own key, and currently valid |
intermediate-ca list | the intermediate is a CA certificate signed by the root, and currently valid |
server-cert list | the leaf chains through the intermediate to the root, carries serverAuth, and is not in the intermediate's CRL |
client-cert list | the same, with clientAuth |
$ homepki server-cert list -d runlocal.dev -i bu1
Server Certificates for runlocal.dev/bu1:
NAME EXPIRES DAYS LEFT CHAIN
──────────────── ────────── ───────── ──────────
cli1.crt 2027-09-26 364 ✗ INVALID: x509: certificate specifies an incompatible key usage
kong-gateway.crt 2027-09-26 364 ✓ OK
In this example a client certificate was copied into server-tls/. The extended key usage check catches it: a clientAuth certificate does not pass as a server certificate.
Revoked leaves
The leaf lists read the intermediate's CRL, when there is one, and report revoked certificates as invalid:
cli1.crt 2027-09-26 364 ✗ INVALID: revoked on 2026-09-26 (keyCompromise)
See Revocation.
JSON output
The table is coloured with ANSI escapes and its column widths depend on the content. In scripts, use -o json (or --output json):
$ homepki client-cert list -d runlocal.dev -i bu1 -o json
[
{
"name": "cli1.crt",
"expires": "2027-09-26",
"days_left": 364,
"chain_valid": false,
"chain_error": "revoked on 2026-09-26 (keyCompromise)"
}
]
| Field | Content |
|---|---|
name | the root's literal name, the intermediate name, or the leaf file name |
expires | NotAfter as YYYY-MM-DD, or unknown when the file cannot be read |
days_left | whole days until expiry; negative once expired, -1 when unknown |
chain_valid | true when the row would show ✓ OK |
chain_error | the reason, present only when chain_valid is false |
An empty tier prints [] in JSON and No server certificates found. in the table, and exits 0. An empty list is not a failure signal.
jq examples
# fail the script when any leaf does not verify
homepki server-cert list -d runlocal.dev -i bu1 -o json \
| jq -e 'all(.[]; .chain_valid)' >/dev/null || echo "broken chain!"
# leaves expiring within 30 days
homepki server-cert list -d runlocal.dev -i bu1 -o json \
| jq -r '.[] | select(.days_left < 30) | .name'
# why each invalid leaf fails
homepki client-cert list -d runlocal.dev -i bu1 -o json \
| jq -r '.[] | select(.chain_valid | not) | "\(.name): \(.chain_error)"'
Checking after --force
--force on a CA tier creates a new key, which orphans every certificate signed by the old one. The tier you replaced still reports ✓ OK; only the tier below flips to invalid:
$ homepki intermediate-ca -d runlocal.dev -n bu1 --force
$ homepki intermediate-ca list -d runlocal.dev
bu1 2032-09-24 2189 ✓ OK
$ homepki server-cert list -d runlocal.dev -i bu1
kong-gateway.crt 2027-09-26 364 ✗ INVALID: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "bu1.runlocal.dev")
After forcing a CA tier, check the lowest tier, not the one you touched. A certificate signed by a replaced key cannot be recovered: re-issue it.
Verifying by hand with OpenSSL
OpenSSL is not needed to use homepki, but it is useful to inspect the results:
ROOT=~/.homepki/runlocal-dev
CRT=$ROOT/bu1/server-tls/kong-gateway.crt
KEY=$ROOT/bu1/server-tls/kong-gateway.key
# chain: root as the anchor, intermediate as untrusted
openssl verify -CAfile $ROOT/ca/runlocal-dev-root-ca.crt \
-untrusted $ROOT/bu1/bu1-intermediate-ca.crt $CRT
# subject, SANs and extended key usage
openssl x509 -in $CRT -noout -subject -ext subjectAltName,extendedKeyUsage
# does the key match the certificate?
diff <(openssl x509 -in $CRT -noout -pubkey) \
<(openssl pkey -in $KEY -pubout) && echo "key matches"
The last check is the one list does not do: it reads certificates only. Run it after re-issuing a leaf if a server was reloaded with files from different runs.