docs(web): 2-command onboarding — install + launch --join
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

Simplify getting-started to 2 steps: npm install + launch --join.
Remove "claudemesh install" section, update join page to show
launch --join as the primary flow, update invite format examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-08 11:13:21 +01:00
parent 2c24f667f9
commit 643c808685
2 changed files with 46 additions and 130 deletions

View File

@@ -5,7 +5,7 @@ import { getMetadata } from "~/lib/metadata";
export const generateMetadata = getMetadata({ export const generateMetadata = getMetadata({
title: "Getting Started", title: "Getting Started",
description: description:
"Install claudemesh, join a mesh, and launch your first peer session in under two minutes.", "Install the CLI and launch your first peer session in two commands.",
}); });
const STEP = ({ const STEP = ({
@@ -59,10 +59,9 @@ const STEP = ({
const VERIFY_CHECKS = [ const VERIFY_CHECKS = [
"Node.js >= 20 installed", "Node.js >= 20 installed",
"claude binary on PATH", "claude binary on PATH",
"claudemesh MCP registered in ~/.claude.json",
"Status hooks registered in ~/.claude/settings.json",
"~/.claudemesh/config.json parses + chmod 0600", "~/.claudemesh/config.json parses + chmod 0600",
"Mesh keypairs valid", "Mesh keypairs valid",
"Broker connectivity",
]; ];
export default function GettingStartedPage() { export default function GettingStartedPage() {
@@ -84,8 +83,7 @@ export default function GettingStartedPage() {
className="mt-4 max-w-xl text-lg leading-[1.65] text-[var(--cm-fg-secondary)]" className="mt-4 max-w-xl text-lg leading-[1.65] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-serif)" }} style={{ fontFamily: "var(--cm-font-serif)" }}
> >
Install the CLI, join a mesh, and launch Claude Code with real-time peer Install the CLI and launch. Two commands — join is built into launch.
messaging. Three commands.
</p> </p>
{/* Prerequisites */} {/* Prerequisites */}
@@ -163,35 +161,33 @@ export default function GettingStartedPage() {
<STEP <STEP
n="2" n="2"
title="Join a mesh" title="Launch"
cmd="claudemesh join https://claudemesh.com/join/eyJ2IjoxLC..." cmd='claudemesh launch --name Alice --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." note="--join enrolls you in the mesh (first time only). On subsequent launches, drop the --join flag."
> >
<p> <p>
Paste the invite link you received. Your ed25519 keypair is This does everything: verifies the invite, generates your ed25519
generated and stored in{" "} keypair, enrolls with the broker, and spawns Claude Code with
real-time peer messaging. Your keys are stored in{" "}
<code <code
className="rounded bg-[var(--cm-bg-elevated)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg-secondary)]" 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)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
~/.claudemesh/config.json ~/.claudemesh/config.json
</code>{" "} </code>{" "}
(chmod 0600). You keep your keys — the broker never sees them. (chmod 0600) — the broker never sees them.
</p> </p>
</STEP> </STEP>
<STEP <div
n="3" className="py-3 text-center text-xs text-[var(--cm-fg-tertiary)]"
title="Launch with real-time messaging" style={{ fontFamily: "var(--cm-font-mono)" }}
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> next time, just:
This spawns Claude Code connected to the mesh with push messaging. <code className="ml-2 rounded bg-[var(--cm-bg-elevated)] px-2 py-1 text-[var(--cm-fg-secondary)]">
The interactive wizard asks for your role and groups — or pass them claudemesh launch --name Alice
as flags: </code>
</p> </div>
</STEP>
<pre <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)]" 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)]"
@@ -200,6 +196,7 @@ export default function GettingStartedPage() {
<code>{`# Full example with all flags <code>{`# Full example with all flags
claudemesh launch \\ claudemesh launch \\
--name Alice \\ --name Alice \\
--join https://claudemesh.com/join/eyJ2IjoxLC... \\
--role dev \\ --role dev \\
--groups "frontend:lead,reviewers" \\ --groups "frontend:lead,reviewers" \\
--message-mode push \\ --message-mode push \\
@@ -227,83 +224,18 @@ claudemesh launch \\
style={{ fontFamily: "var(--cm-font-mono)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
<code>{`$ claudemesh doctor <code>{`$ claudemesh doctor
claudemesh doctor (v0.6.8) claudemesh doctor (v0.8.0)
──────────────────────────────────────────────────────────── ────────────────────────────────────────────────────────────
✓ Node.js >= 20 (v22.15.0) ✓ Node.js >= 20 (v22.15.0)
✓ claude binary on PATH ✓ claude binary on PATH
✓ claudemesh MCP registered in ~/.claude.json
✓ Status hooks registered in ~/.claude/settings.json
✓ ~/.claudemesh/config.json parses + chmod 0600 ✓ ~/.claudemesh/config.json parses + chmod 0600
✓ Mesh keypairs valid (1 mesh(es)) ✓ Mesh keypairs valid (1 mesh(es))
✓ Broker connectivity (wss://ic.claudemesh.com/ws)
All checks passed.`}</code> All checks passed.`}</code>
</pre> </pre>
</div> </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 */} {/* Invite a teammate */}
<div className="mt-16"> <div className="mt-16">
<h2 <h2
@@ -336,11 +268,10 @@ All checks passed.`}</code>
className="rounded bg-[var(--cm-bg-elevated)] px-1.5 py-0.5 text-[12px] text-[var(--cm-fg)]" 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)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
claudemesh join &lt;link&gt; claudemesh launch --name Name --join &lt;link&gt;
</code>{" "} </code>{" "}
— the CLI verifies the signature client-side before enrolling with — joins the mesh and launches in one step. No account creation
the broker. No account creation needed. Identity is the ed25519 needed. Identity is the ed25519 keypair.
keypair.
</p> </p>
</div> </div>
@@ -356,17 +287,18 @@ All checks passed.`}</code>
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)]" 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)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
<code>{`# HTTPS link (clickable, shareable) <code>{`# Join + launch in one step (recommended)
claudemesh launch --name Alice --join https://claudemesh.com/join/eyJ2IjoxLC...
# Or join separately first
claudemesh join https://claudemesh.com/join/eyJ2IjoxLC... claudemesh join https://claudemesh.com/join/eyJ2IjoxLC...
claudemesh launch --name Alice
# With locale prefix (also works) # All invite formats work with both join and --join:
claudemesh join https://claudemesh.com/en/join/eyJ2IjoxLC... # https://claudemesh.com/join/eyJ2IjoxLC...
# https://claudemesh.com/en/join/eyJ2IjoxLC...
# ic:// scheme (legacy, still supported) # ic://join/eyJ2IjoxLC...
claudemesh join ic://join/eyJ2IjoxLC... # eyJ2IjoxLC4uLg (raw token)`}</code>
# Raw token (last resort)
claudemesh join eyJ2IjoxLC4uLg`}</code>
</pre> </pre>
</div> </div>
@@ -445,10 +377,11 @@ claudemesh join eyJ2IjoxLC4uLg`}</code>
style={{ fontFamily: "var(--cm-font-serif)" }} style={{ fontFamily: "var(--cm-font-serif)" }}
> >
<li>Real-time push messages from peers</li> <li>Real-time push messages from peers</li>
<li>Native MCP entries for deployed mesh services</li>
<li>Per-session ephemeral keypair</li> <li>Per-session ephemeral keypair</li>
<li>Display name visible to other peers</li> <li>Display name, groups, and roles</li>
<li>Groups and roles set at launch</li>
<li>Session config isolated in tmpdir</li> <li>Session config isolated in tmpdir</li>
<li>MCP_TIMEOUT + output limits tuned for mesh</li>
</ul> </ul>
</div> </div>
<div className="bg-[var(--cm-bg-elevated)] p-6"> <div className="bg-[var(--cm-bg-elevated)] p-6">

View File

@@ -5,6 +5,7 @@ interface Props {
token: string; token: string;
} }
const LAUNCH_CMD = (token: string) => `claudemesh launch --name YourName --join ${token}`;
const JOIN_CMD = (token: string) => `claudemesh join ${token}`; const JOIN_CMD = (token: string) => `claudemesh join ${token}`;
const INSTALL_CMD = "npm i -g claudemesh-cli"; const INSTALL_CMD = "npm i -g claudemesh-cli";
@@ -60,7 +61,7 @@ export const InstallToggle = ({ token }: Props) => {
} }
if (hasCli === "yes") { if (hasCli === "yes") {
const cmd = JOIN_CMD(token); const cmd = LAUNCH_CMD(token);
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5"> <div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5">
@@ -68,7 +69,7 @@ export const InstallToggle = ({ token }: Props) => {
className="mb-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-clay)]" className="mb-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
run this in your terminal join + launch in one step
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<code <code
@@ -96,7 +97,7 @@ export const InstallToggle = ({ token }: Props) => {
); );
} }
const joinCmd = JOIN_CMD(token); const launchCmd = LAUNCH_CMD(token);
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<ol className="space-y-3"> <ol className="space-y-3">
@@ -127,7 +128,7 @@ export const InstallToggle = ({ token }: Props) => {
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]" className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }} style={{ fontFamily: "var(--cm-font-serif)" }}
> >
Installs the CLI globally. Requires Node.js 20+. Requires Node.js 20+.
</p> </p>
</li> </li>
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5"> <li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5">
@@ -136,46 +137,28 @@ export const InstallToggle = ({ token }: Props) => {
style={{ fontFamily: "var(--cm-font-mono)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
<span className="rounded-full bg-[var(--cm-clay)]/20 px-1.5">2</span> <span className="rounded-full bg-[var(--cm-clay)]/20 px-1.5">2</span>
join the mesh join + launch
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<code <code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]" 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)" }} style={{ fontFamily: "var(--cm-font-mono)" }}
> >
{joinCmd} {launchCmd}
</code> </code>
<button <button
onClick={() => copy(joinCmd, "join")} onClick={() => copy(launchCmd, "join")}
className="rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-3 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:bg-[var(--cm-clay-hover)]" className="rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-3 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }} style={{ fontFamily: "var(--cm-font-sans)" }}
> >
{copiedKey === "join" ? "Copied ✓" : "Copy"} {copiedKey === "join" ? "Copied ✓" : "Copy"}
</button> </button>
</div> </div>
</li>
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-border)] px-1.5">3</span>
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 <p
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]" className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }} style={{ fontFamily: "var(--cm-font-serif)" }}
> >
Spawns Claude Code with mesh connectivity, peer messaging, Joins the mesh and launches Claude Code in one step.
and native access to deployed MCP services.
</p> </p>
</li> </li>
</ol> </ol>