Broadcast
•2 min read
README badge — adoption signal with N=0 suppression
The badge is the entry point. Projects discover @sizl/broadcast through a peer's README, see the badge, click through to the kill log, adopt.
Embed

The slug is constrained to [a-z0-9-]{1,64} so the cache key is bounded and the public path is safe.
States
- Ready (
count === 0or no entry): a quiet greybroadcast: readybadge. - Active (
count >= 1):broadcast: N posts · Yagowhere the relative time bucket is the largest unit (now,Xm ago,Xh ago,Xd ago,Xmo ago,Xy ago).
The N=0 suppression is deliberate. Showing "0 posts" advertises an empty room. "ready" advertises a deployed integration that hasn't yet fired. The badge is an adoption signal — not an analytics readout — and these are different stories.
Cache behavior
The badge response sets:
content-type: image/svg+xml; charset=utf-8
cache-control: public, max-age=300
access-control-allow-origin: *
vary: accept-encoding
README readers tolerate up to 5 minutes of staleness. The * CORS header lets the badge embed in arbitrary contexts (gists, GitHub Pages, blog posts) without round-tripping through GitHub's image proxy. The Worker also falls back to the ready badge if the KV read throws — README embeds keep rendering during a KV outage.
How counts arrive
Every successful Broadcaster.inject() call POSTs a cassette envelope to broadcast.directive.run/posts. That posts-index Worker updates the per-project BADGE_COUNTS KV entry: count++, lastBroadcastAt = signedAt. The badge Worker reads from the same KV namespace, so the badge always trails the kill-log by one cache TTL.
Cassette envelopes are Pluck-signature-verified before the posts index accepts them — a forged badge bump without the matching cassette is rejected at the write boundary.
Accessibility
The SVG sets role="img" and aria-label="broadcast: <state>" so screen readers narrate the badge correctly. Visual text is duplicated in the title element for hover affordance.

