Skip to main content

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

![broadcast](https://directive.run/badge/<your-project-slug>.svg)

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 === 0 or no entry): a quiet grey broadcast: ready badge.
  • Active (count >= 1): broadcast: N posts · Yago where 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.

Previous
MCP server

Stay in the loop. Sign up for our newsletter.

We care about your data. We'll never share your email.

Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works

Directive - Constraint-Driven Runtime for TypeScript | AI Guardrails & State Management