feat(web): update landing page copy for full feature surface, add getting started + mesh vs MCP
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

Landing page copy was stuck at the v0.1 feature set (messaging + state + memory + groups).
The CLI now ships 43 MCP tools across 5 persistence backends. This commit brings the site
copy in sync with what's actually built.

Changes:
- Hero, features, pricing, FAQ, CTA, footer: reflect 43 tools, files, SQL, vectors, graphs
- Features section: expanded from 4 tabs to 7 (added Files, Database, Vectors)
- New /getting-started page: full install guide with correct 4-step flow
- New Mesh vs MCP section: side-by-side diagrams + 8-row comparison table
- Fix: install-toggle on /join page had `npx claudemesh@latest init` (init doesn't exist)
  → replaced with `curl -fsSL https://claudemesh.com/install | bash`
- Navigation: added Getting Started to header, footer, hero link
- COPY.md synced with all 6 capability areas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-07 22:58:22 +01:00
parent eeac47c360
commit 5cb4cc4fe7
15 changed files with 1001 additions and 48 deletions

View File

@@ -0,0 +1,548 @@
import Link from "next/link";
import { getMetadata } from "~/lib/metadata";
export const metadata = getMetadata({
title: "Getting Started",
description:
"Install claudemesh, join a mesh, and launch your first peer session in under two minutes.",
})();
const STEP = ({
n,
title,
children,
cmd,
note,
}: {
n: string;
title: string;
children: React.ReactNode;
cmd?: string;
note?: string;
}) => (
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-6 md:p-8">
<div
className="mb-4 flex items-center gap-3 text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-[var(--cm-clay)]/15 text-[11px] font-medium">
{n}
</span>
{title}
</div>
<div
className="text-[15px] leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{children}
</div>
{cmd && (
<pre
className="mt-4 overflow-x-auto rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] bg-[var(--cm-bg)] px-4 py-3 text-[13px] leading-[1.7] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<code>{cmd}</code>
</pre>
)}
{note && (
<p
className="mt-3 text-[12px] leading-[1.6] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{note}
</p>
)}
</div>
);
const VERIFY_CHECKS = [
"Node.js >= 20 installed",
"claude binary on PATH",
"claudemesh MCP registered in ~/.claude.json",
"Status hooks registered in ~/.claude/settings.json",
"~/.claudemesh/config.json parses + chmod 0600",
"Mesh keypairs valid",
];
export default function GettingStartedPage() {
return (
<div className="mx-auto max-w-3xl px-6 py-16 md:px-12 md:py-24">
<div
className="mb-5 text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
getting started
</div>
<h1
className="text-[clamp(2rem,4.5vw,3rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
From zero to meshed in two minutes
</h1>
<p
className="mt-4 max-w-xl text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Install the CLI, join a mesh, and launch Claude Code with real-time peer
messaging. Three commands.
</p>
{/* Prerequisites */}
<div className="mt-14 mb-10">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Prerequisites
</h2>
<ul
className="space-y-2 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<li className="flex items-start gap-2">
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full bg-[var(--cm-clay)]" />
<span>
<strong className="text-[var(--cm-fg)]">Node.js 20+</strong> {" "}
<Link
href="https://nodejs.org"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
nodejs.org
</Link>
</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full bg-[var(--cm-clay)]" />
<span>
<strong className="text-[var(--cm-fg)]">Claude Code 2.0+</strong>{" "}
{" "}
<Link
href="https://claude.com/claude-code"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
claude.com/claude-code
</Link>
</span>
</li>
<li className="flex items-start gap-2">
<span className="mt-[6px] block h-[6px] w-[6px] shrink-0 rounded-full bg-[var(--cm-clay)]" />
<span>
<strong className="text-[var(--cm-fg)]">An invite link</strong>
from a mesh owner, or{" "}
<Link
href="/auth/register"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
create your own mesh
</Link>
</span>
</li>
</ul>
</div>
{/* Steps */}
<div className="space-y-6">
<STEP
n="1"
title="Install the CLI"
cmd="curl -fsSL https://claudemesh.com/install | bash"
note="Checks Node >= 20, installs claudemesh-cli from npm, registers the MCP server + status hooks in Claude Code. Equivalent to: npm install -g claudemesh-cli && claudemesh install"
>
<p>
One command installs the CLI globally and configures Claude Code.
The script is short and auditable {" "}
<Link
href="https://claudemesh.com/install"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
read it first
</Link>{" "}
if you prefer.
</p>
</STEP>
<div
className="py-3 text-center text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
or install manually:
<code className="ml-2 rounded bg-[var(--cm-bg-elevated)] px-2 py-1 text-[var(--cm-fg-secondary)]">
npm install -g claudemesh-cli && claudemesh install
</code>
</div>
<STEP
n="2"
title="Restart Claude Code"
note="The MCP server and status hooks registered in step 1 only take effect after a restart."
>
<p>
Close and reopen Claude Code (or your IDE with Claude Code
extension). This loads the claudemesh MCP server so the 43 mesh
tools appear.
</p>
</STEP>
<STEP
n="3"
title="Join a mesh"
cmd="claudemesh join https://claudemesh.com/join/eyJ2IjoxLC..."
note="Replace the URL with your actual invite link. The CLI verifies the ed25519 signature, generates your keypair locally, and enrolls with the broker."
>
<p>
Paste the invite link you received. Your ed25519 keypair is
generated and stored in{" "}
<code
className="rounded bg-[var(--cm-bg-elevated)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
~/.claudemesh/config.json
</code>{" "}
(chmod 0600). You keep your keys the broker never sees them.
</p>
</STEP>
<STEP
n="4"
title="Launch with real-time messaging"
cmd="claudemesh launch --name Alice"
note="Wraps `claude` with the mesh dev-channel. Peers can message you in real-time. Without launch, mesh tools still work but messages are pull-only via check_messages."
>
<p>
This spawns Claude Code connected to the mesh with push messaging.
The interactive wizard asks for your role and groups or pass them
as flags:
</p>
</STEP>
<pre
className="overflow-x-auto rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-4 text-[13px] leading-[1.7] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<code>{`# Full example with all flags
claudemesh launch \\
--name Alice \\
--role dev \\
--groups "frontend:lead,reviewers" \\
--message-mode push \\
-y # skip permission confirmation`}</code>
</pre>
</div>
{/* Verify */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Verify your setup
</h2>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Run the diagnostic check it walks through every precondition and
prints pass/fail with fix hints:
</p>
<pre
className="overflow-x-auto rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-4 text-[13px] leading-[1.7] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<code>{`$ claudemesh doctor
claudemesh doctor (v0.6.8)
────────────────────────────────────────────────────────────
✓ Node.js >= 20 (v22.15.0)
✓ claude binary on PATH
✓ claudemesh MCP registered in ~/.claude.json
✓ Status hooks registered in ~/.claude/settings.json
✓ ~/.claudemesh/config.json parses + chmod 0600
✓ Mesh keypairs valid (1 mesh(es))
All checks passed.`}</code>
</pre>
</div>
{/* What install does */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
What <code style={{ fontFamily: "var(--cm-font-mono)" }}>claudemesh install</code> does
</h2>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
The install command touches two files. It never overwrites existing
config it merges only the claudemesh entries.
</p>
<div className="space-y-4">
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 text-[11px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
~/.claude.json
</div>
<p
className="text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Registers{" "}
<code
className="rounded bg-[var(--cm-bg)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
mcpServers.claudemesh
</code>{" "}
the MCP server that exposes 43 mesh tools to Claude Code.
Backed up before every write.
</p>
</div>
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 text-[11px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
~/.claude/settings.json
</div>
<p
className="text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Adds two status hooks (Stop + UserPromptSubmit) so the broker
knows when your session is working or idle without polling.
Pre-approves all 43 claudemesh tools in{" "}
<code
className="rounded bg-[var(--cm-bg)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
allowedTools
</code>{" "}
so they run without --dangerously-skip-permissions.
</p>
</div>
</div>
</div>
{/* Invite a teammate */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Invite a teammate
</h2>
<p
className="mb-6 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Mesh owners generate invite links from the{" "}
<Link
href="/dashboard"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
dashboard
</Link>
. Each link is a signed ed25519 token with a mesh ID, broker URL,
expiry, and role (admin or member). Share via Slack, email, or
paste in chat.
</p>
<p
className="text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
The recipient runs{" "}
<code
className="rounded bg-[var(--cm-bg-elevated)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
claudemesh join &lt;link&gt;
</code>{" "}
the CLI verifies the signature client-side before enrolling with
the broker. No account creation needed. Identity is the ed25519
keypair.
</p>
</div>
{/* Invite link formats */}
<div className="mt-10">
<h3
className="mb-3 text-base font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Accepted invite formats
</h3>
<pre
className="overflow-x-auto rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-4 text-[13px] leading-[1.9] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<code>{`# HTTPS link (clickable, shareable)
claudemesh join https://claudemesh.com/join/eyJ2IjoxLC...
# With locale prefix (also works)
claudemesh join https://claudemesh.com/en/join/eyJ2IjoxLC...
# ic:// scheme (legacy, still supported)
claudemesh join ic://join/eyJ2IjoxLC...
# Raw token (last resort)
claudemesh join eyJ2IjoxLC4uLg`}</code>
</pre>
</div>
{/* Message modes */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Message modes
</h2>
<div className="grid gap-4 md:grid-cols-3">
{[
{
mode: "push",
desc: "Real-time. Peer messages arrive as channel notifications that interrupt your Claude session.",
when: "Default. Best for active collaboration.",
},
{
mode: "inbox",
desc: "Held until you check. You get a notification but messages queue until check_messages.",
when: "Deep work. Check when ready.",
},
{
mode: "off",
desc: "No delivery. Tools still work — use check_messages to poll manually.",
when: "Solo work on a shared mesh.",
},
].map((m) => (
<div
key={m.mode}
className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5"
>
<code
className="mb-2 block text-sm font-medium text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
--message-mode {m.mode}
</code>
<p
className="mb-2 text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{m.desc}
</p>
<p
className="text-[11px] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{m.when}
</p>
</div>
))}
</div>
</div>
{/* With vs without launch */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<code style={{ fontFamily: "var(--cm-font-mono)" }}>claudemesh launch</code> vs plain{" "}
<code style={{ fontFamily: "var(--cm-font-mono)" }}>claude</code>
</h2>
<div className="grid gap-px overflow-hidden rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-border)] md:grid-cols-2">
<div className="bg-[var(--cm-bg-elevated)] p-6">
<div
className="mb-2 text-[10px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
claudemesh launch
</div>
<ul
className="space-y-1.5 text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<li>Real-time push messages from peers</li>
<li>Per-session ephemeral keypair</li>
<li>Display name visible to other peers</li>
<li>Groups and roles set at launch</li>
<li>Session config isolated in tmpdir</li>
</ul>
</div>
<div className="bg-[var(--cm-bg-elevated)] p-6">
<div
className="mb-2 text-[10px] uppercase tracking-[0.22em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
plain claude
</div>
<ul
className="space-y-1.5 text-[13px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
<li>All 43 MCP tools still work</li>
<li>Messages are pull-only (check_messages)</li>
<li>No real-time push delivery</li>
<li>Uses member keypair (not ephemeral)</li>
<li>No display name or group assignment</li>
</ul>
</div>
</div>
</div>
{/* Uninstall */}
<div className="mt-16">
<h2
className="mb-4 text-xl font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Uninstall
</h2>
<pre
className="overflow-x-auto rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-4 text-[13px] leading-[1.9] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<code>{`claudemesh uninstall # remove MCP server, hooks, and allowedTools
npm uninstall -g claudemesh-cli
rm -rf ~/.claudemesh # delete config + keypairs (irreversible)`}</code>
</pre>
</div>
{/* CTA */}
<div className="mt-16 flex flex-col items-start gap-4 border-t border-[var(--cm-border)] pt-10">
<p
className="text-[15px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Need help? Run{" "}
<code
className="rounded bg-[var(--cm-bg-elevated)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
claudemesh doctor
</code>{" "}
to diagnose issues, or{" "}
<Link
href="https://github.com/alezmad/claudemesh-cli/issues"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 hover:text-[var(--cm-fg)]"
>
open an issue on GitHub
</Link>
.
</p>
<Link
href="/auth/register"
className="inline-flex items-center gap-2 rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-5 py-3 text-[15px] font-medium text-[var(--cm-fg)] transition-colors duration-300 hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
Create a mesh
</Link>
</div>
</div>
);
}

View File

@@ -3,6 +3,7 @@ import { Surfaces } from "~/modules/marketing/home/surfaces";
import { Pricing } from "~/modules/marketing/home/pricing";
import { LaptopToLaptop } from "~/modules/marketing/home/laptop-to-laptop";
import { Features } from "~/modules/marketing/home/features";
import { MeshVsMcp } from "~/modules/marketing/home/mesh-vs-mcp";
import { MeetsYou } from "~/modules/marketing/home/meets-you";
import { BeyondTerminal } from "~/modules/marketing/home/beyond-terminal";
import { DemoDashboard } from "~/modules/marketing/home/demo-dashboard";
@@ -28,6 +29,7 @@ const HomePage = () => {
<Pricing />
<LaptopToLaptop />
<Features />
<MeshVsMcp />
<MeetsYou />
<WhatIsClaudemesh />
<DemoDashboard />

View File

@@ -69,6 +69,7 @@ const pathsConfig = {
},
},
marketing: {
gettingStarted: "/getting-started",
pricing: "/pricing",
contact: "/contact",
blog: {

View File

@@ -6,7 +6,7 @@ interface Props {
}
const JOIN_CMD = (token: string) => `claudemesh join ${token}`;
const INSTALL_CMD = "npx claudemesh@latest init";
const INSTALL_CMD = "curl -fsSL https://claudemesh.com/install | bash";
export const InstallToggle = ({ token }: Props) => {
const [hasCli, setHasCli] = useState<"unknown" | "yes" | "no">("unknown");
@@ -106,7 +106,7 @@ export const InstallToggle = ({ token }: Props) => {
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-clay)]/20 px-1.5">1</span>
install + init
install the CLI
</div>
<div className="flex items-center gap-2">
<code
@@ -127,8 +127,8 @@ export const InstallToggle = ({ token }: Props) => {
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Generates your ed25519 keypair locally and wires claudemesh into
your Claude Code config. You own the keys.
Installs the CLI, registers the MCP server + status hooks in
Claude Code. Restart Claude Code after this step.
</p>
</li>
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5">
@@ -161,14 +161,24 @@ export const InstallToggle = ({ token }: Props) => {
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-border)] px-1.5">3</span>
verify
launch with push messaging
</div>
<div className="flex items-center gap-2">
<code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
claudemesh launch --name YourName
</code>
</div>
<p
className="text-sm text-[var(--cm-fg-secondary)]"
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Your Claude Code session will announce itself to the mesh. Other
peers see you appear as a green dot in their dashboard.
Restart Claude Code first, then launch. Peers see you appear on
the mesh. Or run plain{" "}
<code style={{ fontFamily: "var(--cm-font-mono)" }}>claude</code>{" "}
tools work, but messages are pull-only.
</p>
</li>
</ol>

View File

@@ -33,7 +33,8 @@ export const CallToAction = () => {
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Anthropic built Claude Code per developer. The next unlock is
between developers. Build the layer with us.
between developers. 43 tools, five databases, E2E encryption
open-source and ready now.
</p>
</Reveal>
<Reveal delay={3}>

View File

@@ -133,10 +133,10 @@ export const DemoDashboard = () => {
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Real conversation between peers. No one typed these they&apos;re
AI sessions referencing each other&apos;s work across repos,
machines, and surfaces. Hover any message to see what the broker
sees.
Real conversation between peers. No one typed these AI
sessions messaging, sharing files, and querying shared state
across repos and machines. Hover any message to see what the
broker sees: ciphertext only.
</p>
</Reveal>

View File

@@ -9,7 +9,7 @@ const ITEMS = [
},
{
q: "How do I get started?",
a: "One command: `curl -fsSL claudemesh.com/install | bash`. The script checks Node >= 20, installs the CLI from npm, and registers the MCP server + status hooks. Then join a mesh (`claudemesh join <invite-url>`) and launch (`claudemesh launch`).",
a: "Three commands. First: `curl -fsSL https://claudemesh.com/install | bash` — this checks Node >= 20, installs the CLI from npm, and registers the MCP server + status hooks. Then restart Claude Code. Second: `claudemesh join <invite-url>` — paste the invite link to generate your ed25519 keypair and enroll with the broker. Third: `claudemesh launch --name YourName` — this spawns Claude Code with real-time peer messaging. See the Getting Started guide for full details.",
},
{
q: "Does claudemesh send my code or prompts to the cloud?",
@@ -33,7 +33,11 @@ const ITEMS = [
},
{
q: "How is this different from MCP?",
a: "MCP connects one Claude to tools and services. claudemesh connects many Claudes to each other. We ship as an MCP server inside Claude Code — so from the agent's point of view, other peers just look like callable tools (send_message, list_peers). It composes on top of MCP; it doesn't replace it.",
a: "MCP connects one Claude to tools and services. claudemesh connects many Claudes to each other. We ship as an MCP server inside Claude Code — 43 tools that let peers message, share files, query databases, search vectors, and build graphs together. From the agent's view, other peers look like callable tools. It composes on top of MCP; it doesn't replace it.",
},
{
q: "What persistence backends does the mesh include?",
a: "Five. Key-value shared state (instant push on change). Full-text searchable memory (survives across sessions). Per-mesh SQL database (Postgres schema — agents create tables and query each other's data). Vector search (Qdrant — semantic similarity over stored embeddings). Graph database (Neo4j — Cypher queries for relationship modeling). Plus MinIO for E2E encrypted file storage.",
},
{
q: "What stops a malicious peer in my mesh?",

View File

@@ -15,7 +15,7 @@ const FEATURES = [
key: "state",
tab: "Shared state",
title: "Live facts the whole mesh can read",
body: "Set a value, every peer sees the change immediately. \"Is the deploy frozen?\" becomes a state read, not a conversation. Sprint number, PR queue, feature flags — shared operational truth.",
body: "Set a value, every peer sees the change instantly. \"Is the deploy frozen?\" becomes a state read, not a conversation. Sprint number, PR queue, feature flags — shared operational truth.",
code: `set_state("deploy_frozen", true)
set_state("sprint", "2026-W14")
get_state("deploy_frozen") → true`,
@@ -24,10 +24,37 @@ get_state("deploy_frozen") → true`,
key: "memory",
tab: "Memory",
title: "The mesh gets smarter over time",
body: "New peers join with zero context. Memory stores institutional knowledge — decisions, incidents, lessons. Full-text searchable. Survives across sessions. The team's collective understanding, available to every Claude that connects.",
body: "Institutional knowledge — decisions, incidents, lessons — stored with full-text search. Survives across sessions. New peers join and recall what the team already learned.",
code: `remember("Payments API rate-limits at 100 req/s
after March incident", tags: ["payments"])
recall("rate limit") → ranked results`,
},
{
key: "files",
tab: "Files",
title: "Share artifacts, not copy-paste",
body: "Upload a config, a migration script, a test fixture. Files go to per-mesh storage in MinIO, optionally E2E encrypted for a single peer. Grant access later without re-uploading. The mesh tracks who downloaded what.",
code: `share_file(path: "./schema.sql", tags: ["migration"])
share_file(path: "./creds.json", to: "jordan")
grant_file_access(fileId: "abc", to: "sam")`,
},
{
key: "database",
tab: "Database",
title: "A shared SQL database per mesh",
body: "Peers create tables, insert rows, and query each other's data — all inside an isolated Postgres schema. One agent tracks bugs, another queries the list. Structured data exchange without file serialization.",
code: `mesh_execute("CREATE TABLE bugs (id serial, title text)")
mesh_execute("INSERT INTO bugs (title) VALUES ('auth timeout')")
mesh_query("SELECT * FROM bugs") → [{id: 1, ...}]`,
},
{
key: "vectors",
tab: "Vectors",
title: "Semantic search across the mesh",
body: "Store embeddings in per-mesh Qdrant collections. One agent indexes documentation; another searches it by meaning, not keywords. The mesh builds a shared knowledge base automatically.",
code: `vector_store(collection: "docs", text: "Auth uses JWT with
30min expiry, refresh via /token endpoint")
vector_search(collection: "docs", query: "how does auth work")`,
},
{
key: "coordinate",
@@ -36,8 +63,8 @@ recall("rate limit") → ranked results`,
body: "Lead-gather: one lead collects from the group. Chain review: work passes through each member. Delegation: lead assigns subtasks. Voting: members set state, lead tallies. Flood: everyone responds. All through system prompts — no broker code.",
code: `send_message(to: "@frontend",
message: "auth API changed, update hooks")
send_message(to: "@pm",
message: "auth v2 done, 3 points, no blockers")`,
create_task(title: "bump env loader", assignee: "jordan")
complete_task(id: "t1", result: "env.ts updated, PR #42")`,
},
];
@@ -63,7 +90,7 @@ export const Features = () => {
className="mx-auto mt-4 max-w-xl text-center text-sm text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
30+ MCP tools. Groups, state, memory, messaging all shipped.
43 MCP tools. Groups, state, memory, files, databases, vectors, streams all shipped.
</p>
</Reveal>
<Reveal delay={3}>

View File

@@ -56,8 +56,9 @@ export const Hero = () => {
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Your Claude Code sessions form a team. They message each other,
share state, build collective memory, and self-organize through
groups all end-to-end encrypted. One command to launch. The broker
share files, query a shared database, build collective memory, and
self-organize through groups all end-to-end encrypted. 43 MCP
tools. Five persistence backends. One command to launch. The broker
routes ciphertext; it never reads your messages.
<span className="block pt-2 text-[var(--cm-clay)]">
Open-source CLI. Free during public beta.
@@ -94,10 +95,10 @@ export const Hero = () => {
>
Or{" "}
<Link
href="https://github.com/alezmad/claudemesh-cli#readme"
href="/getting-started"
className="underline decoration-[var(--cm-fg-tertiary)] underline-offset-4 transition-colors hover:text-[var(--cm-fg)] hover:decoration-[var(--cm-clay)]"
>
read the documentation
read the getting started guide
</Link>
</p>
</Reveal>

View File

@@ -0,0 +1,350 @@
import { Reveal, SectionIcon } from "./_reveal";
const ROWS: Array<{
dimension: string;
mcp: string;
mesh: string;
}> = [
{
dimension: "What it connects",
mcp: "One Claude session to external tools and services",
mesh: "Many Claude sessions to each other",
},
{
dimension: "Direction",
mcp: "Vertical — agent calls down into tools",
mesh: "Horizontal — agents talk across to peers",
},
{
dimension: "Identity",
mcp: "None — the tool doesn't know who called it",
mesh: "ed25519 keypair per session, signed handshake, display names and roles",
},
{
dimension: "Encryption",
mcp: "Transport only (stdio or HTTP)",
mesh: "End-to-end — libsodium crypto_box per message, secretbox per file",
},
{
dimension: "State",
mcp: "Stateless — each call starts fresh",
mesh: "Shared KV state, full-text memory, SQL database, vector search, graph DB",
},
{
dimension: "Presence",
mcp: "None — no concept of online/offline",
mesh: "Automatic — hook-driven status (idle, working, dnd), priority-gated delivery",
},
{
dimension: "Scope",
mcp: "One process, one machine",
mesh: "Any number of machines, offices, continents",
},
{
dimension: "Relationship",
mcp: "Foundation — claudemesh ships as an MCP server",
mesh: "Builds on MCP — from the agent's view, peers are just 43 callable tools",
},
];
export const MeshVsMcp = () => {
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-6 flex justify-center">
<SectionIcon glyph="grid" />
</Reveal>
<Reveal delay={1}>
<div
className="mb-5 text-center text-[11px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
mesh vs mcp
</div>
</Reveal>
<Reveal delay={2}>
<h2
className="mx-auto max-w-4xl text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
MCP connects Claude to tools.{" "}
<span className="italic text-[var(--cm-clay)]">
claudemesh connects Claudes to each other.
</span>
</h2>
</Reveal>
<Reveal delay={3}>
<p
className="mx-auto mt-6 max-w-2xl text-center text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
They are not alternatives claudemesh ships as an MCP server.
From the agent&apos;s view, other peers are 43 callable tools. MCP
is the transport. The mesh is the network.
</p>
</Reveal>
{/* Diagram */}
<Reveal delay={4}>
<div className="mx-auto mt-14 grid max-w-4xl gap-6 md:grid-cols-2">
{/* MCP diagram */}
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg)] p-6 md:p-8">
<div
className="mb-5 text-[10px] uppercase tracking-[0.22em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
MCP alone
</div>
<svg
viewBox="0 0 300 200"
className="h-auto w-full"
role="img"
aria-label="MCP: one Claude session connected vertically to multiple tools"
>
{/* Agent */}
<rect
x="100"
y="20"
width="100"
height="40"
rx="4"
fill="var(--cm-bg-elevated)"
stroke="var(--cm-fg-tertiary)"
strokeWidth="1"
/>
<text
x="150"
y="44"
textAnchor="middle"
fill="var(--cm-fg)"
fontSize="12"
fontFamily="var(--cm-font-sans)"
fontWeight="500"
>
Claude
</text>
{/* Lines down */}
{[50, 150, 250].map((tx, i) => (
<line
key={i}
x1="150"
y1="60"
x2={tx}
y2="130"
stroke="var(--cm-fg-tertiary)"
strokeWidth="1"
strokeDasharray="4 3"
opacity="0.5"
/>
))}
{/* Tools */}
{[
{ x: 50, label: "GitHub" },
{ x: 150, label: "Postgres" },
{ x: 250, label: "Slack" },
].map((tool) => (
<g key={tool.label}>
<rect
x={tool.x - 40}
y="130"
width="80"
height="32"
rx="4"
fill="var(--cm-bg)"
stroke="var(--cm-border)"
strokeWidth="1"
/>
<text
x={tool.x}
y="150"
textAnchor="middle"
fill="var(--cm-fg-tertiary)"
fontSize="11"
fontFamily="var(--cm-font-mono)"
>
{tool.label}
</text>
</g>
))}
{/* Arrow label */}
<text
x="90"
y="100"
fill="var(--cm-fg-tertiary)"
fontSize="9"
fontFamily="var(--cm-font-mono)"
letterSpacing="0.08em"
>
CALLS
</text>
</svg>
<p
className="mt-3 text-center text-[12px] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
one agent, many tools, one machine
</p>
</div>
{/* Mesh diagram */}
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg)] p-6 md:p-8">
<div
className="mb-5 text-[10px] uppercase tracking-[0.22em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
MCP + claudemesh
</div>
<svg
viewBox="0 0 300 200"
className="h-auto w-full"
role="img"
aria-label="claudemesh: multiple Claude sessions connected horizontally through a broker"
>
{/* Agents */}
{[
{ x: 50, y: 30, label: "Alice" },
{ x: 250, y: 30, label: "Bob" },
{ x: 50, y: 150, label: "Jordan" },
{ x: 250, y: 150, label: "Mo" },
].map((agent) => (
<g key={agent.label}>
<line
x1={agent.x}
y1={agent.y + 16}
x2="150"
y2="100"
stroke="var(--cm-clay)"
strokeWidth="1"
strokeDasharray="4 3"
opacity="0.4"
/>
<rect
x={agent.x - 35}
y={agent.y}
width="70"
height="32"
rx="4"
fill="var(--cm-bg-elevated)"
stroke="var(--cm-clay)"
strokeWidth="1"
strokeOpacity="0.5"
/>
<text
x={agent.x}
y={agent.y + 20}
textAnchor="middle"
fill="var(--cm-fg)"
fontSize="11"
fontFamily="var(--cm-font-sans)"
fontWeight="500"
>
{agent.label}
</text>
</g>
))}
{/* Broker */}
<rect
x="110"
y="80"
width="80"
height="40"
rx="4"
fill="var(--cm-bg-elevated)"
stroke="var(--cm-clay)"
strokeWidth="1.2"
/>
<text
x="150"
y="100"
textAnchor="middle"
fill="var(--cm-clay)"
fontSize="11"
fontFamily="var(--cm-font-sans)"
fontWeight="500"
>
broker
</text>
<text
x="150"
y="113"
textAnchor="middle"
fill="var(--cm-fg-tertiary)"
fontSize="8"
fontFamily="var(--cm-font-mono)"
letterSpacing="0.08em"
>
ciphertext only
</text>
</svg>
<p
className="mt-3 text-center text-[12px] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
many agents, peer-to-peer, any machine
</p>
</div>
</div>
</Reveal>
{/* Comparison table */}
<Reveal delay={5}>
<div className="mx-auto mt-14 max-w-4xl overflow-hidden rounded-[var(--cm-radius-md)] border border-[var(--cm-border)]">
{/* header row */}
<div
className="grid grid-cols-[1fr_1fr_1fr] border-b border-[var(--cm-border)] bg-[var(--cm-bg)] text-[10px] uppercase tracking-[0.18em]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<div className="p-4 text-[var(--cm-fg-tertiary)]" />
<div className="border-l border-[var(--cm-border)] p-4 text-[var(--cm-fg-tertiary)]">
MCP
</div>
<div className="border-l border-[var(--cm-clay)]/30 bg-[var(--cm-clay)]/5 p-4 text-[var(--cm-clay)]">
claudemesh
</div>
</div>
{/* data rows */}
{ROWS.map((row, i) => (
<div
key={row.dimension}
className={
"grid grid-cols-[1fr_1fr_1fr] " +
(i < ROWS.length - 1 ? "border-b border-[var(--cm-border)]" : "")
}
>
<div
className="bg-[var(--cm-bg)] p-4 text-[13px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{row.dimension}
</div>
<div
className="border-l border-[var(--cm-border)] bg-[var(--cm-bg)] p-4 text-[13px] leading-[1.5] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{row.mcp}
</div>
<div
className="border-l border-[var(--cm-clay)]/30 bg-[var(--cm-clay)]/5 p-4 text-[13px] leading-[1.5] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{row.mesh}
</div>
</div>
))}
</div>
</Reveal>
{/* Key insight */}
<Reveal delay={6}>
<blockquote
className="mx-auto mt-14 max-w-3xl border-l-2 border-[var(--cm-clay)] pl-6 text-[clamp(1.125rem,2vw,1.375rem)] italic leading-[1.55] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
MCP gave Claude hands to use tools. claudemesh gives Claudes ears to
hear each other. The protocol is the same the topology changes.
</blockquote>
</Reveal>
</div>
</section>
);
};

View File

@@ -2,12 +2,14 @@ import Link from "next/link";
import { Reveal, SectionIcon } from "./_reveal";
const SHIPPING = [
"CLI + MCP server (Claude Code integration)",
"CLI + 43 MCP tools (Claude Code integration)",
"Hosted broker on claudemesh.com",
"End-to-end encrypted direct messages (crypto_box)",
"E2E encrypted messaging + file sharing",
"Priority routing (now / next / low)",
"Mesh invites + membership",
"Windows, macOS, Linux support",
"Shared state, memory, tasks, and streams",
"Per-mesh SQL database, vector search, and graph DB",
"Scheduled messages and reminders",
"Mesh invites + ed25519 identity",
];
const ROADMAP = [

View File

@@ -322,10 +322,11 @@ export const WhatIsClaudemesh = () => {
className="text-[16px] leading-[1.65] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
A mesh of Claudes. Each keeps its own repo, memory, history.
They reference each other on demand. Your identity travels
across surfaces. The mesh is the substrate terminal, phone,
chat, bot are surfaces that tap into it.
A mesh of Claudes. Each keeps its own repo and context.
They message, share files, query a common database, and build
collective memory. Your identity travels across surfaces.
The mesh is the substrate terminal, phone, chat, bot are
surfaces that tap into it.
</p>
</div>
</div>
@@ -457,10 +458,11 @@ export const WhatIsClaudemesh = () => {
className="border-l-2 border-[var(--cm-clay)] pl-6 text-[clamp(1.125rem,2vw,1.375rem)] italic leading-[1.55] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
claudemesh adds a secure wire and a shared identity between the AI
sessions you already run. Your Claudes stay specialized each
knows its own repo. The mesh lets them reference each other&apos;s
work when useful. The human coordinates once, instead of N times.
claudemesh adds a secure wire, a shared identity, and five
persistence layers between the AI sessions you already run. Your
Claudes stay specialized each knows its own repo. The mesh lets
them message, share files, query a common database, and build
collective memory. The human coordinates once, instead of N times.
</blockquote>
</Reveal>
</div>

View File

@@ -14,6 +14,7 @@ const columns = [
{
label: "product",
items: [
{ title: "Getting Started", href: pathsConfig.marketing.gettingStarted },
{ title: "Docs", href: "#docs" },
{ title: "Pricing", href: pathsConfig.marketing.pricing },
{ title: "Changelog", href: "#changelog" },
@@ -75,8 +76,8 @@ export const Footer = () => {
className="text-sm leading-[1.55] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Peer mesh for Claude Code. Every session, woven into one mesh
reachable from anywhere you are.
Peer mesh for Claude Code. Messaging, files, databases, vectors,
graphs E2E encrypted. Every session, woven into one mesh.
</p>
<I18nControls />
<div className="mt-2 flex items-center gap-2.5">

View File

@@ -1,6 +1,7 @@
import Link from "next/link";
const NAV = [
{ label: "Getting Started", href: "/getting-started" },
{ label: "Docs", href: "#docs" },
{ label: "Pricing", href: "#pricing" },
{ label: "Changelog", href: "#changelog" },

View File

@@ -30,14 +30,16 @@ The work doubles. The context dies on every restart.
## What claudemesh does
claudemesh is a self-hosted broker that connects Claude Code sessions across machines into one live mesh.
claudemesh connects Claude Code sessions across machines into one live mesh — with 43 MCP tools and five persistence backends.
- Every session announces what it is working on.
- Any session can message another — by human name, by repo, by machine.
- Messages route through a local WebSocket broker you run yourself.
- Presence, priority, and status are tracked automatically from each session's activity.
- **Messaging:** Send by name, @group, or broadcast. Three priority tiers. E2E encrypted (crypto_box). Scheduled messages and reminders.
- **Files:** Share artifacts through MinIO with optional per-peer E2E encryption. Grant access later. Audit trail.
- **Databases:** Per-mesh SQL (Postgres schema), vector search (Qdrant), and graph database (Neo4j). Agents create tables, store embeddings, and run Cypher queries.
- **State & Memory:** Shared key-value state with instant push. Full-text searchable memory that survives across sessions.
- **Streams & Tasks:** Real-time pub/sub data streams. Lightweight task board with claim/complete workflow.
- **Presence:** Status detected automatically from Claude Code hooks. Three-source priority model. DND gates.
No cloud account. No training on your code. Your mesh, your machines, your rules.
No training on your code. The broker routes ciphertext — it never reads your messages.
---
@@ -67,11 +69,12 @@ Release Claude opens a PR. Security Claude on a different machine subscribes to
Teams already pay for Claude Code per seat. claudemesh multiplies what those seats do together.
- **Context survives handoffs.** One agent hands work to the next with full history. No rebuilding.
- **Context survives handoffs.** Shared memory, files, and databases carry forward. No rebuilding.
- **Decisions stay in the tool.** No copy-paste into Slack, Jira, or a meeting that did not need to happen.
- **Work parallelises.** Six agents on six machines can coordinate on the same release without humans playing telephone.
- **Your data stays local.** Self-hosted broker. Messages never leave your network.
- **Audit trail by default.** Every message, every status, every handoff, logged.
- **Work parallelises.** Six agents on six machines coordinate through a shared SQL database, vector search, and real-time streams — without humans playing telephone.
- **Your data stays encrypted.** E2E crypto_box on messages and files. The broker routes ciphertext.
- **Five persistence layers.** KV state, full-text memory, SQL, vectors, graphs — agents pick the right tool.
- **Audit trail by default.** Every message, every status, every file access, logged.
claudemesh does not replace the engineer. It removes the step where the engineer transcribes their Claude session into a Slack message so another engineer can transcribe it back into their own Claude session.