<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: win und</title>
    <description>The latest articles on DEV Community by win und (@muuccn).</description>
    <link>https://dev.to/muuccn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4111361%2F7e4beda6-1bbe-4922-8911-6d04a55fba9b.png</url>
      <title>DEV Community: win und</title>
      <link>https://dev.to/muuccn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muuccn"/>
    <language>en</language>
    <item>
      <title>Show HN: Pulse – A changelog that AI agents can verify (signed feed + MCP)</title>
      <dc:creator>win und</dc:creator>
      <pubDate>Sat, 05 Sep 2026 16:05:20 +0000</pubDate>
      <link>https://dev.to/muuccn/show-hn-pulse-a-changelog-that-ai-agents-can-verify-signed-feed-mcp-5g3g</link>
      <guid>https://dev.to/muuccn/show-hn-pulse-a-changelog-that-ai-agents-can-verify-signed-feed-mcp-5g3g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fog%2Fdevto-cover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fog%2Fdevto-cover.png" alt="Pulse — the changelog your AI agents can verify" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building Pulse: A Changelog That Your AI Agents Can Actually Verify
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Six months, one developer, one weird idea: what if your changelog didn't need humans to &lt;em&gt;trust&lt;/em&gt; it — it just &lt;em&gt;verified&lt;/em&gt; itself?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The graveyard of indie changelogs
&lt;/h2&gt;

&lt;p&gt;I've shipped 50+ "v1.0.1 — fix" releases in my life. So has every indie dev I know.&lt;/p&gt;

&lt;p&gt;Look at any GitHub Releases page for a small SaaS and you'll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"v1.0.1 — minor fix"&lt;/li&gt;
&lt;li&gt;"v1.0.2 — small fix"&lt;/li&gt;
&lt;li&gt;"v1.0.3 — fix typo"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nobody reads them. Not users. Not AI agents. Not you, six months later when you're trying to remember what actually shipped.&lt;/p&gt;

&lt;p&gt;I asked 20 indie founders: &lt;em&gt;do you have a real changelog?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;12&lt;/strong&gt; pointed at GitHub Releases and shrugged&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;6&lt;/strong&gt; had abandoned changelogs after the first month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2&lt;/strong&gt; had paid for Beamer and quit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zero had something their users' AI agents could actually &lt;em&gt;read&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That gap became &lt;a href="https://pulse.muuc.cn" rel="noopener noreferrer"&gt;Pulse&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Pulse actually is
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F02-demo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F02-demo.png" alt="Pulse public changelog page" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The public changelog page — grouped by month, with colored tags and a subscribe input.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free for 1 project, forever.&lt;/strong&gt; 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.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part nobody else is doing: Pulse Protocol
&lt;/h2&gt;

&lt;p&gt;Here's the thing that pushed me to spend 6 months on this instead of a weekend.&lt;/p&gt;

&lt;p&gt;By 2026, the most important reader of your changelog isn't human. It's the AI agent your user asks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Is this version safe to upgrade?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"What broke in the last release?"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Should I switch from competitor X?"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But an AI agent can't &lt;em&gt;trust&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;So Pulse signs everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Pulse Protocol works
&lt;/h3&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.muuc.cn/api/public/projects/{slug}/well-known.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A signed entry looks like this in the JSON feed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Webhook retries with exponential backoff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body_markdown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"We now retry failed webhook deliveries up to 3 times..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"improvement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"published_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-09-05T12:34:56Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signature"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ed25519:MCowBQYDK2VwAyEA7a3f9e1c4b8d...e91d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"public_key_fingerprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7a3f:9e1c:4b8d:...:e91d"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F03-comparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F03-comparison.png" alt="Trust me bro vs Verified by signature" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The whole point in one image: traditional SaaS asks you to "trust me bro"; Pulse gives you cryptographic proof.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Letting AI agents read it: the MCP integration
&lt;/h2&gt;

&lt;p&gt;Every Pulse project exposes a &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; endpoint at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST https://api.muuc.cn/api/public/projects/{slug}/mcp
Content-Type: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six tools, all read-only by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pulse_get_well_known&lt;/code&gt; — discovery + public key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pulse_get_feed&lt;/code&gt; — signed feed (optional &lt;code&gt;breaking&lt;/code&gt; filter)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pulse_get_entry&lt;/code&gt; — single signed change&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pulse_search&lt;/code&gt; — keyword search across title, body, tags&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pulse_get_trust&lt;/code&gt; — project Trust Score&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pulse_get_stream&lt;/code&gt; — SSE stream info&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plug it into Cursor in 60 seconds
&lt;/h3&gt;

&lt;p&gt;Add this to &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pulse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.muuc.cn/api/public/projects/demo/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Cursor. Now your agent can answer questions about your product:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F01-cursor-mcp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fscreenshots%2F01-cursor-mcp.png" alt="Cursor agent reading Pulse changelog" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Cursor AI agent calling &lt;code&gt;pulse_get_feed&lt;/code&gt; and &lt;code&gt;pulse_verify_feed&lt;/code&gt;, then answering with cryptographic verification.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notice the green box at the bottom: &lt;em&gt;"All verified against demo project's public key (fingerprint: 7a3f...e91d)"&lt;/em&gt;. 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.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;Since this is dev.to, here's the engineering stuff:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  The tricky part: signing parity
&lt;/h3&gt;

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

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sdk/src/canonical.ts — client side&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canonicalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// sort keys, normalize line endings, strip trailing whitespace&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sortKeysDeep&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body_markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body_markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;published_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server: CanonicalJavaTest.java&lt;/span&gt;
&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;canonicalizationMatchesTypeScript&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canonicalize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sampleEntry&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StandardCharsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UTF_8&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// pinned bytes from SDK test fixture&lt;/span&gt;
  &lt;span class="n"&gt;assertArrayEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PINNED_BYTES&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Things I'm genuinely unsure about
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  1. Where should the signing key live?
&lt;/h3&gt;

&lt;p&gt;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 &lt;em&gt;could&lt;/em&gt; forge entries if our database is compromised.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Is the Trust Score formula fair?
&lt;/h3&gt;

&lt;p&gt;Current formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;em&gt;should&lt;/em&gt; be on the leaderboard. Is this the right incentive, or am I rewarding noise?&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What's missing for AI agents?
&lt;/h3&gt;

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

&lt;p&gt;I don't have strong opinions here. Tell me what would actually be useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public demo&lt;/strong&gt;: &lt;a href="https://pulse.muuc.cn/c/demo" rel="noopener noreferrer"&gt;pulse.muuc.cn/c/demo&lt;/a&gt; — already wired up with 5 changelog entries and a live MCP endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard&lt;/strong&gt;: &lt;a href="https://app.muuc.cn" rel="noopener noreferrer"&gt;app.muuc.cn&lt;/a&gt; — sign up with email, create a project, publish your first entry in 5 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol spec&lt;/strong&gt;: &lt;a href="https://pulse.muuc.cn/protocol" rel="noopener noreferrer"&gt;pulse.muuc.cn/protocol&lt;/a&gt; — the full Ed25519 + MCP design&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI integration guide&lt;/strong&gt;: &lt;a href="https://pulse.muuc.cn/ai" rel="noopener noreferrer"&gt;pulse.muuc.cn/ai&lt;/a&gt; — copy-paste setup for Cursor / Claude Desktop / Continue / Windsurf&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/your-github-handle/pulse" rel="noopener noreferrer"&gt;github.com/your-github-handle/pulse&lt;/a&gt; — PRs welcome, especially for Postgres adapter and ARM64 builds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ship a product, try it. Then come back here and tell me what I missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Week 1&lt;/strong&gt;: I'll be live on &lt;a href="https://news.ycombinator.com/" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt; Tuesday morning, on &lt;a href="https://reddit.com/r/SideProject" rel="noopener noreferrer"&gt;r/SideProject&lt;/a&gt;, and on &lt;a href="https://www.indiehackers.com/" rel="noopener noreferrer"&gt;Indie Hackers&lt;/a&gt;. Come say hi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Week 2&lt;/strong&gt;: Public Trust Score leaderboard goes live (top 20 projects, monthly updates, verified badges).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Month 1&lt;/strong&gt;: Team plan ($19/mo for collab + draft review), Stripe Customer Portal, and a Zapier connector so non-developers can post changelogs from anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you got this far, thanks for reading. Roast me in the comments — I read every one.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fog%2Fdevto-footer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpulse.muuc.cn%2Fassets%2Fog%2Fdevto-footer.png" alt="Pulse brand graphic" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pulse — the changelog your users, and their AI agents, will actually read.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>mcp</category>
      <category>saas</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
