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
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
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"
}
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.
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
Six tools, all read-only by design:
-
pulse_get_well_known— discovery + public key -
pulse_get_feed— signed feed (optionalbreakingfilter) -
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"
}
}
}
Restart Cursor. Now your agent can answer questions about your product:
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-ssgfor 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.securityon the server side,@noble/ed25519in 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));
}
// 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);
}
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
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
- Public demo: pulse.muuc.cn/c/demo — already wired up with 5 changelog entries and a live MCP endpoint
- Dashboard: app.muuc.cn — sign up with email, create a project, publish your first entry in 5 minutes
- Protocol spec: pulse.muuc.cn/protocol — the full Ed25519 + MCP design
- AI integration guide: pulse.muuc.cn/ai — copy-paste setup for Cursor / Claude Desktop / Continue / Windsurf
- GitHub: github.com/your-github-handle/pulse — PRs welcome, especially for Postgres adapter and ARM64 builds
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 — the changelog your users, and their AI agents, will actually read.





Top comments (0)