A daily email of the top 5 Hacker News front-page stories, each with a summary of the linked page and a summary of what the top comments are arguing about.
Source is the official HN Firebase API — keyless and unauthenticated. Two cron jobs:
| File | Schedule | Job |
|---|---|---|
collect.ts | hourly, :07 | Record what's on the front page |
main.ts | 07:00 UTC | Pick the top 5 and email them |
topstories.json is a live snapshot, so a single daily poll would miss anything that
peaked overnight and fell off. The collector records every front-page story hourly into
hn-digest:pool, keeping high-water marks for score and comment count. The digest then
picks the highest peak score of the past 36 hours — a genuine top-of-day, not
whatever happened to be up at 07:00.
pick() takes the 5 highest-peak-score pool entries not already emailed.kids list. kids is in HN's own
rank order — comments carry no score in the API, so this is the only signal for "top
comment". Algolia's children is chronological and hnrss.org/item?id= is
reverse-chronological, so neither can rank.article.ts fetches the linked page and extracts text. Failures (paywalls, JS-only
pages, video, PDFs) leave the summary leaning on the comments, and it says so. Ask/Show
HN self-posts use the post body instead.summarize.ts makes one Claude call per story. No comments means no discussion section
rather than an invented one. The discussion comes back as a framing line plus labelled
bullets, parsed into Discussion { lead, points[] } — the shape follows the thread, so
a genuine disagreement gets opposing bullets while a pile of corrections doesn't get
forced into a fake two-sided split. parseDiscussion falls back to keeping the prose as
a single unlabelled point if the model returns no bullets.render.ts builds the HTML + plain-text email, sent via std/email.| Key | Required | Purpose |
|---|---|---|
ANTHROPIC_API_KEY | yes | Claude API |
DIGEST_TO | no | Recipient. Defaults to the val owner's address. |
DIGEST_DRY_RUN | no | 1 logs the email instead of sending it. |
Five Claude calls a day on claude-sonnet-5 — roughly $2/month.
claude-haiku-4-5 was measured against Sonnet on identical input for the same five
stories and lost on specifics: it missed the Microduck's motor count, loop rate and
licence, missed three of the six argument threads on the Cloudflare post, and got the
Reachy Mini licence wrong. Haiku costs about $1/month less. Not worth it here, but the
model argument to summarize() makes the comparison easy to re-run.
The collector makes ~31 HN API calls an hour; the digest makes ~5 × 31. All free.
Knobs at the top of main.ts: DIGEST_SIZE, MIN_SCORE, MAX_AGE_HOURS,
TOP_COMMENTS, REPLIES_PER_COMMENT, ARTICLE_MAX_CHARS. WATCH_TOP and POOL_TTL_DAYS are in
collect.ts. Prompt and tone live in SYSTEM in summarize.ts.