If you spend most of your day in Emacs and manage any amount of infrastructure, you’ve felt this friction: you need to check a cert, so you alt-tab to a terminal, type out an openssl s_client incantation you can never quite remember, parse the wall of output, and then switch back to whatever you were doing. The context switch is small but constant, and it adds up.
I built certradar-cli — a Rust-based SSL/TLS analysis tool — partly to solve this. But it got way more useful once I wired it into Emacs with a few lines of elisp. Now cert checks are one keystroke away, inline with my workflow, and I actually do them proactively instead of reactively.
Prerequisites
You’ll need certradar-cli installed. It’s a single Rust binary:
cargo install certradar-cli
Verify it’s working:
certradar-cli ssl example.com
If you’re on a system where ~/.cargo/bin isn’t on your PATH, you’ll also want to tell Emacs where to find it:
(add-to-list 'exec-path "~/.cargo/bin")
The M-x Command
This gives you a check-ssl command that prompts for a domain and opens the results in a dedicated read-only buffer:
(defun check-ssl--clean-domain (domain)
"Strip protocol prefix and port suffix from DOMAIN."
(let ((cleaned (replace-regexp-in-string "https?://" "" domain)))
(replace-regexp-in-string ":[0-9]+$" "" cleaned)))
(defun check-ssl (domain)
"Check SSL/TLS certificate for DOMAIN using certradar-cli."
(interactive "sDomain: ")
(let* ((clean-domain (check-ssl--clean-domain domain))
(buf-name (format "*SSL: %s*" clean-domain))
(buf (get-buffer-create buf-name)))
(with-current-buffer buf
(read-only-mode -1)
(erase-buffer)
(insert (format "SSL Certificate Report: %s\n" clean-domain))
(insert (make-string 50 ?─))
(insert "\n\n")
(let ((result (shell-command-to-string
(format "certradar-cli ssl --no-color %s 2>&1"
(shell-quote-argument clean-domain)))))
(insert result))
(goto-char (point-min))
(special-mode))
(switch-to-buffer-other-window buf)))
Usage: M-x check-ssl RET example.com RET. You get a buffer you can scroll, search with C-s, or yank text from. The buffer is in special-mode so you won’t accidentally edit the output.
The domain cleanup helper strips both https:// prefixes and :443 port suffixes, so you can paste https://example.com:8443/path and it just works. --no-color keeps ANSI escape codes out of the buffer — without it, Rust CLI tools tend to dump ^[[32m garbage into your output. shell-quote-argument prevents injection if you’re ever passing untrusted input. switch-to-buffer-other-window keeps your current buffer visible — useful when you’re checking a cert referenced in the code you’re reading.
Check the Domain Under Your Cursor
If you keep domain lists in org files, Ansible inventories, Nginx configs, or anywhere else, this function grabs whatever’s under point and checks it:
(defun check-ssl-at-point ()
"Check SSL cert for the domain under point."
(interactive)
(let ((domain (thing-at-point 'url t)))
(if domain
(check-ssl domain)
(let ((word (thing-at-point 'symbol t)))
(if (and word (string-match-p "\\." word))
(check-ssl word)
(call-interactively #'check-ssl))))))
It tries to grab a full URL at point first, then falls back to the symbol at point if it contains a dot (probably a domain), and prompts you if neither works. No need to strip the protocol here — check-ssl--clean-domain handles all of that.
Bind it somewhere fast:
(global-set-key (kbd "C-c s") #'check-ssl-at-point)
Cursor on domain → C-c s → cert report. No context switch, no copy-paste, no remembering flags.
Async Variant (Don’t Freeze Emacs)
One problem with the version above: shell-command-to-string is blocking. If a domain is hanging or DNS is slow, Emacs locks up until it times out. That’s fine for fast lookups, but if you’re checking anything over a flaky connection, it’s painful.
Here’s the same thing using make-process so Emacs stays responsive while the cert is fetched:
(defun check-ssl-async (domain)
"Check SSL/TLS certificate for DOMAIN asynchronously."
(interactive "sDomain: ")
(let* ((clean-domain (check-ssl--clean-domain domain))
(buf-name (format "*SSL: %s*" clean-domain))
(buf (get-buffer-create buf-name)))
(with-current-buffer buf
(read-only-mode -1)
(erase-buffer)
(insert (format "SSL Certificate Report: %s\n" clean-domain))
(insert (make-string 50 ?─))
(insert "\n\nFetching...\n"))
(switch-to-buffer-other-window buf)
(make-process
:name (format "certradar-%s" clean-domain)
:buffer buf
:command (list "certradar-cli" "ssl" "--no-color" clean-domain)
:sentinel (lambda (proc _event)
(when (eq (process-status proc) 'exit)
(with-current-buffer (process-buffer proc)
(goto-char (point-min))
(when (search-forward "Fetching..." nil t)
(replace-match ""))
(special-mode)))))))
The buffer pops open immediately with “Fetching…”, and the output streams in as it arrives. You can keep typing in other buffers while it runs. If you want this as your default, just rebind C-c s to call check-ssl-async instead.
Org-Babel for Runbooks
If you use org-mode for operational documentation — and if you’re running infrastructure, you really should — this embeds cert checks directly into your notes.
Single Domain Check
#+NAME: ssl-check
#+BEGIN_SRC shell :var domain="example.com" :results output
certradar-cli ssl "$domain"
#+END_SRC
Place your cursor inside the block and hit C-c C-c. The output appears inline, right below the block. Change the :var domain= value and run it again. The results become part of your document — perfect for incident write-ups, audit trails, or change management records.
Batch Checking Multiple Domains
#+NAME: ssl-batch
#+BEGIN_SRC shell :results output
for domain in example.com github.com expired.badssl.com; do
echo "=== $domain ==="
certradar-cli ssl "$domain"
echo ""
done
#+END_SRC
I keep a domains.org file with all the certs I’m responsible for. During maintenance windows, I run through the batch blocks and the results stay versioned in the file. It’s not a replacement for proper monitoring, but it’s a great complement — especially when you need evidence that you actually checked things.
Why This Works So Well
The real value isn’t the execution itself — it’s that your checks live next to your context. You’re writing up an incident and need to verify the cert state? You don’t leave the document. Onboarding someone and want to show them how to verify certs? The runbook runs. Everything stays in one place, reproducible and version-controlled.
Background Expiry Monitoring
This one is for anyone who’s been woken up at 3am because a cert expired silently. It runs a background timer that checks your critical domains and warns you inside Emacs when certs are getting close:
(defvar ssl-watch-domains '("yourdomain.com" "api.yourdomain.com")
"Domains to monitor for certificate expiry.")
(defvar ssl-expiry-warning-days 14
"Warn when cert expires within this many days.")
(defun ssl-check-expiry-warnings ()
"Check watched domains and message any upcoming expirations."
(dolist (domain ssl-watch-domains)
(let ((output (shell-command-to-string
(format "certradar-cli ssl --no-color %s --json 2>/dev/null"
(shell-quote-argument domain)))))
(when (string-match "\"days_until_expiry\":\\s*\\([0-9]+\\)" output)
(let ((days (string-to-number (match-string 1 output))))
(when (<= days ssl-expiry-warning-days)
(message "SSL WARNING: %s expires in %d days!" domain days)
(run-with-timer 0.5 nil
(lambda (d n)
(display-warning 'ssl
(format "%s certificate expires in %d days" d n)
:warning))
domain days)))))))
;; Check once on startup, then every 6 hours
(ssl-check-expiry-warnings)
(run-with-timer 21600 21600 #'ssl-check-expiry-warnings)
Now, this Emacs timer is fine for a handful of domains you want to keep an eye on locally. But if you’re managing infrastructure at any real scale, you probably want something that doesn’t depend on your editor being open. That’s why I also built CertRadar.net — it gives you a centralized dashboard with certificate transparency log monitoring, chain validation, and configuration analysis across your entire infrastructure. The stuff that falls through the cracks — the staging environment cert nobody’s watching, the internal tool with a self-signed cert someone issued a year ago, the domain your team inherited last quarter — CertRadar catches that.
I use both. The CLI for “let me check this right now” and CertRadar for “make sure nothing expires or misconfigures while I’m not looking.”
The timer checks every 6 hours (21600 seconds) and uses display-warning to surface alerts in the *Warnings* buffer, which is hard to miss. Adjust ssl-expiry-warning-days based on your renewal workflow — 14 days works for automated Let’s Encrypt setups, but you might want 30 or 60 if your certs require manual renewal.
Installation: The Full Package
I’ve packaged all of the above into a single certradar.el file you can drop into your config. It also includes a check-ssl-batch interactive command and ssl-watch-start / ssl-watch-stop functions for managing the expiry timer.
Grab it from the certradar-cli repository.
Setup
Download
certradar.elinto~/.emacs.d/lisp/(create the directory if it doesn’t exist)Add this to your
init.el:
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
(require 'certradar)
;; Keybindings
(global-set-key (kbd "C-c s") #'check-ssl-at-point)
(global-set-key (kbd "C-c S") #'check-ssl-batch)
;; Optional: enable the expiry watcher
;; (setq ssl-watch-domains '("yourdomain.com" "api.yourdomain.com"))
;; (ssl-watch-start)
- Restart Emacs or evaluate your init.el with
M-x eval-buffer.
Why Bother
The actual time saved per cert check is maybe 10 seconds. That’s not the point. Removing the friction changes your behavior. When checking a cert is one keystroke away, you check certs. When it requires switching contexts, opening a terminal, and remembering syntax, you don’t — or at least not as often as you should.
28 years into this career, the tools I actually use are the ones embedded in my workflow, not the ones bookmarked in my browser. If Emacs is where you live, your cert checks should live there too.
certradar-cli is free and open source. If you want a full web-based dashboard for monitoring certs across all your domains, check out CertRadar.net. For ongoing SSL monitoring with alerts, there’s SSLGuard.net.
Top comments (0)