A zero-ceremony TLS certificate inspector. Point it at a host and it prints
expiry, issuer, SAN and the rest of the leaf certificate — no more
openssl s_client -connect ... | openssl x509 -noout -dates -ext subjectAltName
incantations.
$ certinfo sen.ltd
sen.ltd:443
───────────
Subject : sen.ltd
Issuer : Amazon RSA 2048 M01
Serial : 0f:d9:10:30:fb:9a:cb:95:c4:d2:9f:6b:2d:93:ba:0d
Not before : 2025-09-15T00:00:00Z
Not after : 2026-10-14T23:59:59Z 181 days left
Signature : sha256WithRSAEncryption
SAN : DNS:sen.ltd, DNS:*.sen.ltd
- You want to know when a cert expires without remembering a three-step OpenSSL pipeline.
- You want a single static binary you can drop into a container, a cron job or a Nagios/Mackerel check.
- You want structured output (
--json) for scripts and a--thresholdexit code for monitors. - You want to inspect certs that your browser refused — expired, self-signed, mismatched hostname — without disabling anything.
cargo install --path .
# or, after cloning
cargo build --release
./target/release/certinfo sen.ltdThe binary is around 1.1 MB on macOS (arm64), fully statically
linked against rustls + ring — no OpenSSL dependency.
certinfo [OPTIONS] <HOST>
Host accepts example.com, example.com:8443, 127.0.0.1:4443, and
IPv6 literals [::1]:443. Default port is 443.
| Flag | Default | Meaning |
|---|---|---|
--json |
off | Emit a single JSON document instead of the coloured text layout. |
--chain |
off | Print every certificate the server presented, not just the leaf. |
--threshold <DAYS> |
— | Exit with code 1 if the leaf has fewer than DAYS remaining. |
--timeout <SECS> |
10 |
TCP and handshake timeout. |
--no-color |
auto | Force plain output (auto-off when stdout is not a TTY). |
| Code | Meaning |
|---|---|
0 |
Certificate is valid (and, if --threshold was given, above it). |
1 |
Certificate is already expired or below --threshold. |
2 |
Network error, parse error, or invalid input. |
Check before rolling out — fail the build if example.com has fewer than
30 days left:
certinfo --threshold 30 example.comMonitoring loop — emit structured facts for a dashboard:
certinfo --json api.example.com \
| jq '{host, days: .leaf.days_remaining, expires: .leaf.not_after}'Inspect a cert your browser rejected:
certinfo expired.badssl.com # prints the chain, exits 1
certinfo self-signed.badssl.com # prints `self-signed`, exits 0Walk the whole chain:
certinfo --chain sen.ltdInspect a non-HTTPS TLS endpoint (IMAPS, SMTPS, etc.):
certinfo imap.example.com:993
certinfo smtp.example.com:465- Open a TCP connection to
host:port. - Drive a TLS handshake with
rustls, configured with a permissiveServerCertVerifierthat accepts every certificate. We want to see the cert, not validate it — your browser already does that, and the whole point of a diagnostic tool is to work on the certs your browser rejected. - Call
ClientConnection::peer_certificates()to pull the DER-encoded chain off the completed handshake. - Parse each cert with
x509-parser: subject, issuer, serial, validity, SAN extension, signature OID. - Render either a human-readable block or a JSON document, and decide
the exit code based on
--threshold.
There is no HTTP request. certinfo stops at the handshake, sends
close_notify, and moves on. That keeps it usable against any TLS
endpoint — IMAPS, LDAPS, Postgres over TLS — as long as the server
sends its certificate eagerly after ClientHello.
cargo test # 20 tests (unit + CLI integration)
cargo build --releaseThe release profile (strip, lto, opt-level = "z", panic = "abort")
squeezes the binary down to ~1.1 MB.
MIT. See LICENSE.