Skip to content

Weekly Digest by Email ​

Follow CVEs and packages and receive one email a week about what changed for them. The first successful Monday check sends a bounded initial snapshot; later messages are sent only when something changed, so a quiet week sends nothing. Mail comes from notify@vulnpatch.dev on Mondays at 13:00 UTC. The pages for people are at cve.vulnpatch.dev/notify; this page describes the API behind them.

What can be followed ​

KindKeyWhat the digest reports
cveA CVE id, such as CVE-2024-3094Added to CISA KEV; EPSS rises by 0.1 or more; a fix version first stated; the record's state changes; its CVSS score is revised; a nixpkgs security issue referencing it opens or closes
packageA package name as OSV spells it, optionally with an ecosystemAdvisories that newly name the package, in every ecosystem or the one given

A subscription can follow up to 25 things.

How a change is detected ​

There is no event log. Each follow keeps a reading taken the last time it was checked. The digest is the difference between that reading and one taken now. Before sending, the service queues the exact message and its proposed next reading. Retries inside Resend's 24-hour idempotency window reuse the same provider key and payload. The stored reading advances only after accepted delivery is recorded. If that outcome cannot be settled inside the window, automatic retries stop rather than risk duplicate mail. Mail remains paused after the retained outbox expires. The manage page's Resume email action abandons the old delivery and resumes mail without changing the follows.

The first successful reading is sent as an initial snapshot. Completion is tracked per follow, so one unavailable follow remains pending even when its siblings are delivered. A package snapshot covers the preceding 90 days and shows at most 20 matches across the subscription, ranked by CISA KEV listing, fresh EPSS, severity when current evidence carries it and recency. Distributor advisories are resolved to their upstream CVEs before ranking and deduplication. Their dates remain labelled as advisory dates rather than CVE publication dates. Missing or stale EPSS scores are fetched from FIRST before ranking. If the relationship, KEV or EPSS evidence needed to rank the snapshot remains unavailable, that follow stays pending and the email says it could not be checked. Every retrieved match enters the reading even when it is omitted from the email, so it is not reported later as new. A direct CVE follow shows its current status. An empty snapshot is still sent to confirm that delivery works. The email labels the snapshot as a starting view, not as newly published vulnerabilities.

A reading that cannot be taken is not a reading. The digest lists the follow as unchecked and states the reason. Its stored reading stays where it was. A source the dossier could not reach (KEV, EPSS, nixpkgs) keeps that field's previous value, so an outage neither reports a change nor hides the next one.

Subscribing ​

http
POST /api/v1/notify/subscribe
Content-Type: application/json

{
  "email": "you@example.org",
  "follows": [
    { "kind": "cve", "key": "CVE-2024-3094" },
    { "kind": "package", "key": "openssl" },
    { "kind": "package", "key": "lodash", "ecosystem": "npm" }
  ]
}

A confirmation email is sent to the address. Nothing is followed and nothing else is sent until the link in it is opened (double opt-in); the link works once and for 48 hours.

An address that already receives the digest is not sent a confirmation, since one carrying new follows would let whoever typed the address change what its owner receives. It is sent a link to its manage page instead, and the request's follows go nowhere; the owner adds follows from that page, which only their own link reaches.

The response is identical for a new, a pending, an already subscribed and a limited address, so the endpoint cannot be used to learn which an address is. Each address is sent at most one email an hour and three a day by this endpoint, counted under a hash of the address; past that nothing is sent and the answer does not change. Each client is limited to a few requests a day, an IPv6 client counting by its /64. A deployment that cannot send mail answers 503 and stores nothing.

Confirming ​

http
POST /api/v1/notify/confirm/{token}

Opens the confirmation link. The response carries the subscription and its linkToken, the capability the manage and unsubscribe pages use. Every digest carries the same token in its footer links.

Managing a subscription ​

All of these take the link token and answer with the subscription: its status, what it follows, when it was last checked (lastCheckedAt), when the last snapshot or digest went out (lastDigestAt), whether any current follow still awaits its snapshot (initialSnapshotPending) and whether mail is paused after an uncertain delivery (deliveryIssue). A checked quiet week after the initial snapshot advances only lastCheckedAt. The address is returned masked as the first character and the domain.

GET /api/v1/status publishes the last digest run's aggregate status, sanitized failureCode and delivery counts. It never publishes addresses, follows, tokens or per-subscription errors.

http
GET  /api/v1/notify/subscription/{token}   what the address follows
POST /api/v1/notify/follows/{token}        replace the follows ({ "follows": [...] })
POST /api/v1/notify/unsubscribe/{token}    stop the digest at once
POST /api/v1/notify/resubscribe/{token}    undo an unsubscribe, within 30 days
POST /api/v1/notify/resume/{token}         resume mail after a delivery issue, without touching follows
POST /api/v1/notify/delete/{token}         delete everything held for the address

Unsubscribing in one click ​

Every digest carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers (RFC 8058), naming POST /api/v1/notify/unsubscribe/{token}, so a mail client's own unsubscribe button works without opening a page. The form body the client sends is ignored: the token is the request. The footer link lands on a page that is already unsubscribed on arrival and offers the undo below the confirmation, never in its place.

An unsubscribed address is kept for 30 days so the undo works, then forgotten. Deleting removes the subscription, its stored readings and the address at once.

What is stored ​

The address, the follows and the last reading for each follow. While a message is being delivered, an outbox also holds that exact message and its proposed next reading. If delivery cannot be reconciled inside the provider's deduplication window, the outbox is retained for up to 30 days and mail remains paused after it expires. Resuming email, unsubscribing or deleting removes it sooner. There is no open or click tracking and no preference beyond the follows. The privacy statement says the same in plain words.

Errors ​

StatusMeaning
400The address is not one, or a follow is malformed. Nothing was stored.
404No subscription is reachable through this token: never issued, expired or the address has been forgotten.
409Undo asked for a live subscription.
429Rate limited. Honour Retry-After.
502The confirmation email could not be sent. Nothing takes effect.
503Email subscriptions are not available on this deployment.