Blame Today
Blame things on blame.today, the anonymous 'who do you blame today' board. Nostr, no account.
Install
Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).
claude mcp add --transport http blame-today 'https://blame.today/mcp'
{
"mcpServers": {
"blame-today": {
"type": "http",
"url": "https://blame.today/mcp"
}
}
}
Documentation
From the project's README on GitHub, refreshed weekly. Links open on GitHub.
blame-web
The blame.today web client — the "raw firehose." A client-side Svelte + Vite app that talks straight to nostr relays: the vote path has no backend, no auth, no rate limit (events go direct to relays). A thin Cloudflare Worker fronts the static site to force https, redirect www -> apex, and serve the MCP pointer at /mcp. This is the open, canonical realization of blame; the iOS app lives in the sibling blame-ios repo.
Split out of blame-today/blame-mobile (the old iOS app + worker repo) so each surface is honestly its own thing — they share a brand and the pureblameapp nostr tag, but no code, build, or deploy.
what it is
- a blame target is a nostr
kind 1event taggedpureblameapp; a vote is akind 7reaction taggede:<targetId>+pureblameapp. throwaway key per event. - the leaderboard is built from relay
COUNTs (all-time + a 24h "hot" window), not by fetching events. - the News tab mines today's headlines client-side (
compromise) into clickable suggestions. - content filter (profanity / PII / gibberish) is the only gate — it's an open firehose.
dev
Votes are signed once per click and retried with the same event id. A new target must be acknowledged before its opening vote is sent. After five failed attempts the board offers a retry with that same signed event. Queued and failed deliveries live only in the current tab; keep it open until pending votes finish.
Counts are relay estimates: the second-highest fresh report from distinct configured relays, separately for each target and window. Fewer than two reports means unavailable, shown as an ellipsis rather than zero. A live event or acknowledgement requests a new snapshot; it never adds to the snapshot, so overlapping delivery cannot count twice. A relay can correct its estimate downward. Samples older than 90 seconds are removed on a new reply or the next 45-second resync, including during silence. This is not an exact union across relays. NIP-45 filters the application tag and event kind but cannot filter reaction content. Incoming board events are separately checked for shape, application tag, canonical id and signature.
COUNT responses are unsigned relay claims. One dishonest relay cannot raise the estimate above every honest reported count; it can still influence the result within that range. Two dishonest relays can corroborate a false count. Partial coverage and unlimited signed voting remain part of this open board, so these estimates are not a verified vote census. One relay can accept a vote while its count awaits another response. Mine preserves the submitted topic. Old cached totals are discarded on reload; saved topics and ownership remain.
Each tab retains at most 1,000 remote targets plus its owned targets. New targets replace the oldest unowned remote target, so a full cached board still receives live topics. Incoming frames are limited to 128 KiB, with at most 1,024 frames / 1 MiB waiting for verification per relay. A relay that overflows that queue is disconnected and retried. Unanswered COUNT requests are capped at 12,000 across the pool and superseded requests are removed. These are client resource limits; they do not change which events relays accept.
npm install
npm run dev # vite dev server
npm test # vitest unit tests
npm run test:e2e # playwright
npm run build # -> dist/
deploy
Push to main → .github/workflows/deploy-web.yml builds dist/ and runs wrangler deploy, publishing to the Cloudflare Worker (blame-today, Workers + Static Assets) that serves blame.today. The worker (worker/index.js) serves dist/ as static assets, force-upgrades http → https, redirects www → apex, and serves the MCP inline at /mcp (worker/mcp.js). Needs CLOUDFLARE_API_TOKEN (Workers:Edit) + CLOUDFLARE_ACCOUNT_ID repo secrets. The /about prose lives in public/about/ and Vite copies it into dist/about/.
Migrated off GitHub Pages 2026-06-16 — the zone is on Cloudflare now and blame.today is a custom domain on the worker. A push touching only scripts/** or infra/** skips the rebuild.
infra (the Cloudflare config around the worker)
The CF "clicky layer" — DNS records, zone settings (HSTS, Always Use HTTPS), the www → apex redirect rule, and email routing (*@blame.today → gmail) — is declarative IaC in infra/ (OpenTofu). wrangler owns the worker code + the apex custom domain; OpenTofu owns everything else, each resource with exactly one owner. See infra/README.md.
Frequently asked questions
What is Blame Today?
Blame things on blame.today, the anonymous 'who do you blame today' board. Nostr, no account.
How do I install Blame Today?
It runs as a remote server. In Claude Code: claude mcp add --transport http blame-today 'https://blame.today/mcp'. For other clients, paste the JSON configuration from this page into their MCP settings.
Does Blame Today need an API key?
Its registry entry lists no required key or secret. Some features may still need an account with the service it connects to.
Is Blame Today open source?
Its code is public on GitHub, but the repository does not declare a standard license.
Who maintains Blame Today?
It is published by blame-today. AI.tn lists it but does not maintain or endorse it.
From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.