certs CLI

Run locally without us. No middleman. No rate limits. Same engine as certs.lol.

MITGo<5s scans

Install

# Homebrew
brew install yokedotlol/tap/certs

# Or one-liner
curl -sSL https://certs.lol/install.sh | bash

# Or download from GitHub Releases
curl -sL https://github.com/yokedotlol/certs-lol/releases/latest/download/certs_darwin_arm64.tar.gz | tar xz
sudo mv certs /usr/local/bin/

Quick Start

# Scan a domain
certs stripe.com

# JSON output (default when piped)
certs stripe.com | jq

# Grade only
certs stripe.com -g

# CI gate — fail if below A
certs api.prod.example.com --assert "min-grade A"

# Compliance check
certs payments.prod --profile pci

# Scan internal hosts (private IPs allowed by default)
certs internal-api.corp.local

# Mail server TLS
certs --mx example.com

Three Modes

ModeWhenUse
PrettyTTY (default)Human-readable, colored tree output
JSONPiped / --jsonMachine-readable, matches certs.lol API
Assert--assertCI/CD gating with pass/fail reports

Assertion Rules

Compose multiple assertions to gate deploys. All must pass — any failure exits non-zero.

certs api.prod \
  --assert "min-grade A" \
  --assert "no-tls1.0" \
  --assert "cert-days 30" \
  --assert "no-insecure-ciphers"

Grade

min-grade <G>Grade must be ≥ threshold (A+, A, B, C, D, F)

Certificate

cert-days <N>≥ N days until expiry
cert-type <type>Validation level (DV, OV, EV) — minimum match
cert-key-min <bits>Minimum key size in bits
cert-key-type <type>Key type (RSA, ECDSA, Ed25519)
cert-san <pattern>At least one SAN must match glob
cert-issuer <pattern>Issuer must contain string
cert-chain-validChain must be valid
cert-has-sctsCT SCTs must be present

Protocol

min-tls <ver>Minimum supported TLS version
max-tls <ver>Maximum supported TLS version
no-tls1.0TLS 1.0 must not be supported
no-tls1.1TLS 1.1 must not be supported
has-tls1.3TLS 1.3 must be supported
has-pqPost-quantum key exchange required
has-echEncrypted Client Hello required

Ciphers

no-insecure-ciphersZero insecure ciphers (RC4, NULL, EXPORT, anon)
no-weak-ciphersZero weak ciphers (3DES, CBC-no-FS, RSA-kex)
max-weak-ciphers <N>At most N weak ciphers
min-strong-ciphers <N>At least N strong ciphers
has-forward-secrecyAt least one FS cipher present

Security & DNS

has-hstsHSTS header required
hsts-min-age <secs>HSTS max-age minimum
has-hsts-preloadHSTS preload directive required
has-dnssecDNSSEC required
has-caaCAA records required
has-ocsp-staplingOCSP stapling required

Compliance

compliant-pciPCI DSS 4.0 transport requirements
compliant-nistNIST SP 800-52r2 requirements
compliant-hipaaHIPAA transport requirements

Mail

has-starttlsServer must offer STARTTLS upgrade

Profiles

Named bundles for common use cases:

# Production baseline
certs api.prod --profile production

# PCI DSS compliance
certs payments.prod --profile pci

# Maximum strictness
certs cdn.prod --profile strict
ProfileAssertions
productionmin-grade A, no-tls1.0, no-tls1.1, no-insecure-ciphers, cert-days 14, has-hsts
stagingmin-grade B, no-insecure-ciphers, cert-days 7
strictmin-grade A+, no-tls1.0/1.1, no-weak/insecure, has-tls1.3, has-pq, has-forward-secrecy, cert-days 30, has-hsts, has-hsts-preload, has-dnssec, cert-has-scts
pcicompliant-pci, no-insecure/weak, has-tls1.3, cert-days 30, has-hsts
nistcompliant-nist, has-tls1.3, no-tls1.0/1.1, cert-key-min 256
hipaacompliant-hipaa, cert-days 30, no-insecure-ciphers, has-hsts
baselinemin-grade C, no-insecure-ciphers, cert-days 7, cert-chain-valid

Profiles and --assert compose freely:

certs cdn.prod --profile production --assert "has-pq"

Config File

Check a .certs.yaml into your repo for team-wide defaults:

# .certs.yaml
profile: production
assertions:
  - cert-type OV
  - cert-issuer DigiCert
  - has-pq
  - hsts-min-age 63072000
targets:
  - api.example.com
  - cdn.example.com
  - payments.example.com
# Uses .certs.yaml from current directory
certs

# Or specify config
certs --config path/to/.certs.yaml

Lookup order: --config flag → .certs.yaml in cwd → ~/.config/certs/config.yaml

Bulk Scanning

# Scan from file (auto-concurrent above 3 targets)
certs --file domains.txt --out results/

# With assertions
certs --file production-domains.txt --profile production --out results/

# Control concurrency
certs --file domains.txt --workers 20 --quiet

Writes per-target JSON files and a _summary.json with aggregate results.

STARTTLS & Mail

# Auto-detect from port
certs mail.example.com --port 25       # SMTP STARTTLS
certs mail.example.com --port 587      # SMTP STARTTLS
certs mail.example.com --port 993      # IMAP implicit TLS

# Explicit protocol
certs mail.example.com --port 2525 --starttls smtp

# MX lookup — resolve and scan all mail servers
certs --mx example.com --assert "has-tls1.3"

CI Examples

GitHub Actions

name: TLS Check
on: [push]
jobs:
  tls:
    runs-on: ubuntu-latest
    steps:
      - name: Install certs
        run: |
          curl -sL https://github.com/yokedotlol/certs-lol/releases/latest/download/certs_linux_amd64.tar.gz | tar xz
          sudo mv certs /usr/local/bin/
      - name: Check TLS
        run: certs api.example.com --profile production

GitLab CI

tls-gate:
  script:
    - curl -sL https://github.com/yokedotlol/certs-lol/releases/latest/download/certs_linux_amd64.tar.gz | tar xz
    - ./certs api.example.com --profile production
  only:
    - main

Exit Codes

0Scan succeeded, all assertions passed
1Scan succeeded, assertion(s) failed
2Usage error (bad flags, invalid assertion)
3Scan/connection error

Flags

FlagShortDefaultDescription
--json-jautoForce JSON output
--table-tautoForce pretty output
--grade-gPrint only the letter grade
--assert-aAssertion rule (repeatable)
--profile-PNamed assertion profile
--config-c.certs.yamlConfig file path
--port-p443Target port
--timeout15sConnection timeout
--starttlsForce STARTTLS protocol
--probe-onlyfalseSkip enrichment
--no-privatefalseBlock private/reserved IPs
--mxResolve MX and scan mail servers
--file-fRead targets from file
--out-oWrite results to directory
--workers-w10Concurrent workers (bulk)
--quiet-qfalseSuppress progress

Source

github.com/yokedotlol/certs-lol — MIT licensed.