sec-daily-digest

March 7, 2026 ยท View on GitHub

English is the primary README. Chinese version: README.zh-CN.md.

sec-daily-digest fetches recent articles from CyberSecurityRSS OPML feeds and Twitter/X security / AI KOL accounts, scores and filters them (AI-first with rule fallback), deduplicates against historical archives, merges vulnerability events, monitors source health, and generates a bilingual daily markdown digest for cybersecurity researchers.

๐Ÿ’ฌ One-Liner

Tell your AI assistant:

"Generate today's cybersecurity digest, focus on vulnerabilities and APT activity"

The assistant fetches, scores, deduplicates, and renders a full Markdown report โ€” hands-free.

More examples:

๐Ÿ—ฃ๏ธ "Use Claude to analyze today's security news, output to ./output/digest.md"

๐Ÿ—ฃ๏ธ "Generate this week's security roundup, skip Twitter, focus on CVEs"

๐Ÿ—ฃ๏ธ "Full-text enrichment with Gemini scoring, email to me@example.com"

Or via CLI:

bun scripts/sec-digest.ts --dry-run --output ./output/digest.md

๐Ÿ“Š What You Get

An AI-scored, deduplicated cybersecurity digest from dual data sources (security RSS + Twitter/X researchers):

LayerScaleContent
๐Ÿ“ก RSS (tiny)~50 feedsCyberSecurityRSS curated โ€” vulns, threat intel, malwareโ€ฆ
๐Ÿ“ก RSS (full)400+ feedsFull CyberSecurityRSS OPML (--opml full)
๐Ÿฆ Twitter/X15 researchersTavis Ormandy, Brian Krebs, Kevin Beaumont, Marcus Hutchinsโ€ฆ

Pipeline

RSS fetch + Twitter KOL fetch (parallel)
        โ†“
  dedup + time filter โ†’ archive penalty (seen before โˆ’5)
        โ†“
  full-text enrich (optional) โ†’ AI scoring + classification โ†’ AI summary + translation
        โ†“
  vulnerability merge (CVE-first + semantic clustering)
        โ†“
  render Markdown digest โ†’ email delivery (optional)

Quality scoring: priority source (+3), archive penalty (โˆ’5), keyword weights, recency.

Highlights

  • TypeScript + Bun runtime, no new npm dependencies
  • Dual source monitoring: CyberSecurityRSS OPML + Twitter/X security KOLs (parallel fetch)
  • Historical dedup: articles seen in the past 7 days receive a โˆ’5 score penalty; archive auto-cleans after 90 days
  • Source health monitoring: tracks per-source fetch failures; surfaces unhealthy sources in digest footer
  • Full-text enrichment (--enrich): fetches article body before AI scoring for better classification
  • Email delivery (--email): sends digest via gog
  • Mandatory OPML update check before each run
    • Default profile: tiny.opml
    • Optional profile: CyberSecurityRSS.opml (--opml full)
    • On remote check failure: continue with cached OPML
  • Explicit provider selection: --provider openai|gemini|claude|ollama (default: openai)
  • Balanced ranking: Security 50% + AI 50%
  • Score display: ๐Ÿ”ฅN (integer, 1โ€“10 range)
  • Vulnerability merge: CVE-first + semantic clustering fallback
  • Output sections:
    • ## ๐Ÿ“ ไปŠๆ—ฅ่ถ‹ๅŠฟ โ€” macro trend highlights
    • ## ๐Ÿ” Security KOL Updates โ€” Twitter/X KOL tweets (when credentials present)
    • ## AIๅ‘ๅฑ• โ€” AI/LLM articles
    • ## ๅฎ‰ๅ…จๅŠจๆ€ โ€” security articles
    • ## ๆผๆดžไธ“ๆŠฅ โ€” merged vulnerability events
    • ## โš ๏ธ Source Health Warnings โ€” unhealthy sources (when detected)

Config and State

Persistent directory: ~/.sec-daily-digest/ (override with SEC_DAILY_DIGEST_HOME)

File / DirectoryDescription
config.yamlMain config (provider, hours, top_n, weightsโ€ฆ)
sources.yamlTwitter/X KOL list + custom RSS sources
health.jsonPer-source fetch health history
archive/YYYY-MM-DD.jsonDaily article archive for historical dedup
twitter-id-cache.jsonTwitter user ID cache (official API only, 7-day TTL)
opml/tiny.opmlCached tiny OPML
opml/CyberSecurityRSS.opmlCached full OPML

Quick Start (CLI)

cd /path/to/sec-daily-digest
bun install

# Dry run (no AI, no Twitter)
bun scripts/sec-digest.ts --dry-run --output ./output/digest.md

# With AI scoring
OPENAI_API_KEY=sk-... bun scripts/sec-digest.ts \
  --provider openai --opml tiny --hours 48 --output ./output/digest.md

# With Twitter KOLs
TWITTERAPI_IO_KEY=your-key bun scripts/sec-digest.ts \
  --provider claude --output ./output/digest.md

# Weekly mode
bun scripts/sec-digest.ts --mode weekly --provider openai --output ./output/weekly.md

# Full-featured
TWITTERAPI_IO_KEY=your-key bun scripts/sec-digest.ts \
  --provider claude --enrich --email me@example.com \
  --output ./output/digest.md

CLI Options

FlagDescriptionDefault
--provider <id>openai|gemini|claude|ollamaopenai
--opml <profile>tiny|fulltiny
--hours <n>Time window in hours48
--mode <daily|weekly>Shortcut: daily=48h, weekly=168hโ€”
--top-n <n>Max articles to select20
--output <path>Output markdown file path./output/sec-digest-YYYYMMDD.md
--dry-runRule-based scoring only (no AI calls)false
--no-twitterDisable Twitter/X KOL fetchingfalse
--email <addr>Send digest via gogโ€”
--enrichFetch full text before scoringfalse
--helpShow helpโ€”

Environment Variables

AI Providers

OpenAI (default):

  • OPENAI_API_KEY (required)
  • OPENAI_API_BASE (optional)
  • OPENAI_MODEL (optional)

Gemini:

  • GEMINI_API_KEY (required)
  • GEMINI_MODEL (optional)

Claude:

  • ANTHROPIC_API_KEY (required)
  • CLAUDE_MODEL (optional)
  • CLAUDE_API_BASE (optional)

Ollama:

  • OLLAMA_API_BASE (optional, default http://127.0.0.1:11434)
  • OLLAMA_MODEL (optional)

Twitter/X

VariableDescription
TWITTERAPI_IO_KEYtwitterapi.io key โ€” preferred, 5 QPS
X_BEARER_TOKENOfficial Twitter API v2 bearer token
TWITTER_API_BACKENDtwitterapiio|official|auto (default: auto)

Backend selection logic:

  • TWITTERAPI_IO_KEY set โ†’ use twitterapi.io
  • Only X_BEARER_TOKEN set โ†’ use official API
  • Neither set โ†’ Twitter silently disabled (no crash)

Other

VariableDescription
SEC_DAILY_DIGEST_HOMEOverride state directory (default: ~/.sec-daily-digest)

Configuring RSS Sources

RSS feeds come from CyberSecurityRSS OPML files (synced automatically). To add custom RSS sources on top of OPML, edit ~/.sec-daily-digest/sources.yaml:

sources:
  - id: my-blog
    type: rss
    name: "My Security Blog"
    url: "https://myblog.example.com/feed.xml"
    enabled: true
    priority: false
    topics:
      - security
    note: "Personal blog, check weekly"

RSS Source Field Reference

FieldTypeRequiredDescription
idstringyesUnique identifier across all sources. If it matches a default source ID, the entire default entry is replaced by this one.
type"rss"yesMust be "rss" for RSS/Atom feed sources.
namestringyesHuman-readable display name โ€” shown in source attribution lines and health warning reports.
urlstringyesFull URL of the RSS or Atom feed to fetch.
enabledbooleanyesSet to false to disable this source without deleting the entry. For default sources, a minimal {id, enabled: false} entry is sufficient.
prioritybooleanyesIf true, articles from this source receive a +3 quality score bonus, helping them surface above lower-priority sources with similar content. Use for high-signal feeds you always want represented.
topicsstring[]yesTopic tags for metadata and categorization. Example values: security, ai, exploit, malware. Currently used for labeling; scoring is keyword-based.
notestringnoFree-text description for your own reference. Not used in scoring or output.

Configuring Twitter/X KOL Accounts

On first run, ~/.sec-daily-digest/sources.yaml is auto-created with 15 default security researchers (Tavis Ormandy, Brian Krebs, Kevin Beaumont, Marcus Hutchins, etc.).

Disable a default account

Provide only id + enabled: false โ€” no other fields needed:

sources:
  - id: thegrugq
    enabled: false

Add a new account

sources:
  - id: myresearcher
    type: twitter
    name: "My Researcher"
    handle: myresearcher
    enabled: true
    priority: false
    topics:
      - security
    note: "Tracks APT campaigns"

Replace a default account's config

Provide a full entry with the same id to override all fields:

sources:
  - id: taviso
    type: twitter
    name: "Tavis Ormandy"
    handle: taviso
    enabled: true
    priority: true
    topics:
      - security
      - exploit
    note: "Google Project Zero"

Twitter Source Field Reference

FieldTypeRequiredDescription
idstringyesUnique identifier across all sources. Must not collide with RSS source IDs. If it matches a default Twitter source ID, the entire default entry is replaced.
type"twitter"yesMust be "twitter" for Twitter/X account sources.
namestringyesDisplay name shown in the ๐Ÿ” Security KOL Updates section of the digest. Can differ from the Twitter display name.
handlestringyesTwitter/X username without the @ prefix. Example: briankrebs (not @briankrebs).
enabledbooleanyesSet to false to stop fetching this account. A minimal {id, enabled: false} entry disables a default source without needing all fields.
prioritybooleanyesIf true, tweets from this account receive a +3 quality score bonus in the article ranking pipeline. Useful for accounts whose tweets you always want in the AIๅ‘ๅฑ• / ๅฎ‰ๅ…จๅŠจๆ€ sections.
topicsstring[]yesTopic tags for metadata. Does not affect fetching โ€” all tweets from an enabled account are fetched regardless.
notestringnoFree-text description for your own reference. Not used in scoring or output.

Merge behavior: On each run, the default 15-account list is merged with your sources.yaml. Entries with matching id replace defaults; new ids are appended. This means new default accounts added in future releases will appear automatically โ€” unless you explicitly disable them.

Email Delivery

Requires gogcli โ€” a Gmail CLI that uses the official Gmail API:

# Install (macOS)
brew install steipete/tap/gogcli

# Authenticate (one-time)
gog auth login

# Send digest via email
bun scripts/sec-digest.ts --provider claude --email me@example.com --output ./output/digest.md

Under the hood, the --email flag calls:

gog gmail send --to <addr> --subject "sec-daily-digest YYYY-MM-DD" --body-file -

cron Integration

# Daily at 07:00
0 7 * * * cd /path/to/sec-daily-digest && \
  bun scripts/sec-digest.ts --mode daily --output ~/digests/sec-$(date +\%Y\%m\%d).md \
  2>&1 | tee -a ~/.sec-daily-digest/cron.log

# Weekly on Monday at 08:00
0 8 * * 1 cd /path/to/sec-daily-digest && \
  bun scripts/sec-digest.ts --mode weekly --opml full \
  --output ~/digests/weekly-$(date +\%Y\%m\%d).md \
  2>&1 | tee -a ~/.sec-daily-digest/cron.log

Install This Skill

Set source path:

SKILL_SRC="~/z3dev/Skills/sec-daily-digest"

OpenClaw

clawhub install sec-daily-digest

Claude Code

Install as a personal skill:

mkdir -p ~/.claude/skills
ln -sfn "$SKILL_SRC" ~/.claude/skills/sec-daily-digest

Or project-local:

mkdir -p ./.claude/skills
ln -sfn "$SKILL_SRC" ./.claude/skills/sec-daily-digest

Codex

mkdir -p ~/.agents/skills
ln -sfn "$SKILL_SRC" ~/.agents/skills/sec-daily-digest

OpenCode

User-level:

mkdir -p ~/.config/opencode/skills
ln -sfn "$SKILL_SRC" ~/.config/opencode/skills/sec-daily-digest

Project-level:

mkdir -p ./.opencode/skills
ln -sfn "$SKILL_SRC" ./.opencode/skills/sec-daily-digest

Run as a Skill

/sec-digest

Tests

bun test