Product

Newsroom

World news, mapped where it happened, read aloud on a schedule.

v0.2 — self-hosted, running

newsroom / by the numbers

  • 715automated tests across 36 files, none needing a database or an API key
  • ~95%of stories carry a real, credited publisher photograph — never a generated one
  • 8platform post formats per bulletin, every one held for human approval

Ingests twenty world news feeds, summarizes each story into labelled facts, plots it where it happened, and produces a spoken broadcast with per-platform posts — that never publish themselves.

The challenge

Automated news summarization has two failure modes that matter more than quality. The first is quiet fabrication: about a third of RSS items ship no usable body at all — some publishers put a bare <img> tag in <content> and nothing else — which leaves a summarizer working from a headline and padding the result with restatements of the same fact until it hits a bullet count. The second is that the output format of automated news is also the output format of automated disinformation, and building the photoreal version of this makes you a tooling vendor for it.

What we built

A self-hosted Next.js application over Postgres and Prisma, with MapLibre for the map, node-cron for scheduling, and a provider adapter for AI.

Facts are labelled and sourced. Each bullet is marked fact or attributed, carries its source, a topic, a country and a map position. Ingest falls through three body sources — the feed snippet, the article’s real paragraphs minus boilerplate, then schema.org articleBody — in one request that also collects the photograph. The prompt requires each bullet to carry information no other bullet carries, and to return fewer rather than pad.

The presenter is deliberately not photoreal. It is a drawn figure, nobody in particular, carrying a permanent non-dismissible “AI PRESENTER” mark, imitating no real broadcaster’s branding. It lip-syncs from SpeechSynthesis boundary events through a viseme set, gestures on emphasis scoring, and moves on damped springs rather than linear interpolation — which is what stopped it reading as robotic. It is fully deterministic: no Math.random, so the same script always produces the same performance. Story pictures are never generated; each is the outlet’s own photograph, credited on screen and linked back.

Nothing spends money or goes outward by itself. Reading is public; ingest, broadcast, retell, edit and publish all require a token exchanged for a signed httpOnly session. The scheduler builds bulletins but will not render audio, because audio costs money — a ~2,100-character bulletin runs about $0.32, and it waits for a human. Drafts never publish themselves. In webhook mode the app POSTs approved posts to the operator’s own automation, so platform credentials never live in this app; internal and plaintext URLs are refused. Every draft is validated against its platform’s real limits — threaded on X and Mastodon, truncated elsewhere, parked as rejected with a reason when it cannot be made valid.

Capabilities this demonstrates

  • AI & Automation — multi-provider adapter, keyless mock path, bounded spend, responsible synthetic media
  • Secure Software Engineering — SSRF-bounded feed allowlist, token/session auth, rate limits, body caps, tenancy isolation