Commit Graph

65 Commits

Author SHA1 Message Date
Alejandro Gutiérrez
541440c357 feat(web): unread badge on dashboard mesh cards
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
Universe page aggregates unread topic_message rows per mesh for the
viewing user. Counts messages newer than topic_member.last_read_at
(or all messages if the viewer never opened the topic) and excludes
anything the viewer authored. One JOIN-grouped query, not N+1.

Mesh card surfaces the count as a clay-rounded badge to the left of
the role chip — matches the per-topic badge style on the mesh detail
page so unread is the same visual idiom across the dashboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:08:11 +01:00
Alejandro Gutiérrez
a80eb6fcca feat(api+web): unread counts per topic + PATCH /read mark-as-read
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
PATCH /v1/topics/:name/read upserts topic_member.last_read_at for the
api key's issuing member. The chat panel calls it on mount and on
every inbound SSE message (5s debounce so we don't hammer it).

GET /v1/topics now returns unread per topic — counts messages newer
than last_read_at and not authored by the viewer. Mesh detail page
shows a clay-rounded badge next to each topic name with the count
(99+ ceiling).

AuthedApiKey gains issuedByMemberId so endpoints can attribute
side-effects to the minting member. Required because external api
keys aren't tied to a specific peer member; only dashboard- and
CLI-minted keys carry one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:06:01 +01:00
Alejandro Gutiérrez
7e71a61db4 feat(api+web): stream topic chat live over server-sent events
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
GET /v1/topics/:name/stream opens an SSE firehose, polled server-side
every 2s and streamed as `message` events. Forward-only — clients
hit /messages once for backfill, then live from connect-time onward.
Heartbeats every 30s keep the connection through proxies.

Web chat panel reads the stream via fetch + ReadableStream so the
bearer token stays in the Authorization header (EventSource can't
set custom headers, which would force token-in-URL leaks). Auto-
reconnect with exponential backoff. setInterval polling removed.

Vercel maxDuration bumped to 300s on the catch-all API route so
streams aren't cut at the 10s default.

drizzle migrations/meta/ deleted — superseded by the filename-
tracked custom runner in apps/broker/src/migrate.ts (c2cd67a).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 19:02:38 +01:00
Alejandro Gutiérrez
f727620d16 feat(web): topic discoverability — counts on cards + inline creation
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
Two UX wins for the v0.2.0 chat surface:

- Mesh cards on /dashboard now show topic count alongside members and
  tier ("3 MEMBERS · 2 TOPICS · FREE"). Active topics render in clay,
  zero in tertiary. One aggregate query, not N+1.
- Mesh detail page replaces the CLI-hint empty state with an inline
  CreateTopicForm. Non-empty topic lists get a compact "+ new topic"
  pill in the section header. Server action validates name format
  (lowercase letters/digits/dashes, 1-50 chars), inserts via the
  unique (meshId, name) index, auto-subscribes the creator as topic
  lead, then redirects into the chat.

Sidebar audit — kept platform/manage/dev structure as is. Topics are
mesh-scoped so a top-level "topics" entry would have nothing to land
on without a mesh chosen first. Discoverability lives on the mesh
cards instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:27:19 +01:00
Alejandro Gutiérrez
b60daff886 feat(web): topic chat UI over /api/v1/* (v0.2.0)
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
New dashboard route at /dashboard/meshes/[id]/topics/[name] gives signed-in
users a thin chat client over the v0.2.0 REST surface. The mesh detail page
now lists topics with one-click links into the chat. Backend layout:

- packages/api/src/modules/mesh/api-key-auth.ts — exports
  createDashboardApiKey() that mints a 24h read+send key scoped to a single
  topic for the caller's member id. The page server component calls this on
  every render and embeds the secret in the props of the client component;
  the secret never touches sessionStorage so a tab close = key effectively
  abandoned (the row remains until expiresAt).
- apps/web/.../topics/[name]/page.tsx — server component, NextAuth gate,
  resolves the user's meshMember.id, mints the key, renders the shell.
- apps/web/src/modules/mesh/topic-chat-panel.tsx — client component, polls
  GET /v1/topics/:name/messages every 5s, sends via POST /v1/messages.
  Encoding wraps base64(plaintext) into the ciphertext field — matches the
  current broker contract until per-topic HKDF lands in v0.3.0.

The mesh detail page gains a Topics section with empty-state copy that
points users at the CLI verb (claudemesh topic create) for now; topic
creation from the web UI is a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:19:38 +01:00
Alejandro Gutiérrez
0664180a54 feat(web): universe dashboard — meshes + incoming invitations
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
New /dashboard landing that surfaces meshes and invitations-to-you
in one view. Replaces the simple mesh grid at /dashboard (preserved
at /dashboard/legacy).

Backend additions:
- GET /api/my/invites/incoming — pending_invite rows addressed to
  the authed user's email, joined with invite for role + expiry and
  user/mesh for display. Unaccepted + unrevoked + unexpired only.
- DELETE /api/my/invites/incoming/:id — dismiss a pending invite
  (revokes the pending_invite row only; underlying invite code stays
  valid so the inviter can re-send).

Web additions (all under apps/web/src/modules/dashboard/universe/):
- welcome.tsx — editorial serif header with mesh + invite counts
- invitations.tsx — client card with Accept (→ /i/:code claim flow)
  and optimistic Decline
- meshes-grid.tsx — hero card + compact grid, linked to mesh detail
- reveal.tsx — fade-up motion matching marketing _reveal.tsx

Styling uses the existing claudemesh design tokens (--cm-clay,
--cm-bg-elevated, Anthropic Sans/Serif/Mono) — nothing redefined.

Onboarding redirect (0 meshes → /meshes/new?onboarding=1) preserved,
now gated on 0 invitations too so users with pending invites still
land on the dashboard.

Sidebar icon switched to Atom for the "universe" concept.

Standalone prototype saved at prototypes/live-dashboard.html for
reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:31:15 +01:00
Alejandro Gutiérrez
45d85f5eaa chore: wrap up the gap-closing session
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
- info/inbox commands → unified render.ts
- install route: drop in-memory counter, rely on PostHog + structured logs
- docs: roadmap, CLAUDE.md reflect alpha.31 state
- tests workflow now also builds + smoke-tests the CLI bundle
- homebrew tap bootstrap kit in packaging/homebrew-tap-bootstrap/
  (README + copy of the formula template for dropping into the tap repo)
- upstream Claude Code issue draft for rich <channel> UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:53:59 +01:00
Alejandro Gutiérrez
ee12510ef1 refactor: rename cli-v2 → cli, archive legacy cli, plus broker-side grants + auto-migrate
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
- apps/cli/ is now the canonical CLI (was apps/cli-v2/).
- apps/cli/ legacy v0 archived as branch 'legacy-cli-archive' and tag
  'cli-v0-legacy-final' before deletion; git history preserves it too.
- .github/workflows/release-cli.yml paths updated.
- pnpm-lock.yaml regenerated.

Broker-side peer-grant enforcement (spec: 2026-04-15-per-peer-capabilities):
- 0020_peer-grants.sql adds peer_grants jsonb + GIN index on mesh.member.
- handleSend in broker fetches recipient grant maps once per send, drops
  messages silently when sender lacks the required capability.
- POST /cli/mesh/:slug/grants to update from CLI; broker_messages_dropped_by_grant_total metric.
- CLI grant/revoke/block now mirror to broker via syncToBroker.

Auto-migrate on broker startup:
- apps/broker/src/migrate.ts runs drizzle migrate with pg_advisory_lock
  before the HTTP server binds. Exits non-zero on failure so Coolify
  healthcheck fails closed.
- Dockerfile copies packages/db/migrations into /app/migrations.
- postgres 3.4.5 added as direct broker dep.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:44:52 +01:00
Alejandro Gutiérrez
ccf95ff382 feat(distribution): binary release pipeline + brew + winget
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
- .github/workflows/release-cli.yml: build self-contained binaries via
  `bun build --compile` for darwin/linux/windows × x64/arm64 on every
  cli-v* tag, attach to GitHub Release with SHA256SUMS, auto-bump the
  homebrew tap on non-prerelease versions.
- packaging/homebrew/claudemesh.rb.template: formula template for the
  homebrew-claudemesh tap.
- packaging/winget/claudemesh.yaml.template: winget manifest template.
- /install script now detects absence of Node and downloads the
  platform-appropriate binary from the GitHub Release, installs to
  ~/.claudemesh/bin, and shims into ~/.local/bin — zero Node required.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 02:42:16 +01:00
Alejandro Gutiérrez
d33b8fc43b feat(web): install.sh and InstallToggle use one-command UX
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
- /install shell script now points users at `claudemesh <invite-url>`
  (one step) instead of the split join+launch
- InstallToggle first-time panel shows single copy-block with
  install+launch on the same line
- Also advertises url-handler install and shell completions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 02:33:36 +01:00
Alejandro Gutiérrez
d07cff788c feat: three-token auth flow (session_id + user_code + device_code)
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
- session_id (clm_sess_...) in browser URL — identifies login attempt
- user_code (ABCD-EFGH) visual confirmation — shown in both terminal and browser
- device_code (secret) — CLI polls with this, never displayed
- CLI accepts stdin paste of JWT token while polling (race)
- Web page handles both ?session= and ?code= params

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:19:08 +01:00
Alejandro Gutiérrez
ea4e3b03bb feat: paste-token auth flow for CLI
Some checks failed
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
- Broker: POST /cli/token generates a 30-day JWT
- Web: /token page with Generate + Copy button
- Web: /api/auth/cli/token proxies to broker
- CLI: login option 3 "Paste a token" for headless environments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:17:38 +01:00
Alejandro Gutiérrez
1a42c2ef09 chore: trigger Vercel redeploy
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:58:17 +01:00
Alejandro Gutiérrez
465ff9a10e fix(web): rewrite CLI auth login as standalone component
Remove dependency on SocialProviders/RegisterForm which need
React Query providers. Self-contained with authClient directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:06:42 +01:00
Alejandro Gutiérrez
a365fef170 feat(web): dedicated CLI auth page with inline login/register
No more redirect to generic /auth/login. The /cli-auth?code=XXXX page
now shows auth forms inline (Google, GitHub, email) with device code
context — like Anthropic's "Build with Claude" page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:51:18 +01:00
Alejandro Gutiérrez
ca441dae45 feat(broker): device-code auth with PostgreSQL persistence
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
- Drizzle schema: device_code + cli_session tables in mesh pgSchema
- Broker endpoints: POST /cli/device-code, GET /cli/device-code/:code,
  POST /cli/device-code/:code/approve, GET /cli/sessions
- Web app API routes now proxy to broker (no in-memory state)
- Tracks devices per user: hostname, platform, arch, last_seen, token_hash
- JWT signed with CLI_SYNC_SECRET, 30-day expiry
- Session revocation support via revokedAt column

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:22:13 +01:00
Alejandro Gutiérrez
ac709dbe92 feat(web): add device-code OAuth API for CLI authentication
New API endpoints:
- POST /api/auth/cli/device-code/new — issue device code + user code
- GET /api/auth/cli/device-code/[code] — poll device code status
- POST /api/auth/cli/device-code/[code]/approve — approve by device code
- POST /api/auth/cli/device-code/approve-by-user-code — approve by user code

Updated cli-auth page to auto-approve on page load after authentication
(no manual "Approve" button click needed).

Enables `claudemesh login` and `claudemesh register` CLI commands.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:10:09 +01:00
Alejandro Gutiérrez
5e97d48cd5 feat(web): animated mesh hero with peer constellation + comparison section
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
- New hero section with a live animated mesh background: three equal
  Claude Code peers in a triangle layout + six desaturated background
  peers, all rendered pixel-perfect from pure React/CSS using the exact
  Unicode characters and colors from Claude Code's own source.
- User prompts type into the bottom prompt-input box and "submit" to
  scrollback (matching real Claude Code behavior). Mesh sends fly as
  envelope icons with fading trails between peers; receivers pulse on
  arrival. Dynamic routing by peer displayName.
- Radial vignette overlay keeps the hero title crisp while letting the
  corner peers pulse visibly around the edges. Top/bottom linear fades
  bleed into adjacent sections.
- Responsive scaling via ResizeObserver: cover-fit in hero bg context,
  contain-fit for standalone use.
- Features section: added Skills, MCPs, and Commands as the first
  three tabs — the mesh's real differentiators. Updated subtitle copy.
- New "Where claudemesh fits" section positioned between Features and
  WhatIsClaudemesh: four-card comparison (vs MCP, vs subagents,
  vs OpenClaw, and the positive claim) framing claudemesh as a wire
  between Claude Code sessions, not a replacement for any of them.

All work is additive: 10 new files in apps/web/src/modules/marketing/
home/fake-claude-code/ plus hero-mesh-animation.tsx, hero-with-mesh.tsx,
and where-mesh-fits.tsx. Single edit each to features.tsx and
(marketing)/page.tsx to swap in the new hero and mount the new section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:39:24 +01:00
Alejandro Gutiérrez
c1fa3bcb5c feat: anthropic-style mesh + invite redesign (wave 1 checkpoint)
Ships the user-visible friction fixes and the foundation for the v2
invite protocol. API wiring + CLI client + email UI ship in wave 2.

Meshes — shipped
- Drop global UNIQUE on mesh.slug; mesh.id is canonical everywhere
- Server derives slug from name; create form has no slug field
- Two users can freely name their mesh "platform"; no collision errors
- Migration 0017

Invites v1 — shipped (URL shortener, backward compatible)
- New invite.code column (base62, 8 chars, nullable unique index)
- createMyInvite mints both token + short code; returns shortUrl
- GET /api/public/invite-code/:code resolves short code to token
- New route /i/[code] server-redirects to /join/[token]
- Invite generator UI shows short URL; QR encodes short URL
- Advanced fields (role/maxUses/expiresInDays) collapsed under disclosure
- Migration 0018

Invites v2 — foundation (broker + DB only; API+CLI+Web wiring in wave 2)
- Broker: canonicalInviteV2, verifyInviteV2, sealRootKeyToRecipient
- Broker: POST /invites/:code/claim endpoint (atomic single-use accounting)
- Broker tests: invite-v2.test.ts (signature, expiry, revocation, exhaustion)
- DB: mesh.invite gains version/capabilityV2/claimedByPubkey columns
- DB: new mesh.pending_invite table for email invites
- Migration 0019
- Contract locked in docs/protocol.md §v2 + SPEC.md §14b

Consent landing — shipped
- /join/[token] redesigned: explicit role, inviter, mesh stats, consent
- New server components: invite-card, role-badge, inviter-line, consent-summary
- "Join [mesh] as [Role]" primary action (not just "Join")

Error surfacing — shipped
- handle() now parses {error} responses from hono route catch blocks
- onError fallback includes timestamp so handle() can match apiErrorSchema
- Real error messages reach the UI instead of "Something went wrong"

Docs
- SPEC.md §14b: v2 invite protocol
- docs/protocol.md: v2 claim wire format
- docs/roadmap.md: status
- .artifacts/specs/2026-04-10-anthropic-vision-meshes-invites.md

Deferred to wave 2/3
- API claim route wiring (packages/api)
- createMyInvite v2 capability generation
- Email invite mutation + Postmark delivery
- CLI v2 join flow (x25519 keypair + unseal)
- Web invite-generator email field + v2 display

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:41:11 +01:00
Alejandro Gutiérrez
5df2664bae feat(web): rewrite hero (pain-first) + streamline page + enterprise tier
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
Hero: 'Your Claude Code sessions work alone. claudemesh connects
them.' Three pain cards (context dies, teams relay by hand, setup
per developer). Solution paragraph focuses on shared wire with
E2E encryption.

Page: removed 5 redundant sections (Surfaces, LaptopToLaptop,
MeshVsMcp, MeetsYou, BeyondTerminal, DemoDashboard). Kept the
strongest: Hero, Features, WhatIsClaudemesh, Timeline, Pricing,
FAQ, CTA, MeshStats.

Pricing: added Enterprise tier with Contact sales → info@claudemesh.com.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 02:00:27 +01:00
Alejandro Gutiérrez
e6962f1454 feat(web): /install route with server-side tracking
Some checks failed
CI / Docker build (linux/amd64) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
Restores the curl installer script at /install. Adds:
- In-memory fetch counter (visible in container logs)
- Server-side PostHog event 'install_script_fetched' with
  IP, user-agent, and referer (fire-and-forget)
- Console log per fetch for monitoring

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:57:37 +01:00
Alejandro Gutiérrez
ed8dab8bd3 fix(web): update email to alex@mourente.ai + correct LinkedIn URL
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:44:48 +01:00
Alejandro Gutiérrez
0661e6223a fix(web): correct LinkedIn URL on about page
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 13:17:24 +01:00
Alejandro Gutiérrez
18dc29aba1 feat(web): timeline section — 66 releases, every feature shipped
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
Editorial timeline with vertical track, colored phase markers,
2-column feature grids per milestone. Shows v0.1→v0.8 evolution:
Foundation → Groups → Shared Intelligence → Files → Data Platform
→ Platform. Anchored by '66 npm releases. Every feature below is
in production today.' Dashed 'next' card at bottom for roadmap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:50:52 +01:00
Alejandro Gutiérrez
643c808685 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>
2026-04-08 11:13:21 +01:00
Alejandro Gutiérrez
2c24f667f9 refactor(web): remove install script, simplify onboarding to 3 steps
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
CI / Lint (push) Has been cancelled
Drop /install route (curl|bash script). Install is just `npm i -g
claudemesh-cli`. Update hero, FAQ, getting-started, and join flow to
reflect the simplified 3-step onboarding: install → join → launch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:09:17 +01:00
Alejandro Gutiérrez
6ab3fbbea3 fix(web): fix getting-started metadata export, use TURBOPACK=0 env for build
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
- generateMetadata instead of metadata (getMetadata returns a function)
- Use TURBOPACK=0 env prefix instead of --no-turbopack flag (not recognized in Docker)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 00:37:10 +01:00
Alejandro Gutiérrez
7d432b3aaa feat(web): add state timeline and resource panels to live mesh dashboard
Two new panels below the existing peer graph + live stream grid:
- StateTimelinePanel: vertical timeline of audit events and presence
  status changes, auto-scrolling, sorted newest-first
- ResourcePanel: 2x2 card grid showing live peers, envelopes by
  priority, audit event breakdown, and session status

Both share the same TanStack Query cache key as the existing panels
(no extra API calls). Matches the --cm-* dark terminal aesthetic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:50:18 +01:00
Alejandro Gutiérrez
59332dc47d feat(web): add peer graph visualization to live mesh dashboard
Renders peers as SVG nodes in a radial layout with animated edges
showing real-time message traffic. Shares the same TanStack Query
cache as LiveStreamPanel (same queryKey). Side-by-side on desktop,
stacked on mobile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 23:32:41 +01:00
Alejandro Gutiérrez
5cb4cc4fe7 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>
2026-04-07 22:58:22 +01:00
Alejandro Gutiérrez
ec9626503c fix(web): force-dynamic on payload admin page (build CSS error)
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:16:21 +01:00
Alejandro Gutiérrez
0b4e389f2b feat(web): restore payload CMS (cuidecar pattern + importMap)
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
Release / Publish multi-arch images (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:30:16 +01:00
Alejandro Gutiérrez
d8bafe3144 fix(web): fully remove payload runtime from production build
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
Remove ALL Payload imports, withPayload wrapper, and (payload)
routes. Blog index + changelog are now static data arrays.
Blog post at /blog/peer-messaging-claude-code is static TSX.

Payload CMS stays as a dev dependency for future local admin
but has zero presence in the production build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:25:02 +01:00
Alejandro Gutiérrez
2be08ab85f fix(web): withPayload + redirect admin + externalized packages
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
Final working pattern: withPayload via require() for build
compatibility, admin page replaced with redirect (no RootPage
import = no React #130), payload packages externalized from
turbopack bundle. Blog/changelog use server-side getPayload().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:16:38 +01:00
Alejandro Gutiérrez
9cefe863e3 fix(web): fully remove withPayload + admin routes from prod
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
withPayload crashes ALL routes with React #130 in standalone
output — even with admin page replaced by redirect. The wrapper
injects a client-side ConfigProvider that fails hydration.

Removed: withPayload wrapper, entire (payload) route group.
Kept: payload.config.ts, migrations, blog/changelog server-side
queries with graceful DB fallback.

Payload admin runs on local dev only (add withPayload back in
next.config when running pnpm dev). Production content via
static TSX pages or future API-based publishing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 02:30:26 +01:00
Alejandro Gutiérrez
78c80cc43c fix(web): withPayload for build, admin redirects to home
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
Keep withPayload (needed for build compilation) but replace the
admin RootPage with a redirect. The RootPage's ConfigProvider
causes React #130 in standalone output. Blog/changelog use
server-side getPayload() which works fine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 02:26:13 +01:00
Alejandro Gutiérrez
d14bdf6b5a fix(web): regenerate payload importMap for /payload route
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 02:01:16 +01:00
Alejandro Gutiérrez
f1af8c0a79 fix(web): payload at /payload route (cuidecar pattern)
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
Replicate working cuidecar Payload setup:
- require() instead of ESM import for withPayload
- routes.admin = "/payload" to avoid /admin conflicts
- (payload)/payload/ route group with own layout + importMap

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:51:06 +01:00
Alejandro Gutiérrez
96cae38196 fix(web): remove payload admin routes + withPayload (stabilize prod)
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
Payload CMS integration crashes the entire production app — the
withPayload wrapper + admin routes break when DB tables don't
exist and the layout conflicts with i18n routing.

Keeping: payload.config.ts, blog/changelog pages with graceful
DB fallback, static blog post page. Payload admin will be added
back once properly integrated with a dedicated route group that
doesn't inherit the main app layout.

The blog post at /blog/peer-messaging-claude-code is static TSX
and works without Payload runtime.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:48:25 +01:00
Alejandro Gutiérrez
c5bf1c303f feat(web): publish blog post as static page
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
Static TSX page at /blog/peer-messaging-claude-code while Payload
admin is not yet configured in production. Full 1100-word post on
protocol, dev-channels, prompt-injection, and next steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:29:17 +01:00
Alejandro Gutiérrez
b758fe07ff fix(web): graceful fallback when payload db unavailable
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
Production has no SQLite — Payload pages now catch connection
errors and render empty state instead of crashing with React #130.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:21:04 +01:00
Alejandro Gutiérrez
8de952d91b fix(web): force-dynamic on payload pages (no DB at build time)
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:15:53 +01:00
Alejandro Gutiérrez
8bd8d1ff76 fix(web): remove payload REST API route + cli backup guards
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
Remove Payload's /api/[...slug] route that conflicts with existing
/api/[...route]. Blog/changelog pages use Payload's local API.

Includes cli install.ts backup + assertNoMcpLoss guards (from
worktree agent).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:11:09 +01:00
Alejandro Gutiérrez
8e906daf6f feat(web): /about page — builder story + background
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:23:49 +01:00
Alejandro Gutiérrez
de684c44bb feat(web): payload cms v3 + blog + changelog data model
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
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:22:40 +01:00
Alejandro Gutiérrez
00b5ba8190 feat(web): /install shell script + real curl one-liner on landing
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 showed \`curl -fsSL claudemesh.sh/install | bash\` but
the domain didn't resolve, so anyone copy-pasting got a DNS error.

Ship:
- apps/web/src/app/install/route.ts: GET returns an auditable bash
  installer (Node preflight, npm install -g claudemesh-cli, runs
  claudemesh install, prints next steps, colored output). No Node
  auto-install — fails clean if missing with a pointer.
- apps/web/src/proxy.ts: exclude /install from the i18n matcher so
  Next.js returns the shell script unmangled.
- hero.tsx + features.tsx: swap claudemesh.sh → claudemesh.com.

Test: curl http://localhost:3000/install | bash -n → OK.
Content-Type: text/x-shellscript; charset=utf-8.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:37:39 +01:00
Alejandro Gutiérrez
6acfc252b0 feat(web): public /join/[token] page + https invite url
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
Clickable HTTPS invite URLs replace the raw ic://join/<token> as the
primary share format. Someone receiving a link in Slack now lands on
a friendly page with install instructions, not a dead-end.

Backend:
- createMyInvite returns a new joinUrl field
  (https://claudemesh.com/join/<token>) alongside the existing
  ic://join/<token> inviteLink and raw token. Schema + Hono route
  updated. ic:// scheme stays — CLI parses both.
- New GET /api/public/invite/:token in packages/api/src/modules/public/
  (unauthed). Decodes the base64url payload, verifies ed25519
  signature against owner_pubkey using the same canonicalInvite()
  contract the broker enforces on join, then joins mesh/invite/user
  to return the shape needed by the landing page. Does NOT mutate
  usedCount — this is a read-only preview.
- Error taxonomy: malformed | bad_signature | expired | revoked |
  exhausted | mesh_archived | not_found. Each returned with any
  metadata we CAN surface (meshName, inviterName, expiresAt) so the
  error page can be specific ("ask Jordan for a new one").
- cache-control: public max-age=30 on valid invites, no-store on
  errors (reasons flip as state changes).

Frontend:
- New public route /[locale]/join/[token] (no auth). Server
  Component fetches the preview endpoint, branches on valid/invalid,
  renders a minimal landing-design-language shell (wordmark header,
  clay accents, serif headlines, mono commands).
- Valid-invite view: "You're invited to {meshName}", inviter +
  role + member-count lede, install-toggle component.
- Invalid-invite view: per-reason error copy + inviter name when
  available + link back to /.
- InstallToggle client component: three-way state
  (unknown/yes/no). Asks "first time / already set up?", then shows
  either the 3-step install+init+join path with per-step copy
  buttons, or the single claudemesh join <token> command for users
  who have the CLI. Every code block has copy-to-clipboard.
- Security footer: "ed25519 keypair generated locally, you keep
  your keys, broker sees ciphertext only, leave anytime with
  claudemesh leave <mesh-slug>".

Invite generator (/dashboard/meshes/[id]/invite):
- QR code now encodes the HTTPS joinUrl instead of ic:// (phone
  cameras land on the web page → friendly path).
- Primary CTA copies the HTTPS URL. Secondary "Copy CLI command"
  for fast-path users. Footer explanation updated.

CLI coordination note: dispatched to broker/db lane — claudemesh CLI
needs to accept BOTH ic://join/<token> AND
https://claudemesh.com/join/<token> (extract <token> from pathname).
Server side already returns both.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:36:24 +01:00
Alejandro Gutiérrez
509af3afe0 feat(web): public mesh stats counter + /api/public/stats endpoint
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
Live social-proof counter on the landing page, tied to the E2E
narrative. Formatted as understated mono footer, not hero brag.

Backend — new GET /api/public/stats (unauthed, 60s in-memory cache):
  {
    messagesRouted: SELECT COUNT(*) FROM mesh.message_queue,
    meshesCreated: SELECT COUNT(*) FROM mesh.mesh WHERE archivedAt IS NULL,
    peersActive: SELECT COUNT(*) FROM mesh.presence WHERE disconnectedAt IS NULL,
    lastUpdated: ISO timestamp,
  }

Aggregate counts only — no ids, no names, no ciphertext, no routing
metadata. Safe for public consumption. cache-control header sets
public/s-maxage=60 for edge caching. `x-cache: HIT|MISS` for debug.

Frontend — new MeshStats Server Component at
modules/marketing/home/mesh-stats.tsx. Reads the endpoint server-side
via the ~/lib/api/server client, renders monospace footer:

  ciphertext routed → 4,217 messages · 12 meshes · 8 peers online
  broker sees none of it

Graceful zero state: when messagesRouted === 0 shows
"ciphertext → ready to route" instead of embarrassing zeros. Tabular-
nums for the numeric spans so they don't jitter across renders.

Mounted between <CallToAction /> and <LatestNewsToaster />. Page-level
`export const revalidate = 60` so Next.js ISR refreshes the counter
every minute without a DB hit on every request (combined with the
API cache = two-layer 60s TTL, DB sees ~1 query/minute).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:00:00 +01:00
Alejandro Gutiérrez
e91fc80bbc fix(web): emoji → inline SVG icons for claude.ai-style visual consistency
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Anthropic design language is icon-only, no emoji. User flagged that
claude-intercom components (and copy I wrote) were leaning on emoji
decoration. Swept all user-visible emojis in apps/web + packages/ui.

Changes:
- meshes/new onboarding banner: "Welcome to claudemesh 👋" → drop the
  wave, text stands alone
- meshes/[id]/invite banner: "🎉 Mesh created" → "Mesh created"
- demo-dashboard script message: "thanks 🙏" → "thanks." (inline prose)
- MeshStream message-type chips: replaced the ⟐ / ← / → unicode
  glyphs with proper inline SVG icons (10×10 stroke paths). Each chip
  now carries: plus-sign for broadcast, up-arrow for hand-raise,
  right-arrow for direct. Same claude-orange / emerald / neutral
  coloring, same typography — just geometry instead of text symbols.

Nothing swapped to Lucide React imports yet — Icons barrel in
packages/ui/web only exports a subset (Circle, Check, MessageCircle,
Sparkles, Megaphone), and the four glyphs we needed were simpler as
inline SVG than adding barrel exports + per-component import plumbing.
If emoji→Lucide fully lands, we'll add the rest to the Icons barrel
in one pass.

Skipped per PM spec: TTS announcements, commit messages, code
comments, logs — not user-visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:15:53 +01:00
Alejandro Gutiérrez
995d8a3c12 feat(web): mobile-responsive pass on mesh detail + invites list
Some checks failed
CI / Tests / 🧪 Test (push) Has been cancelled
Mesh detail page at /dashboard/meshes/[id]:
- Header: flex-col → flex-row at sm breakpoint. Live/Invite buttons
  stretch full-width stacked on mobile (flex-1), auto-width side-by-
  side from sm up.
- "Generate invite link" truncates to "Invite" on mobile (viewport
  constrained) so the button fits next to Live.
- Members + active-invites rows: stack metadata vertically on mobile
  (flex-col → sm:flex-row), wrap badges inside with flex-wrap so the
  member display-name + role + revoked badges don't horizontal-scroll.

Invites list at /dashboard/invites:
- Wrap the table in overflow-x-auto with min-w-[560px] on the table
  itself. 5-column data-table that genuinely needs horizontal space
  — don't fake it with card stacking, let the user scroll naturally.

Quick-send composer deferred to a follow-up — writes a message to the
mesh, which requires a client-side encryption decision (ed25519
keypair in the browser? key derivation from session? plaintext-to-
broker and break E2E?). Parked as its own spec.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:13:16 +01:00