Files
kill-the-news/CLAUDE.md
T
Julien Herr db31e33a8b docs: extract install/deploy/config guide into INSTALL.md
Slim README down to project overview (why, features, architecture,
security) with a short Installation quick-start that links to the new
INSTALL.md. Repoint setup.sh references and CLAUDE.md maintenance list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 21:49:19 +02:00

7.5 KiB

CLAUDE.md

This file provides guidance to Claude Code when working in this repository.

Commands

npm install           # Install dependencies (also builds client scripts via prepare)
npm run dev           # Start local dev server (wrangler dev)
npm test              # Run all tests once
npm run test:watch    # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run build         # Dry-run deploy bundle (wrangler deploy --dry-run)
npm run build:client  # Compile client scripts only (src/scripts/client → src/scripts/generated)
npm run deploy        # Deploy to Cloudflare production
npm run format        # Format with Prettier

Run a single test file:

npx vitest run src/routes/admin.test.ts

Project summary

kill-the-news is a Cloudflare Worker that ingests email newsletters and exposes them as private RSS/Atom feeds. Self-hosted, free-tier-friendly (Cloudflare + ForwardEmail).

Architecture

Single Cloudflare Worker built with Hono. Routes:

Method Path Purpose
GET / Public status page (monitoring counters + link to admin)
POST /api/inbound Webhook from ForwardEmail; IP-allowlisted to their MX sources
GET /api/stats Public monitoring counters (JSON)
GET /rss/:feedId Public RSS 2.0 feed
GET /atom/:feedId Public Atom feed (with WebSub hub header)
GET /entries/:feedId/:entryId Individual email HTML view
GET /files/:attachmentId/:filename R2 attachment serving
GET /admin Password-protected admin UI
/hub WebSub hub (subscribe/publish)
GET /favicon.svg, /favicon.ico Project favicon (envelope logo); fallback for per-feed favicons
GET /favicon/:feedId Per-feed favicon from the last sender's domain (falls back to project)
GET /health Health check
email Cloudflare Email routing handler (alternative to ForwardEmail webhook)

Source layout

src/
  index.ts                  # App entrypoint: CORS, IP middleware, route mounting, email handler export
  config/constants.ts       # Shared constants (TTLs, limits)
  types/index.ts            # Env, FeedConfig, EmailData, WebSubSubscription, etc.
  routes/
    inbound.ts              # ForwardEmail webhook handler
    rss.ts                  # RSS feed renderer
    atom.ts                 # Atom feed renderer
    entries.ts              # Single email HTML view
    files.ts                # R2 attachment serving
    hub.ts                  # WebSub hub
    home.tsx                # Public status page (GET /)
    stats.ts                # Monitoring counters API (GET /api/stats)
    admin.tsx               # Admin UI entrypoint (hono/jsx)
    admin/                  # Admin sub-modules
      feeds.tsx             # Feeds CRUD UI
      emails.tsx            # Emails list/delete UI
      ui.tsx                # Shared UI components
      helpers.ts            # Shared admin helpers
  lib/
    cloudflare-email.ts     # Cloudflare Email routing handler
    email-parser.ts         # Email parsing (mailparser)
    email-processor.ts      # Core ingestion logic (parse → store)
    feed-fetcher.ts         # KV feed/email fetch helpers
    feed-generator.ts       # RSS/Atom XML generation
    forwardemail.ts         # ForwardEmail webhook types/parsing
    id-generator.ts         # Feed/entry ID generation
    logger.ts               # JSON structured logger
    storage.ts              # KV key helpers
    websub.ts               # WebSub subscription management
    worker.ts               # Typed worker export helper
  scripts/
    client/                 # TypeScript client scripts (compiled by esbuild)
      dashboard.ts          # Admin dashboard interactions
      emails-page.ts        # Emails page interactions
    generated/              # Compiled output (gitignored, rebuilt on npm install)
  styles/                   # CSS files bundled into the Worker
    variables.css
    layout.css
    components.css
    utilities.css
  data/nouns.ts             # Word list for ID generation
  test/setup.ts             # Test mocks: MockKV, createMockEnv()

KV schema

All data lives in the EMAIL_STORAGE KV namespace:

Key Value
feeds:list { feeds: Array<{ id, title, description? }> }
feed:<feedId>:config FeedConfig
feed:<feedId>:metadata { emails: Array<{ key, subject, receivedAt, size?, attachmentIds? }> }
feed:<feedId>:<timestamp> Full EmailData
websub:<feedId>:<callbackHash> WebSubSubscription
stats:counters Counters (cumulative monitoring counters singleton)

src/lib/storage.ts contains key-builder helpers — use them; don't inline key strings in routes.

Worker bindings (Env)

EMAIL_STORAGE: KVNamespace;    // All feed/email data
ADMIN_PASSWORD: string;        // Worker secret — never in config files
DOMAIN: string;                // e.g. "getmynews.app"
ATTACHMENT_BUCKET?: R2Bucket;  // R2 for email attachments
FEED_MAX_SIZE_BYTES?: string;  // Optional email size cap
PROXY_TRUSTED_IPS?: string;    // Trusted reverse-proxy IPs
PROXY_AUTH_SECRET?: string;    // Shared secret for proxy auth

Client scripts

src/scripts/client/ contains TypeScript that runs in the browser. It is compiled by esbuild into src/scripts/generated/ (gitignored) and bundled into the Worker as inline <script> tags. The prepare npm hook rebuilds them on npm install. Run npm run build:client to rebuild manually.

Testing

Tests run in Node (not a Worker runtime). Hono test requests pass the mock env as the 3rd argument:

const res = await app.request("/path", init, createMockEnv());

MSW (msw/node) handles external HTTP mocks. Tests that hit validation paths intentionally produce stderr output — expected.

Configuration

  • wrangler.toml is generated locally from wrangler-example.toml by setup.sh — do not commit it
  • ADMIN_PASSWORD is set via wrangler secret put — never in config files
  • Keep compatibility_date current on runtime upgrades

When changing behavior

Update together:

  • README.md
  • INSTALL.md (setup, deployment, and configuration guide)
  • AGENTS.md
  • setup.sh (if setup/deploy assumptions changed)
  • Tests under src/routes/*.test.ts and src/test/setup.ts