How it works
How each issue gets from a scheduled Claude task to a page on this site, with no AWS credentials involved on the Claude side.
The short version
Every week a scheduled Claude task writes the digest and emails it as usual. It then sends a second copy to a dedicated inbox address. AWS receives that email, checks who sent it and what it contains, and only then turns it into a static web page. The email is the only way in: there is no login, no API key and no upload form.
The pipeline
-
Claude Write and send
A scheduled Claude task researches the week’s AWS announcements, groups them by Well-Architected pillar and writes the digest. It emails the normal digest to its readers, then sends the same content to a dedicated, unpublished inbox address with the subject
AWS Weekly Digest – YYYY-MM-DD. Claude holds no AWS credentials at any point. -
SES Receive and verify
Amazon SES accepts mail for that one address only, over TLS. It scans for spam and viruses and checks SPF, DKIM and DMARC. It stores the raw message in a private inbox bucket, then invokes the publisher function. The bucket deletes raw mail after 30 days.
-
Lambda Gate
Before anything is published, the publisher function checks that:
- the spam, virus, SPF, DKIM and DMARC verdicts all passed;
- the From header is exactly the one allowed sender;
- the subject has the expected form and a real date;
- the message has an HTML body under 2 MB with at least one recognisable item.
If any check fails, nothing is published and an alert goes out (see below).
-
Lambda Clean and build
The function unwraps Gmail’s tracking links and rebuilds the digest’s structure from the email. Every piece of email HTML passes through a strict allow-list sanitiser (nh3), so only plain formatting survives. Page structure, item anchors and copy buttons are then generated from trusted templates, not copied from the email.
-
S3 Write the pages
The function saves the issue page and its data, updates the issue list with a conditional write so two publishes can’t overwrite each other, and regenerates the archive, the all-changes page and the pillar pages.
/latest/is updated if this is the newest issue. -
CloudFront Refresh and serve
The function invalidates the changed paths so the new issue is visible within minutes. Readers are served static files from a private S3 bucket through CloudFront, with a strict content security policy. A few small same-origin scripts add optional conveniences (search, copy buttons, opening an article from a link); every page works without them.
When something goes wrong
- Rejected email
- Failed verification or a wrong sender, subject or content: nothing is published and an alert is sent. The reason is logged, but email bodies never are.
- Unexpected error
- The function is retried twice. If it still fails, the failure is sent to the same alert topic and a CloudWatch alarm fires.
- Design changes
- Every page is re-rendered from saved issue data when the code or templates change, so a redesign never needs an issue to be re-sent.
Design choices
- Email as the only door. A compromised or misbehaving task can, at worst, send a rejected email.
- Static output. Readers only ever fetch pre-built files, so there is no server-side code to attack.
- Untrusted input. Email content is sanitised and re-templated. It is never published as-is.
- Everything as code. The whole stack is Terraform, so it can be rebuilt from the repository.
See the latest issue for the result, or browse all issues.