(motir-core) The certificate status job — refresh pending customer domains from the platform, surface issued / expired / revoked / failed, and record the last check
A customer domain's certificate state changes on the platform, not in our database — this job is what carries the change back, so the settings pane shows the truth and never a stale "pending". Fly validates and issues asynchronously after the lifecycle requests a certificate, renews on its own, and a domain can stop resolving because a customer edited DNS nobody told us about. Nothing in the request path can observe any of that.
What ships
- A job in the repository's OWN job engine —
lib/jobs/definitions/publicAddressCertificateRefresh.tsviadefineJob(Inngest is retired here:tests/jobs/inngest-retired.test.tsasserts it), registered inlib/jobs/registry.ts, scheduled bylib/jobs/cron.tson the cadence the ADR's Q5 sets (recommend every 5 minutes forpending_certificate/verifying, hourly forissued), with acatchUpposture consistent withlib/jobs/catchUp.ts. - Per run:
listByStatusOlderThanfor the statuses it owns, in bounded pages; for each row call the port'scheck(hostname)(the adapter) and map the answer onto the enum:issuedwhen the platform reports the certificate issued;failedwith the platform's reason when validation has failed or the required DNS is absent;expired/revokedwhen the platform says so on a previously-issued row;verifyingrows re-run the_motir-verifyTXT lookup the lifecycle owns and advance or fail. Every transition writeslastCheckedAt, andissuedAton the firstissued. The enum is total: aRecord<PublicAddressStatus, …>decides what each value does on a check, including the values that do nothing (active,alias). - The platform call is a side effect outside any transaction (
CLAUDE.md): read the platform, THEN one short write per row; a platform failure for one row marks nothing and moves on (logged, DLQ'd throughlib/jobs/dlq.tsafter the engine's retry budget), so one unreachable API call never wedges the sweep. - Not-configured is inert: with
FLY_CERTS_TOKENunset the job logs once and exits — a self-hosted build schedules it and it does nothing. - Tests: table-driven over every
PublicAddressStatus× every port answer; the DLQ path for a platform failure; the bounded page size; the not-configured exit; theRecordtotality (a compile-time failure when the enum grows).
Boundary
No route, no UI, no certificate REQUEST (the lifecycle requests; this only reads), no rename or alias handling. The pane (next card) renders whatever status this job last wrote plus lastCheckedAt, which is why it is blocked_by this card.
Acceptance criteria
- The job is defined with
defineJob, registered, and scheduled; a test enumerates the registry and finds it, andinngest-retired.test.tsstays green. - For each
PublicAddressStatusvalue, a table-driven test states the transition (or the no-op) for each port answer, and the mapping is a totalRecord. - A platform error on one hostname leaves that row unchanged, is retried by the engine, reaches the DLQ after the budget, and does not prevent the other rows in the same run from being checked.
lastCheckedAtis written on every checked row andissuedAtexactly once; a row that isissuedand later reported expired by the platform becomesexpiredwith the reason.- With
FLY_CERTS_TOKENunset the run exits without a repository read, asserted with a spy. - No file outside
motir-coreis touched.
Context refs
motir-core/lib/jobs/defineJob.ts·registry.ts·cron.ts·catchUp.ts·dlq.ts— the engine;tests/jobs/inngest-retired.test.ts- the adapter —
CertificateProvider.check; the store —listByStatusOlderThan,updateStatus - the decision — Q5 (Fly renews; we surface state)
motir-core/CLAUDE.md— side effects outside transactions