GirderPersonal projects by Abhinav Roy

Projects · personal project · October 2026 to date

How I built Industrial Cyber Brief

A daily OT/ICS threat briefing that reads the news so plant managers, OT engineers and security teams do not have to. AI does the reading and the first draft; published rules decide what goes out; the editor stays in charge.

Today's issue · Archive · Methodology · Executive report

The problem

Industrial security news is scattered across government CERTs, vendor advisories, trade press and threat research. Most of it is written for IT. The people who run plants need three things from it: does this touch my site, how bad is it, and what do I do this week if I cannot patch. I was reading dozens of sources before my first meeting each day to answer those questions for myself. The Brief does it once, in public, for everyone.

What I built

Two pieces. A pipeline that wakes up at 10:00 IST, reads the sources, decides what matters and sends the email. And a website that shows the same issue, keeps the archive and gives each story a page that can be shared. Every story carries three facts stated by the source, then a clearly labelled editor's reading: the Purdue level and why, who should act, how to check exposure, what to do if you cannot fix it yet, what to watch for, and the MITRE ATT&CK for ICS techniques involved.

Industrial Cyber Brief, system overview cron-job.org starts a scheduled CI run at 10:00 IST. The run collects 30 public feeds and the CISA known-exploited list, removes items it has seen before using a file in the newsletter repository, enriches, triages with Claude Haiku, selects the top ten, writes with Claude Sonnet, renders the email and website data, sends through Buttondown with Gmail as a fallback, saves its state and archive back to the repository, and opens an update on the website repository. That update passes automatic checks and is deployed by Cloudflare Pages to brief.girder.in for web readers. Email readers receive the issue from Buttondown. cron-job.org Daily at 10:00 IST Mondays 10:00 IST: weekly THE ALARM CLOCK Public sources 30 feeds: CERTs, vendor advisories, OT press, threat research + CISA exploited list Newsletter repo state/seen.json archive/ (every issue) THE MEMORY · GIT Newsletter engine Scheduled CI pipeline · about 5 minutes 1Collect · 30 feeds + known-exploited list 2Dedupe · skip anything seen before 3Enrich · CVE ids, exploited?, CVSS 4Triage · Claude Haiku rates every item 5Select · published rules, top ten 6Summarise · Claude Sonnet writes 7Render · email + website data 8Send · to every subscriber 9Remember · save state and archive 10Hand off · update the website Anthropic API Haiku: triage Sonnet: writing public news text only THE ANALYST Buttondown subscriber list, delivery, unsubscribe handling fallback: Gmail SMTP Email readers Web readers brief.girder.in today · archive · weekly Cloudflare Pages global CDN, static files also serves girder.in Website repo today's data update, automatic checks, merged when they pass writes deploys
Outside service My code and data AI model step Readers On a phone, scroll the diagram sideways.

How a morning runs

  1. Collect. 30 public feeds, plus CISA's list of vulnerabilities known to be exploited.
  2. Dedupe. Anything seen on an earlier day is dropped.
  3. Enrich. CVE ids pulled out, matched against the exploited list; CVSS kept only when the source states it.
  4. Triage, with a small fast model. Is it relevant to industrial sites, how severe, which vendors and sectors, what kind of action.
  5. Select, by rule. Severity floor, exploited first, official advisories before commentary, top ten.
  6. Write, with a stronger model. The three facts and the editor's reading, in structured fields, checked against fixed vocabularies.
  7. Render and send. HTML and plain-text email, with a fallback mail route if the newsletter service fails.
  8. Remember and publish. State and archive saved as a commit; the website update opens, passes its checks, merges and deploys itself.

Design decisions

Facts and inference kept apart

The model may only restate what the source says in the three facts. Anything it infers sits in a separate, labelled block. Scores are quoted, never estimated.

Rules in code, not in the prompt

What gets selected is decided by published rules in ordinary code. The model rates; it does not choose.

No server, no database

The pipeline runs once a day and saves its memory to git. Nothing to patch, nothing listening, full history and rollback for free.

Fail closed

If the AI provider fails, nothing is sent. If the website fails a check, the email still goes and the site keeps yesterday's issue.

Every change is gated

100+ automated tests, accessibility checks, a strict content security policy and secret scanning run before anything reaches readers.

Small models where they suffice

Triage uses the cheapest model on every item; the stronger model only writes the ten that made the cut.

Stack

PythonAnthropic Claude (Haiku, Sonnet)Structured outputsJinja email templatesScheduled CICloudflare PagesStatic HTML, CSS, JavaScriptpytest, Playwright, axeButtondown

The rules it keeps

What I learned, and what is next