Skip to content
← Back to portfolio

Project case study · Web system

TL;DR for X

A personal Manifest V3 Chrome extension that scans the X Following timeline and summarizes it through a Node server running the Claude Code CLI.

Chrome ExtensionJavaScriptNode.jsAIClaude
TL;DR for X

The idea

TL;DR for X is a personal Chrome extension built against Manifest V3. On X.com’s “Following” timeline, it auto-scrolls, captures posts, dedupes them by tweet ID, and folds reposts into one record with a reposters list. On subsequent runs, a saved tweet marker acts as the cursor. The summarizer returns Markdown with themed sections and a “Worth opening” list of tweet IDs; the slide-in panel makes those recommendations actionable by jumping back to the matching tweet. Per-tweet like/dislike feedback supplies content examples for the next run.

The summarizer boundary

The part I care about most is the boundary around the summarizer. The extension calls a companion Node.js service on a personal VPS, not Cloudflare, through a small Bearer-token-authenticated API with /summarise and /feedback endpoints. The server refuses to boot without TLDR_TOKEN. It validates the complete Bearer ... header with a length check followed by Node’s timing-safe comparison.

Instead of integrating an LLM API directly, the service invokes the local Claude Code CLI by default as spawn('claude', ['-p']) and pipes the assembled prompt over stdin. It enforces a five-minute process timeout: SIGTERM first, then SIGKILL after a five-second grace period. The browser request has the same five-minute abort limit, so a stuck CLI or server request can return to a recoverable error state.

The prompt-level injection defense has two explicit boundaries. Both scraped tweet text and the <taste> feedback block are labeled untrusted data, and any directives inside them are treated as content to ignore rather than commands. Taste feedback is content-only: the <taste> block carries liked/disliked tweet text, not author identity, because the same account can publish both liked and disliked posts. That keeps the preference signal about what a post says, not who posted it.

This is a personal, Chrome-only tool that I sideload through “Load unpacked.” I never published it to the Chrome Web Store. The repository includes a small Node test suite and a self-authored AUDIT_FINDINGS.md covering bugs, regressions, reliability, and UX. It is explicitly not a security-hardening, test-coverage, or maintainability audit.

Scroll for more →

Architecture

Extension: Manifest V3 content scripts handle scrolling, scraping, deduplication, the slide-in panel, and per-tweet feedback.

Backend: A Bearer-token-authenticated Node.js server on a personal VPS exposes /summarise and /feedback.

Safety and review: The service uses timing-safe Bearer-token validation and refuses to start without a configured token. It has a two-boundary prompt-injection policy for tweet and taste data, a bounded Claude process, tests, and an audit document explicitly scoped to bugs, reliability, and UX.

Have a project in mind?

Tell me what you're working on and where you need help.