DEV Community

Cover image for Show HN: Pulse – A changelog that AI agents can verify (signed feed + MCP)
win und
win und

Posted on

Show HN: Pulse – A changelog that AI agents can verify (signed feed + MCP)

Pulse — the changelog your AI agents can verify

Building Pulse: A Changelog That Your AI Agents Can Actually Verify

Six months, one developer, one weird idea: what if your changelog didn't need humans to trust it — it just verified itself?

The graveyard of indie changelogs

I've shipped 50+ "v1.0.1 — fix" releases in my life. So has every indie dev I know.

Look at any GitHub Releases page for a small SaaS and you'll see:

  • "v1.0.1 — minor fix"
  • "v1.0.2 — small fix"
  • "v1.0.3 — fix typo"

Nobody reads them. Not users. Not AI agents. Not you, six months later when you're trying to remember what actually shipped.

I asked 20 indie founders: do you have a real changelog?

  • 12 pointed at GitHub Releases and shrugged
  • 6 had abandoned changelogs after the first month
  • 2 had paid for Beamer and quit

Zero had something their users' AI agents could actually read.

That gap became Pulse.


What Pulse actually is

Pulse is a hosted changelog service for indie developers, SaaS founders, and small product teams. It gives you everything you need to ship a beautiful, AI-friendly changelog in 5 minutes:

  • A Markdown editor with live preview, drafts, and a tag picker
  • A polished public page at pulse.muuc.cn/c/{slug} — branded, grouped by month, with RSS + JSON feeds
  • A drop-in embed widget — one <script> tag renders your latest updates inside your own site
  • Email + Slack notifications with double opt-in for subscribers
  • An MCP endpoint so Cursor, Claude Desktop, and other AI agents can read your changelog directly
  • A Trust Score + public leaderboard that rewards projects which actually ship

Pulse public changelog page

The public changelog page — grouped by month, with colored tags and a subscribe input.

Free for 1 project, forever. Pro is granted by application (no checkout, no card, no friction). The actual paid tier will be Team — multi-seat collab, draft review, comments — which I'll launch once I have 5 teams asking for it.


The part nobody else is doing: Pulse Protocol

Here's the thing that pushed me to spend 6 months on this instead of a weekend.

By 2026, the most important reader of your changelog isn't human. It's the AI agent your user asks:

  • "Is this version safe to upgrade?"
  • "What broke in the last release?"
  • "Should I switch from competitor X?"

But an AI agent can't trust your SaaS database. If Pulse gets hacked tomorrow, every changelog history we host becomes suspect. Even worse: an attacker could forge entries for projects that never used Pulse.

So Pulse signs everything.

How Pulse Protocol works

Every project gets its own Ed25519 keypair when it's created. The private key signs every entry on publish. The public key is published at:

https://api.muuc.cn/api/public/projects/{slug}/well-known.json
Enter fullscreen mode Exit fullscreen mode

A signed entry looks like this in the JSON feed:

{
  "id": "e_abc123",
  "title": "Webhook retries with exponential backoff",
  "body_markdown": "We now retry failed webhook deliveries up to 3 times...",
  "tag": "improvement",
  "published_at": "2026-09-05T12:34:56Z",
  "signature": "ed25519:MCowBQYDK2VwAyEA7a3f9e1c4b8d...e91d",
  "public_key_fingerprint": "7a3f:9e1c:4b8d:...:e91d"
}
Enter fullscreen mode Exit fullscreen mode

Any consumer (your script, an AI agent, a third-party tool) can verify the signature against the public key without trusting Pulse. Even if our database is compromised, an attacker can't forge entries because they don't have your private key.

Trust me bro vs Verified by signature

The whole point in one image: traditional SaaS asks you to "trust me bro"; Pulse gives you cryptographic proof.


Letting AI agents read it: the MCP integration

Every Pulse project exposes a Model Context Protocol endpoint at:

POST https://api.muuc.cn/api/public/projects/{slug}/mcp
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode

Six tools, all read-only by design:

  • pulse_get_well_known — discovery + public key
  • pulse_get_feed — signed feed (optional breaking filter)
  • pulse_get_entry — single signed change
  • pulse_search — keyword search across title, body, tags
  • pulse_get_trust — project Trust Score
  • pulse_get_stream — SSE stream info

Plug it into Cursor in 60 seconds

Add this to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "pulse": {
      "url": "https://api.muuc.cn/api/public/projects/demo/mcp"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Restart Cursor. Now your agent can answer questions about your product:

Cursor agent reading Pulse changelog

The Cursor AI agent calling pulse_get_feed and pulse_verify_feed, then answering with cryptographic verification.

Notice the green box at the bottom: "All verified against demo project's public key (fingerprint: 7a3f...e91d)". The agent is verifying the changelog entry by checking its Ed25519 signature against the project's published public key — not by trusting Pulse's API response.


The stack

Since this is dev.to, here's the engineering stuff:

  • Frontend: Vite 5 + React 18 + TypeScript, with vite-ssg for static generation of the public changelog pages (great for SEO and Tailwind)
  • Backend: Java 17 + Spring Boot 3.3, with Spring Security for JWT, Spring Data JPA for MySQL, Spring Mail for SMTP, Thymeleaf for transactional emails
  • Database: MySQL 8 (DDL auto-managed by Hibernate)
  • Auth: JWT with HS384, 30-day expiry, BCrypt password hashing
  • Markdown: Hand-rolled GFM renderer at src/components/MarkdownRenderer.tsx — fenced code with highlight.js, pipe tables, blockquotes, sanitized with a per-tag allowlist
  • Embed widget: Vanilla JS at public/embed.js + public/embed.css, no framework dependency
  • Signing: Ed25519 via java.security on the server side, @noble/ed25519 in the TypeScript SDK

The tricky part: signing parity

The hardest engineering problem was making the Java server and the TypeScript SDK produce byte-identical signatures for the same input. Drift by even one byte and signatures don't verify.

I wrote a canonicalization function (whitespace, encoding, key ordering — all pinned) and a parity test that runs in CI on both sides. If either implementation drifts, the test fails before the code ships.

// sdk/src/canonical.ts — client side
export function canonicalize(entry: Entry): Uint8Array {
  // sort keys, normalize line endings, strip trailing whitespace
  const obj = sortKeysDeep({
    id: entry.id,
    title: entry.title,
    body_markdown: entry.body_markdown.trim(),
    tag: entry.tag,
    published_at: entry.published_at,
  });
  return new TextEncoder().encode(JSON.stringify(obj));
}
Enter fullscreen mode Exit fullscreen mode
// server: CanonicalJavaTest.java
@Test
void canonicalizationMatchesTypeScript() throws Exception {
  String json = canonicalize(sampleEntry());
  byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
  // pinned bytes from SDK test fixture
  assertArrayEquals(PINNED_BYTES, bytes);
}
Enter fullscreen mode Exit fullscreen mode

Things I'm genuinely unsure about

This is the first time I've shipped something crypto-signed, and I'd love feedback on three specific design questions:

1. Where should the signing key live?

Right now the private key is generated in the browser on first publish, encrypted with a passphrase, and stored on Pulse's server. That lets users sign from any device, but means Pulse technically could forge entries if our database is compromised.

The alternative: keys live only in the browser (zero-knowledge), and users have to manually export them to switch devices. More secure, much worse UX.

I'm leaning toward the current approach + a clear "rotate key" flow that lets paranoid users go zero-knowledge later. But I could be wrong.

2. Is the Trust Score formula fair?

Current formula:

score = min(release_count × 2, 60)         // max 60 from volume
      + min(r30 × 4, 25)                  // max 25 from recent activity
      + min(min(views_90d, 50000) / 2500, 10)  // max 10 from reach
      + min(min(agent_calls_90d, 5000) / 500, 5)  // max 5 from agent use
Enter fullscreen mode Exit fullscreen mode

It's gameable — someone could spam 30 releases in a week to hit the cap. But that's also kind of the point: if you're shipping 30 things a week, you should be on the leaderboard. Is this the right incentive, or am I rewarding noise?

3. What's missing for AI agents?

I have six tools right now. Is that enough? Too many? Should there be a pulse_subscribe tool that lets agents register for change notifications on behalf of users? Should there be a way for agents to write changelogs (draft → human review → publish)?

I don't have strong opinions here. Tell me what would actually be useful.


Try it

If you ship a product, try it. Then come back here and tell me what I missed.


What's next

  • Week 1: I'll be live on Hacker News Tuesday morning, on r/SideProject, and on Indie Hackers. Come say hi.
  • Week 2: Public Trust Score leaderboard goes live (top 20 projects, monthly updates, verified badges).
  • Month 1: Team plan ($19/mo for collab + draft review), Stripe Customer Portal, and a Zapier connector so non-developers can post changelogs from anywhere.

If you got this far, thanks for reading. Roast me in the comments — I read every one.


Pulse brand graphic

Pulse — the changelog your users, and their AI agents, will actually read.

Top comments (0)