Files
claudemesh/.artifacts/specs/2026-05-02-handoff.md
Alejandro Gutiérrez f08d6c9f0c
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
docs(handoff): 2026-05-02 — state after 1.5.0 + v0.2.0 backend
Three pending sessions ranked by leverage: ship 1.6.0 npm release, fix migration drift, build web chat UI.
2026-05-02 15:55:53 +01:00

7.7 KiB

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 <pg-container>:/tmp/ && docker exec <pg-container> 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.