# claudemesh handoff — 2026-05-02 State of the world after a long session that shipped 1.5.0 and the v0.2.0 backend. Read this before the next session — it captures what's done, what's deployed where, what's not, and the architectural decisions worth knowing. --- ## Where things stand ### Released to npm - **`claudemesh-cli@1.5.0`** (latest tag, published earlier today). CLI-first architecture lock-in: zero-tool MCP, policy engine, bundled `claudemesh` skill. Verified install + smoke-tested via clean `npm i -g`. ### In `main` but NOT released yet Everything below is committed, deployed to the broker (`wss://ic.claudemesh.com/ws`) and the web app (Vercel `claudemesh.com`), but **`claudemesh-cli@1.5.0` on npm doesn't have any of it**. Users won't see it until v1.6.0 publishes. | Feature | Code path | Verified live? | |---|---|---| | Topics (schema, broker routing, CLI verbs, skill) | `packages/db/src/schema/mesh.ts`, `apps/broker/src/broker.ts`, `apps/cli/src/commands/topic.ts` | ✅ created `#deploys-test`, sent + persisted | | `apikey create/list/revoke` (CLI + broker WS) | `apps/cli/src/commands/apikey.ts`, broker dispatch | ✅ full lifecycle exercised | | REST `/api/v1/*` (messages, topics, peers, history) | `packages/api/src/modules/mesh/v1-router.ts` + `api-key-auth.ts` | ✅ posted via curl, history round-trips | | Bridge peer (SDK + CLI) | `packages/sdk/src/bridge.ts`, `apps/cli/src/commands/bridge.ts` | ⚠️ code only — never run end-to-end | ### Architectural commitments locked this session - **CLI-first, MCP push-pipe** (1.5.0): MCP `tools/list = []`. Inbound peer messages still arrive as `experimental.claude/channel` notifications. The bundled skill is the sole CLI-discoverability surface for Claude. - **Topics complement groups, don't replace them** (v0.2.0): mesh = trust boundary, group = identity tag, topic = conversation scope. Three orthogonal axes. - **Humans use REST + apikey, not browser WS** (v0.2.0): the broker already plumbs `peer_type: "human"`. The real blocker was browser-side ed25519, which we sidestep by exposing REST. Web chat UI = thin client over `/v1/*` using dashboard session auth. - **Spec lives at**: `.artifacts/specs/2026-05-02-architecture-north-star.md` (1.5.0) and `.artifacts/specs/2026-05-02-v0.2.0-scope.md` (v0.2.0 cut + design sketches). --- ## Three pending sessions, ranked by leverage ### Session A — Ship v1.6.0 npm release (~30 min, highest leverage) **Why first**: backend is feature-complete but unreleased. Users still get the no-topics 1.5.0. Steps: 1. Bump `apps/cli/package.json` 1.5.0 → 1.6.0. 2. Update `apps/cli/README.md` migration note (mention topics, apikey, bridge). 3. Add `## v1.6.0` section to `docs/roadmap.md`. 4. Build + verify: `cd apps/cli && pnpm build && node dist/entrypoints/cli.js --version`. 5. `npm publish --tag latest --access public --no-git-checks --ignore-scripts`. 6. `git tag cli-v1.6.0 && git push github cli-v1.6.0` — workflow builds 5 binaries + auto-bumps Homebrew/winget tap. 7. Verify on a clean prefix: `PREFIX=/tmp/cm16 mkdir -p $PREFIX && npm install -g --prefix $PREFIX claudemesh-cli@1.6.0 && $PREFIX/bin/claudemesh --help | grep -E "topic|apikey|bridge"`. ### Session B — Migration drift fix (~1 day, highest pain reduction) **Why second**: every schema change today requires manual `psql -f migration.sql` against prod. The drizzle `_journal.json` stops at idx 11, runtime migrator silently skips anything not in journal. Today's `0022_topics.sql` and `0023_api_keys.sql` were applied by hand. **Future migrations will keep needing this until fixed.** Recommended approach: 1. Replace `drizzle-orm/postgres-js/migrator` in `apps/broker/src/migrate.ts` with a custom runner. 2. Scan `migrations/*.sql` lexicographically (already named `NNNN_*.sql`). 3. Track applied filenames in a new `mesh.__cmh_migrations` table (filename + sha256 + applied_at). 4. On startup: filter unapplied files, run them in transaction order under `pg_try_advisory_lock`. Fail loud on hash mismatch (catches edits after deploy). 5. Backfill the table with all 0000-0023 entries one-time so prod is consistent. 6. Drop the drizzle journal usage entirely (`migrations/meta/_journal.json` becomes dead state). This unblocks every future feature touching DB. ### Session C — Web chat UI (~2-3 days, highest visibility) **Why third**: the demo. Backend is ready; this is pure React + REST. Path: `apps/web/src/app/[locale]/dashboard/(user)/meshes/[id]/topics/[name]/page.tsx` (new). Components needed: - Topic header (members count, settings button). - Message stream — `GET /api/v1/topics/:name/messages?limit=50`. Poll every 5s for new (no WS yet — REST polling is fine for v0.2.0). - Compose box — `POST /api/v1/messages` with `{topic, ciphertext, nonce}`. - Members sidebar — `GET /api/v1/peers`. - Apikey lifecycle: on first load, server-side issue an apikey for the dashboard user (using their existing NextAuth session) scoped to `read,send` on this topic. Stash in browser session storage. Server-side helper for apikey issuance lives in `packages/api/src/modules/mesh/api-key-auth.ts` — refactor `verifyBearer` to also expose a `createApiKeyForUser(userId, meshId, scope)` helper for the dashboard handler. --- ## Three less-urgent followups (don't block sessions A-C) 1. **Bridge end-to-end smoke test**: never actually run between two meshes. Needs second test mesh + bridge member onboarding ritual. Worth doing before any blog post / external demo. 2. **`/v1/peers` includes only WS-connected agents**, not humans (since humans are REST-only and never appear in `presence`). Decide: synthetic presence rows for active apikey sessions? Or document that `/v1/peers` is "agents online"? 3. **Topic ciphertext is plaintext base64** in the current implementation — no actual encryption. The schema names it `ciphertext` for forward-compat, but the code base64-encodes UTF-8. Real per-topic symmetric key derivation (HKDF from mesh root_key + topic_id) is a v0.3.0 item. --- ## Production state worth knowing - **Broker**: `wss://ic.claudemesh.com/ws`, deployed via Coolify on OVHcloud VPS. Auto-redeploys on push to `gitea-vps main`. Deploy ETA ~3 min. - **Web**: `claudemesh.com`, Vercel auto-deploy on push to `github main`. Deploy ETA ~2 min. - **Postgres**: container `eo1f5gydsgrg19b57e9s4zw7` on the VPS. SSH via `ssh ovh`, then `docker exec eo1f5gydsgrg19b57e9s4zw7 psql -U claudemesh -d claudemesh`. - **Test mesh**: `openclaw` on the same broker has 5 active peers and one topic (`#deploys-test`). - **Active apikey** (from earlier today's smoke): `cm_OC12dRti…` was revoked. None active right now. --- ## Files most worth reading first in next session 1. `.artifacts/specs/2026-05-02-architecture-north-star.md` — the 7 architectural commitments. 2. `.artifacts/specs/2026-05-02-v0.2.0-scope.md` — design sketches for topics, REST, bridge. 3. `apps/cli/skills/claudemesh/SKILL.md` — the canonical CLI surface; ships in npm tarball. 4. This file. --- ## Memory not yet captured Worth adding to `~/.claude/projects/-Users-agutierrez-Desktop-claudemesh/memory/MEMORY.md` next session: - **Drizzle journal drift is a recurring trap** — manual psql until session B lands. Save the exact apply ritual: `scp migrations/NNNN.sql ovh:/tmp/ && ssh ovh "docker cp /tmp/NNNN.sql :/tmp/ && docker exec psql -U claudemesh -d claudemesh -f /tmp/NNNN.sql"`. - **`workspace:*` deps break `npm publish`** — keep SDK as devDependency in `apps/cli/package.json`; Bun bundles it into dist so runtime doesn't need it. Same trick for any other workspace-only build deps. - **Commitlint hard-caps body lines at 100 chars** — use `git commit -F /tmp/cm-commit.txt` rather than `-m` heredocs. Heredocs that exceed the limit fail the husky hook silently.