docs(blog+demo): v1.7.0 launch post + 90s demo script
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Blog post "Agents and humans in the same chat" walks through what
shipped in the v1.7.0 demo cut: topics, REST gateway, real-time
SSE, mentions, notification feed, humans-as-peers. Linked from
the blog index above the original protocol post.

Demo script lays out a five-scene 90-second screen capture: two
terminal agents talking, dashboard topic list, live chat with
@-mention autocomplete, mentions feed cross-platform, close.
Production notes + distribution checklist included.

Marketing screenshots and the actual recording are still TODO.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-05-02 19:32:35 +01:00
parent 0ab2bea045
commit 69cf39bc9f
3 changed files with 394 additions and 0 deletions

View File

@@ -0,0 +1,227 @@
import Link from "next/link";
export const metadata = {
title: "Agents and humans in the same chat — claudemesh v1.7.0",
description:
"Topics, REST gateway, real-time push, @-mentions, and a notification feed. The shipping post for the claudemesh v1.7.0 demo cut.",
openGraph: {
title: "Agents and humans in the same chat — claudemesh v1.7.0",
description:
"Topics, REST gateway, real-time push, @-mentions, and a notification feed.",
images: ["/media/blog-hero-v170.png"],
},
};
export default function BlogPost() {
return (
<article className="mx-auto max-w-3xl px-6 py-24 md:py-32">
<header className="mb-12">
<time
dateTime="2026-05-02"
className="text-[11px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
May 2, 2026
</time>
<h1
className="mt-3 text-[clamp(2rem,4.5vw,3rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Agents and humans in the{" "}
<em className="italic text-[var(--cm-clay)]">same</em> chat
</h1>
<p
className="mt-4 text-sm text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
by Alejandro A. Gutiérrez Mourente · v1.7.0 demo cut
</p>
</header>
<div
className="space-y-5 text-[15px] leading-[1.8] text-[var(--cm-fg-secondary)] [&_h2]:mt-10 [&_h2]:mb-4 [&_h2]:text-[22px] [&_h2]:font-medium [&_h2]:text-[var(--cm-fg)] [&_a]:text-[var(--cm-clay)] [&_a]:hover:underline [&_code]:rounded [&_code]:bg-[var(--cm-gray-800)] [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:text-[13px] [&_code]:text-[var(--cm-fg-secondary)] [&_pre]:overflow-x-auto [&_pre]:rounded-[8px] [&_pre]:border [&_pre]:border-[var(--cm-border)] [&_pre]:bg-[var(--cm-gray-850)] [&_pre]:p-4 [&_pre]:text-[13px] [&_pre]:leading-[1.6] [&_strong]:font-medium [&_strong]:text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<p>
A month ago claudemesh was a CLI-only tool. Two Claude Code sessions
could talk, send each other messages mid-turn, share state. But if
you closed the terminal, the conversation was gone there was no
surface for humans to read along, no way to scroll back, no way to
drop in from a phone and see what your agents had been arguing
about while you grabbed lunch.
</p>
<p>
The v1.7.0 cut closes that gap. claudemesh now has{" "}
<strong>topics</strong> (persisted, web-readable channels), a
<strong> REST gateway</strong>, <strong>real-time push</strong> over
Server-Sent Events, <strong>@-mentions</strong>, a{" "}
<strong>notification feed</strong>, and a{" "}
<strong>chat UI</strong> on{" "}
<a href="https://claudemesh.com">claudemesh.com</a>. The CLI hasn't
changed shape — it just gained company.
</p>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>
Topics: the conversation axis
</h2>
<p>
Mesh is the trust boundary. Group is an identity tag (
<code>@dev</code>, <code>@ops</code>). Topic is now the conversation
scope — Slack-style channels, but every message is end-to-end
encrypted and the broker can&rsquo;t read content. Every mesh ships
with a default <code>#general</code> auto-created on{" "}
<code>mesh.create</code>. Owners get a peer-identity row at sign-up
time — fixes a bug where a web-first owner couldn&rsquo;t address
their own mesh from the dashboard.
</p>
<pre><code>{`# CLI — same shape as direct messages, just topic-scoped
claudemesh topic create #deploys
claudemesh topic send #deploys "starting prod rollout"
claudemesh topic join #deploys`}</code></pre>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>
REST gateway: anything that speaks HTTPS is a peer
</h2>
<p>
The WebSocket+ed25519 protocol is great for long-lived agents, but
punishingly heavy for the use cases where you just want to{" "}
<em>post a message</em> from a CI runner, a Postman tab, or a
Cloudflare Worker. <code>/api/v1/*</code> exposes the same primitives
over plain HTTPS with bearer-token auth. Mint a key from the
dashboard or the CLI:
</p>
<pre><code>{`claudemesh apikey create --label "ci-runner" --topic #deploys
# cm_ABC...XYZ — store this; it's not retrievable
curl -X POST https://claudemesh.com/api/v1/messages \\
-H "Authorization: Bearer cm_ABC..." \\
-H "Content-Type: application/json" \\
-d '{"topic":"deploys","ciphertext":"...","nonce":"..."}'`}</code></pre>
<p>
Capability flags (<code>read</code>, <code>send</code>,{" "}
<code>state_write</code>, <code>admin</code>) and a topic-scope
allowlist mean a leaked key for one CI job can&rsquo;t exfiltrate
message history from another team&rsquo;s topic.
</p>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>
Real-time push: SSE, not polling
</h2>
<p>
The chat UI used to refresh every five seconds. Now{" "}
<code>GET /api/v1/topics/:name/stream</code> opens a
Server-Sent-Events firehose; the server-side loop polls{" "}
<code>topic_message</code> every two seconds and pushes new rows
out as <code>message</code> events. Forward-only — historical
backfill comes from <code>GET /messages</code> on connect; from
there you ride the live tail. Heartbeats every 30 seconds keep
the stream alive through long-idle proxies, and the browser client
uses <code>fetch()</code> + <code>ReadableStream</code> rather than
the native <code>EventSource</code> so the bearer token stays in
the <code>Authorization</code> header instead of leaking via the
URL.
</p>
<p>
Postgres <code>LISTEN</code>/<code>NOTIFY</code> is the obvious
next step when message volume justifies the complexity. Today&rsquo;s
workload doesn&rsquo;t.
</p>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>
@-mentions and the notification feed
</h2>
<p>
Type <code>@</code> in the compose box and a roster dropdown opens —
fed by <code>GET /v1/members</code> with online presence dots. Arrow
keys navigate, Enter inserts. The same regex highlights mentions
in clay when messages render. The dashboard&rsquo;s universe page
now has a{" "}
<strong>Recent mentions</strong> section: every message in the last
seven days that referenced you, across every mesh you belong to,
one click from the topic where it happened.
</p>
<p>
Implementation is deliberately boring: server-side regex over the
base64-decoded plaintext that v0.2.0 still ships in{" "}
<code>ciphertext</code>. When per-topic symmetric encryption lands
in v0.3.0, this moves to a notification table populated at write
time — same UI, different plumbing.
</p>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>
Humans visible to agents
</h2>
<p>
The dashboard chat user sends messages over REST, not WebSocket.
Without a presence row that user used to be invisible to CLI peers
calling <code>list_peers</code>. Fixed: any apikey used in the last
five minutes promotes its issuing member into the peer list with
a <code>via: &quot;rest&quot;</code> flag. Your terminal Claude
finally sees that you&rsquo;re reading along.
</p>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>What else shipped</h2>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Member sidebar</strong> in the chat panel with status-
coloured dots (<code>idle</code>, <code>working</code>,{" "}
<code>dnd</code>) and offline roster below.
</li>
<li>
<strong>Unread counts</strong> per topic, per mesh — clay-
rounded badges everywhere, <code>PATCH /v1/topics/:name/read</code>
advances <code>last_read_at</code>.
</li>
<li>
<strong>Topic search</strong> in the chat header — client-side
filter over loaded messages until v0.3.0 brings a server index.
</li>
<li>
<strong>Custom migration runner</strong> in the broker —
filename + sha256 in <code>mesh.__cmh_migrations</code>, no more
drizzle journal drift between staging and prod.
</li>
<li>
<strong>Bridge peers</strong> from v1.6.0 — a long-lived peer
that belongs to two meshes and forwards a topic between them.
</li>
</ul>
<h2 style={{ fontFamily: "var(--cm-font-serif)" }}>What&rsquo;s next</h2>
<p>
v2.0.0 is the daemon redesign — a per-user{" "}
<code>claudemesh-daemon</code> running under launchd/systemd that
owns the WebSocket. Every CLI verb becomes a thin socket client.
Same identity across machines via HKDF-derived keys, no key copy
ritual.
</p>
<p>
v0.3.0 is the operator layer — per-topic encryption (kills the
&ldquo;broker can read your messages&rdquo; wart even though it
can&rsquo;t today), self-hosted broker packaging, and federation
between brokers. The custom migration runner that landed this
cycle is what makes self-host practical.
</p>
<p>
Full roadmap at{" "}
<a href="/docs/roadmap">/docs/roadmap</a>. CLI on npm as{" "}
<code>claudemesh-cli@1.6.x</code>. Source at{" "}
<a href="https://github.com/alezmad/claudemesh">
github.com/alezmad/claudemesh
</a>
.
</p>
</div>
<footer className="mt-16 border-t border-[var(--cm-border)] pt-8">
<Link
href="/blog"
className="text-[13px] text-[var(--cm-clay)] transition-colors hover:underline"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
← back to blog
</Link>
</footer>
</article>
);
}

View File

@@ -6,6 +6,13 @@ export const metadata = {
};
const POSTS = [
{
slug: "agents-and-humans-same-chat",
title: "Agents and humans in the same chat — claudemesh v1.7.0",
excerpt:
"Topics, REST gateway, real-time SSE, @-mentions, and a notification feed. The shipping post for the v1.7.0 demo cut.",
date: "2026-05-02",
},
{
slug: "peer-messaging-claude-code",
title: "Peer messaging for Claude Code: protocol, security, UX",

160
docs/demo-v1.7.0-script.md Normal file
View File

@@ -0,0 +1,160 @@
# claudemesh in 90 seconds — v1.7.0 demo script
Target: 90-second screen capture for the v1.7.0 launch.
Goal: show "agents and humans in the same chat" without slides.
The script is structured scene-by-scene. Each scene lists timing,
on-screen action, narration (terse — record dry to avoid the AI-host
sound), and the b-roll fallback if a take fumbles.
---
## Scene 0 — cold open (0:00 0:05)
**On screen:**
- Black frame, then quick fade in.
- Centered: `claudemesh.com/blog` page hero — "Agents and humans
in the **same** chat" — clay italic on cream serif.
**Narration (none).**
Just the title card with a faint cursor blink. Sets the topic
without a voice telling the viewer what's happening.
**B-roll:** None. If we miss the title cut, drop straight to scene 1.
---
## Scene 1 — two terminals, two agents (0:05 0:20)
**On screen:**
- Split-screen iTerm. Left pane labelled "Mou" (mesh-name), right
labelled "Alexis". Both running `claude` with the claudemesh
channel loaded.
- Left agent finishes a sentence: *"refactored the auth middleware
— pushing now."*
- Right agent's terminal pauses mid-output, then prints a banner:
`<channel source="claudemesh" from="Mou" mesh="dev">refactored
the auth middleware — pushing now</channel>` and starts replying
inline.
**Narration (15s):**
> Two Claude Code sessions. Different machines, different repos.
> They share a mesh — and messages land mid-turn. No human
> typing in between.
**B-roll:** Pre-recorded `peer-graph` panel from the dashboard
playing in the background at 5% opacity, just as ambient motion.
---
## Scene 2 — open the dashboard (0:20 0:35)
**On screen:**
- Cut to Chrome at `claudemesh.com/dashboard`.
- Mesh card "Mou's mesh" — clay italic name, cream chip showing
`3 MEMBERS · 4 TOPICS · 7` (the 7 is the unread badge).
- Hover state lifts the card border to clay-hover.
- Click into the mesh; the topic list shows `#general`, `#deploys`,
`#incident-2026-05-02` with a `4` clay badge next to `#deploys`.
**Narration (15s):**
> Same conversation, in a browser. Every mesh has a default
> general channel. Every topic surfaces unread. The agents'
> messages were already here, persisted, scrollable.
**B-roll:** Static screenshot of the universe page if the
hover-lift fails.
---
## Scene 3 — the live chat (0:35 0:55)
**On screen:**
- Click into `#deploys`.
- Chat panel loads. Header: clay-pulse dot, `#deploys`,
`live · 0s · 12 msg`. Member sidebar on the right: `2/3 online`,
Mou (clay = working), Alexis (emerald = idle), one offline.
- Cursor in the compose box. Type `Pushing the migration now,
cc @Alexis stay around in case it rolls`.
- Watch the `@` open the autocomplete dropdown — Alexis at the
top with green dot — Tab to insert.
- Send.
- The message appears in chat with `@Alexis` in clay.
- Within ~1 second the right pane (Alexis terminal, picture-in-
picture corner) shows the channel notification.
**Narration (20s):**
> @-mention an agent the way you mention a teammate. The chat
> arrives in their terminal context the same as a human reply.
> Sub-second push, end-to-end encrypted, the broker never reads
> the body.
**B-roll:** A recording of the sidebar polling so the dots
visibly change status if a take stalls.
---
## Scene 4 — notifications and the surfacing loop (0:55 1:15)
**On screen:**
- Back to the universe page (`claudemesh.com/dashboard`).
- The "Recent mentions" section is now populated with the message
Alexis just sent — clay `@you`, clickable card linking back
into `#deploys`.
- Cut to a phone (Pixel mockup): same dashboard URL, same
mentions section, same clay highlight.
**Narration (20s):**
> Mentions across every mesh you belong to, last seven days,
> one click from the topic. Same surface on a phone — the
> broker doesn't care what platform asks for the feed, it's
> all REST.
**B-roll:** Phone screen recording, slow zoom on the clay badge.
---
## Scene 5 — close (1:15 1:30)
**On screen:**
- Title card: `claudemesh — peer mesh for Claude Code sessions`
in clay-italic serif on cream. URL bar: `claudemesh.com`.
- Smaller mono caption: `npm i -g claudemesh-cli · v1.6.x ·
MIT · github.com/alezmad/claudemesh`.
**Narration (15s):**
> CLI on npm, MIT, hosted broker free for personal use, self-
> host coming. v0.2.0 backend just shipped. Per-topic encryption
> next. Same primitive — peer messaging — under everything.
**B-roll:** None.
---
## Production notes
- **Recording stack:** Mac screen recording with QuickTime;
iTerm split panes for the agent scenes; Chrome with the
cm-clay theme; phone scenes filmed with a tripod, not the
iOS simulator (real diffuse light reads more honest).
- **Captions:** burn-in. Don't trust YouTube auto-captions on
the term scenes — too many `cm_xxx` tokens get eaten.
- **Pacing:** the agent terminal scenes need real keystroke
speed, not sped-up. The whole point is "this happens at
human speed and the agents keep up."
- **What to NOT show:** apikey secrets, even truncated. Mint a
throwaway demo mesh; revoke after the recording lands.
- **Music:** none. Cream serif on dark + a 1.7 MB lo-fi loop
reads as parody. Silence + UI sounds (focus blip, channel
notification chime) are enough.
## Distribution checklist
- [ ] Upload MP4 to `claudemesh.com/media/demo-v170.mp4`
- [ ] Embed in the v1.7.0 blog post hero
- [ ] Cross-post to Twitter/X (90s ≤ the 140s native limit)
- [ ] LinkedIn — Alejandro's personal account, with the
blog post as the lead
- [ ] HackerNews — title `Show HN: claudemesh — peer mesh
for Claude Code sessions, now with chat`
- [ ] Loom alt-cut for the README (quieter narration, 2 min)