Documentation menu

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.

CommandA row is ✓ OK when
root-ca listthe root is a CA certificate, signed by its own key, and currently valid
intermediate-ca listthe intermediate is a CA certificate signed by the root, and currently valid
server-cert listthe leaf chains through the intermediate to the root, carries serverAuth, and is not in the intermediate's CRL
client-cert listthe 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)"
  }
]
FieldContent
namethe root's literal name, the intermediate name, or the leaf file name
expiresNotAfter as YYYY-MM-DD, or unknown when the file cannot be read
days_leftwhole days until expiry; negative once expired, -1 when unknown
chain_validtrue when the row would show ✓ OK
chain_errorthe 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.