C
// BUILT FOR REAL NEEDS

We built CertFleet because the alternatives were too expensive.

We are a team of developers at MCS. We needed reliable SSL/TLS and uptime monitoring for our own sites and applications. The tools on the market were either bloated observability platforms or charged too much for simple certificate expiry alerts.

// OUR MISSION

Focused on Reliability

We are developers focused on reliable infrastructure and exceptional developer experience. We created CertFleet to solve a concrete problem we had at MCS: keeping certificates and uptime under control without paying for an entire observability suite.

The product is intentionally narrow: it does SSL/TLS monitoring + uptime checks extremely well, with generous free tier and fair pricing.

// COMPANY & LOCATION
MCS
5Q Avenue des Naudières
44800 Saint-Herblain, France
MCS is registered under SIREN 106 942 600 (R.C.S. Nantes).
Independently operated from France (near Nantes).
All core services run on Cloudflare's global network.
TLS probe located in Europe (europe-west1).
// ARCHITECTURE

Clear, minimal, and built on modern primitives.

Everything is designed for reliability, low cost, and transparency. No unnecessary services.

High-level system
CertFleet architecture overview: Astro landing, React app, Hono API on Cloudflare connected to D1, KV, external TLS probe in Europe and crt.sh

The only component outside Cloudflare is the passive TLS probe (Workers cannot read the full served X.509 certificate).

Certificate check flow
CertFleet certificate verification flow showing live TLS probe combined with Certificate Transparency logs
Monitoring & alerting loop
CertFleet monitoring and alerting architecture with 1-minute cron, idempotent alerts via email, Slack and webhooks
DEVELOPER INTEGRATION

Signed webhooks (verify with HMAC)

When WEBHOOK_SIGNING_SECRET is set on the worker, every webhook (SSL expiry + uptime down) includes an x-signature header. This is the hex-encoded HMAC-SHA256 of the exact raw JSON body.

Example SSL webhook payload
{
  "type": "ssl.expiring",
  "hostname": "example.com",
  "port": 443,
  "daysRemaining": 7,
  "threshold": 7,
  "notAfter": "2026-07-10T12:00:00.000Z",
  "issuer": "Let's Encrypt",
  "status": "expiring"
}

Uptime payload uses type: "uptime.down".

Verification (Node.js)
const crypto = require('crypto');
// IMPORTANT: use the raw body string, before JSON.parse
const rawBody = /* exact JSON string from request */;
const receivedSig = req.headers['x-signature'];
const secret = 'your-webhook-signing-secret';

const expected = crypto
  .createHmac('sha256', secret)
  .update(rawBody)
  .digest('hex');

if (receivedSig !== expected) {
  // reject — not from CertFleet
  return res.status(401).send('bad signature');
}
Python example
import hmac
import hashlib

secret = b'your-webhook-signing-secret'
raw_body = b'{"type":"ssl.expiring",...}'   # exact raw bytes!

expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
received = request.headers.get('x-signature')

if received != expected:
    raise Exception("Invalid signature - not from CertFleet")
Quick test with bash / openssl
# Quick local test (compute what the signature should be)
echo -n "$RAW_JSON_PAYLOAD" | \
  openssl dgst -sha256 -hmac "$WEBHOOK_SIGNING_SECRET" | \
  awk '{print $2}'

Implementation: workers/api/src/services/alerts.ts (signPayload).
Handy generator: workers/api/test/test-signed-webhook.js

IN ACTION

Real dashboard — actual production UI

CertFleet SSL certificate fleet dashboard showing valid, expiring, expired and error certificates with days-to-expiry bars

SSL fleet — VALID / EXPIRING / EXPIRED / ERROR states, CT new-cert badge, per-domain issuer and next-check time.

How SSL monitoring works →
CertFleet uptime monitor list showing UP, DOWN and PAUSED monitors with latency and HTTP status codes

Uptime monitors — UP 38ms / DOWN 503 / PAUSED. Per-monitor interval, latency, next check.

Uptime monitoring details →
CertFleet SSL certificate history modal showing issuer, expiry dates and TLS version over time

SSL history — per-check audit trail: issuer, TLS version, days remaining at each probe.

CertFleet uptime check history timeline showing response times and downtime events

Uptime history — chronological checks with response time and detected outage windows.

CertFleet domain expiration monitors showing WHOIS/RDAP registration expiry dates

Domain expiry monitors — WHOIS/RDAP registration dates, alert 60/30/14/7 days before.

CertFleet DNS change monitors showing A, MX, CNAME record tracking with change alerts

DNS monitors — track A, MX, CNAME, TXT records. Alert instantly on any change.

CertFleet public status page showing service component health, uptime bars and resolved incident history

Public status page — hosted at /s/your-slug. Shows component health, 90-day uptime bars and incident history. No login required for viewers.

CertFleet team management showing members with roles, pending invitations and invite form

Team & RBAC — invite members by email, assign owner / admin / member / viewer roles. Invitation email sent automatically.

CertFleet alert settings panel configuring email, Slack and webhook channels per certificate

Per-resource alert config — email, Slack, custom webhook. Choose expiry thresholds: 30/14/7/1 days.

CertFleet API keys management page showing active keys with last-used timestamps

REST API keys — create named keys per integration (CI/CD, Grafana, custom scripts). Full API from Starter.

API docs →
CertFleet status page detail showing components, incident timeline and management options

Status page management — add components (monitors + certs), post incidents, track resolution.

// DATA PROTECTION

RGPD / GDPR compliance

Data controller

MCS, registered under SIREN 106 942 600 (R.C.S. Nantes), EU identification number FR4401.106942600
5Q Avenue des Naudières
44800 Saint-Herblain, France

What data we process
  • Public checker: only the hostname you submit (no personal data, rate-limited + protected by Cloudflare Turnstile).
  • Accounts: email address (from password signup or OAuth Google/GitHub), user ID.
  • Monitored resources: the hostnames, ports, and full URLs you explicitly add to monitor (chosen by you).
  • Alert channels: email addresses, Slack webhook URLs, or custom webhook URLs you configure.
  • Technical data: minimal logs for rate limiting, abuse prevention and debugging (IP addresses are not stored long-term for the public checker).
Purposes & legal basis

We process data only to provide the monitoring and alerting service you requested (contract performance) and to prevent abuse. We do not sell data or use it for advertising.

Where data lives
  • Primary storage: Cloudflare D1 (SQLite) and KV — global but with strong privacy controls.
  • Emails sent via Resend (EU-friendly routing when possible).
  • Passive TLS probe: short-lived requests only (hostname in, certificate details out). No storage of your traffic or content.
  • Certificate Transparency data: fetched from public crt.sh (no personal data involved).
Retention & deletion

Check history and monitors are kept as long as the resource is monitored or until you delete it from your account. You can delete domains, monitors, or your entire account at any time from the dashboard. Alerts are idempotent (we track what was already sent to avoid duplicates).

Your rights (RGPD)

You have the right to access, rectify, erase, restrict processing, data portability, and to object. For any request, contact us at the address above or via the support channels in the app. We will respond within legal timeframes.

Security & design choices
  • Passive TLS reading only — we never scan ports or attempt exploitation.
  • Auth sessions stored in KV with short TTL, httpOnly + Secure cookies.
  • Public checker protected by Turnstile + per-IP rate limiting in KV.
  • No unnecessary third-party analytics or trackers on the landing or app.
Last updated: June 2026. This page is part of our commitment to transparency. For any question about data processing, write to us using the contact details above.

Questions about how we build or protect data?

// EXPLORE MORE