---
title: "The certificate an agent provisioned and nobody agreed to renew"
description: "An AI agent that ran certbot once did the work correctly and made no commitment about the renewal. The setup is in your repository; the recurring obligation it created is in nobody's calendar."
url: https://harpywatch.com/blog/the-certificate-nobody-agreed-to-renew
kind: article
author: "The HarpyWatch team"
published: 2026-10-22T09:00:00+00:00
tags: ["AI Agents", "Reliability", "TLS"]
publisher: "HarpyWatch"
---

# The certificate an agent provisioned and nobody agreed to renew

Ninety days ago, an agent set up TLS on a new subdomain. It picked a challenge
type, ran certbot, wrote the nginx block, reloaded, and verified the padlock. It
was correct, it was fast, and it was finished in about four minutes.

Today the certificate expires.

Nothing went wrong in that session. The work was good. The problem is
structural: **the agent completed a task that quietly created a recurring
obligation, and only the task had an owner.**

## Setup is a transaction, maintenance is a relationship

A human engineer doing that work carries something the agent does not. They
leave the terminal knowing that a thing now exists which will need attention
later. That knowledge is fuzzy and unreliable — it is the reason certificates
lapsed long before agents existed — but it is not zero. It occasionally becomes
a diary entry, a runbook line, or a nagging feeling in week eleven.

An agent's session ends. There is no week eleven. The next session is a
different context with a different task, and it has no more knowledge of the
certbot run than it has of anything else it did not do.

This is not a criticism of agents; it is a description of how they are deployed.
They handle the one-off well — reading the docs, getting the flags right,
handling the edge case in the DNS challenge. The ongoing part needs two things a
coding-agent setup does not normally include: state that outlives the context
window, and a scheduler that will wake something up in eleven weeks with that
state attached. Both are buildable — a state store the agent writes obligations into, and
something outside the agent that reads it on a schedule. Neither comes with the
tools by default, so in practice neither exists.

The dangerous part is that the transaction *looks* like the whole job. The
padlock appears. The test passes. Something completed successfully, so the
thought stops there.

## The whole class, not just certificates

Certificates are the clearest case because the clock is short and the failure is
total. But the pattern generalises to anything with an expiry, a renewal, or a
host it lives on:

| What an agent set up correctly | What it never agreed to do again |
| --- | --- |
| A Let's Encrypt certificate | Renew it, or notice when the renewal timer stopped |
| A domain registration or DNS zone | Renew the registration; keep the contact address real |
| An API key, OAuth client, or service account | Rotate it before its expiry; update every consumer |
| A cron entry or systemd timer on a box | Recreate it when the box is replaced |
| A webhook signing secret | Roll it on the schedule the provider requires |
| A cloud credential with a maximum lifetime | Notice the maximum lifetime at all |

Two of those deserve expanding.

**The cron entry on a box that got replaced.** An agent added a renewal timer to
a host. Six weeks later, a different session rebuilt that host from an image, or
scaled the group, or moved the service to a new instance type. The new machine
is correct in every way that was written down. The timer was not written down —
it was typed into a live box. Nothing failed. The renewal simply stopped
happening, silently, and the first symptom arrives when the certificate runs out.

**The API key with an expiry.** Keys that expire are a security improvement and
an operational trap. The agent that integrated the service used a key that was
valid at the time. Expiry is often a year out, which is far past the horizon of
anyone's attention, and the failure mode is not an interstitial in a browser
but a background job that starts returning 401 and retrying quietly.

## Why nobody has the calendar entry

Calendar entries and runbook lines are made by the person who did the work,
during the work, because doing it made them aware of the future obligation. When
the work is done by an agent, the awareness is generated inside a session that
ends, and the human in the loop reviewed a *result* — a working site — not a
process.

Reviewing a diff tells you what the code does. It does not tell you what
external, time-bound facts the code now depends on. A merged pull request that
adds an nginx server block does not announce that it has bound your site's
availability to a renewal timer running somewhere else on a short clock.

So the obligation exists, it is real, it has a deadline, and it belongs to
nobody. This is the same failure that has always produced expired certificates.
Agents did not invent it. They industrialised it. The amount of
correctly-completed setup work a small team can produce has gone up sharply. The
number of people available to carry the obligations it creates has not.

## Detect the consequence, because the cause is unbounded

You cannot fix this by asking agents to remember, and you should be sceptical of
fixing it by asking humans to be more diligent. Certificates were lapsing for
that reason long before any of this was automated.

What works is checking the observable fact rather than the process that was
supposed to produce it. We build a monitoring product, so the three checks named
below are ours; the principle holds whatever you use to make the measurement.

**`ssl-cert-expiry`** connects to the hostname a visitor uses and reads the
certificate that was actually presented. It is indifferent to whether renewal is
automated, whether the timer survived the rebuild, or whether anyone reloaded
nginx afterwards. If the socket is serving something that expires in nine days,
that is the fact, regardless of what the file on disk says.

**`cert-chain-trust`** verifies the chain the server sends, in order, to a
trusted root. An agent reconfiguring a proxy can very easily produce a valid
leaf with a missing intermediate. It works in the browser it was tested in and
fails in a client with a colder trust store — which is to say, in your customer's
integration and not on your laptop.

**`domain-expiry`** watches the registration underneath all of it, on a much
longer clock. It is the same failure with the volume turned down: a renewal
reminder going to an address that belonged to somebody who left, and when it
lapses there is no name left to present a certificate for.

## The one-line version

There is a limit worth stating: this catches the consequence, not the cause. A
check tells you the certificate has nine days left. It does not tell you the
timer died in a rebuild six weeks ago, and it will not restart it.

That is a real gap and it is still the right trade. The cause is unbounded —
there are a great many ways a renewal can stop happening, and each new piece of
infrastructure adds a few. The consequence is finite,
observable from outside, and requires no privileged access.

So the working assumption should be that anything an agent provisioned with an
expiry date will be renewed by nobody. There are other ways to contradict that
assumption — declaring the timer in infrastructure-as-code so a rebuild carries
it, watching Certificate Transparency logs for a renewal that did not appear —
and they are worth having. The cheapest one, and the one that survives all of
them failing at once, is a scheduled measurement from outside against the
address a visitor actually uses.
