feat(cli): 1.30.0 — per-session broker presence
flips CLAUDEMESH_SESSION_PRESENCE default to ON. With the broker side already shipped (the session_hello handler from earlier in this sprint A wave), every claudemesh launch now gets its own long-lived broker presence row owned by the daemon and identified by a per-launch ephemeral keypair vouched by the member's stable key. Two sessions in the same cwd finally see each other in peer list — the symptom users have been hitting since 1.28.0 dropped the bridge tier. Bumps roadmap: 1.30.0 = presence (was queued for 1.30/wizard); the launch-wizard refactor moves to 1.31.0, setup wizard to 1.32.0, the mesh→workspace rename to 1.33.0. Verification smoke documented in the 1.30.0 changelog entry. Rollback: CLAUDEMESH_SESSION_PRESENCE=0 (also accepts "false"/"off"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,88 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.30.0 (2026-05-04) — per-session broker presence
|
||||||
|
|
||||||
|
Sprint A Phase 3. Two `claudemesh launch` sessions in the same cwd now
|
||||||
|
see each other in `peer list`. Each launched session has a long-lived
|
||||||
|
broker presence row owned by the daemon, identified by a per-launch
|
||||||
|
ephemeral keypair vouched by the member's stable key (OAuth-refresh-vs-
|
||||||
|
access shape).
|
||||||
|
|
||||||
|
### What landed
|
||||||
|
|
||||||
|
- **broker `session_hello`** — new WS message type. Validates a
|
||||||
|
parent-vouched `parent_attestation` (≤24h TTL, ed25519 signature by
|
||||||
|
the parent member) plus a session-keyed signature on the hello
|
||||||
|
itself. Inserts a presence row keyed on `sessionPubkey` but
|
||||||
|
`member_id` from the parent, so member-targeted operations stay
|
||||||
|
unchanged. Older brokers reply `unknown_message_type` — newer clients
|
||||||
|
drop back to the previous behavior.
|
||||||
|
- **daemon `SessionBrokerClient`** — slim WS variant of
|
||||||
|
`DaemonBrokerClient`. Presence-only, no outbox drain. Lifetime tied
|
||||||
|
to a registry hook: register opens it, deregister/reaper closes it.
|
||||||
|
Reconnect with exponential backoff up to 30 s.
|
||||||
|
- **session-registry hooks** — `setRegistryHooks({ onRegister,
|
||||||
|
onDeregister })` in `apps/cli/src/daemon/session-registry.ts`. Hook
|
||||||
|
errors are caught so they never throttle the registry. SessionInfo
|
||||||
|
gains an optional `presence` field carrying the per-launch keypair
|
||||||
|
+ attestation.
|
||||||
|
- **IPC `POST /v1/sessions/register`** — accepts an optional
|
||||||
|
`presence` block on the body (`session_pubkey`, `session_secret_key`,
|
||||||
|
`parent_attestation`). Older payloads continue to work.
|
||||||
|
- **`claudemesh launch`** — generates an ed25519 session keypair and a
|
||||||
|
12 h parent attestation per launch (mesh secret key signs it),
|
||||||
|
forwards both to the daemon under `body.presence`. The flag
|
||||||
|
`CLAUDEMESH_SESSION_PRESENCE` defaults to ON; set `=0` to roll back
|
||||||
|
if a broker on a given mesh is misbehaving.
|
||||||
|
- **latent 1.29.0 bug fix** — `claudemesh launch` referenced
|
||||||
|
`claudeSessionId` before its `const` declaration further down the
|
||||||
|
file, hitting the temporal dead zone → `ReferenceError` silently
|
||||||
|
swallowed by the surrounding catch. Net: the IPC session-token
|
||||||
|
registration has been failing every launch since 1.29.0, falling
|
||||||
|
every session back to user-level scope. Hoisted the declaration up
|
||||||
|
so the registration actually runs.
|
||||||
|
|
||||||
|
### Sequencing
|
||||||
|
|
||||||
|
The broker side ships first and bakes for ~24 h. Only then does the
|
||||||
|
flag default flip on the CLI side. Older CLIs continue working
|
||||||
|
unchanged (no per-session WS), and the protocol is purely additive on
|
||||||
|
the wire.
|
||||||
|
|
||||||
|
### Verification (smoke)
|
||||||
|
|
||||||
|
In two shells, both `cd ~/Desktop/foo`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ claudemesh launch --name SessionA -y # shell 1
|
||||||
|
$ claudemesh launch --name SessionB -y # shell 2
|
||||||
|
```
|
||||||
|
|
||||||
|
In a third shell:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ claudemesh peer list --json --mesh foo \
|
||||||
|
| jq '.[] | {n: .displayName, c: .cwd}'
|
||||||
|
{ "n": "SessionA", "c": "/.../foo" } ← persistent, not query-induced
|
||||||
|
{ "n": "SessionB", "c": "/.../foo" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside SessionA, `peer list --mesh foo` now lists SessionB. Kill
|
||||||
|
SessionB; within ≤30 s the reaper drops it from `peer list`.
|
||||||
|
|
||||||
|
### Out of scope (deferred)
|
||||||
|
|
||||||
|
- **Attestation auto-refresh** — current 12 h TTL is comfortably
|
||||||
|
longer than typical sessions; if a session lives past the TTL and
|
||||||
|
the WS reconnects after expiry, the broker rejects with `expired`
|
||||||
|
and the SessionBrokerClient quiets. Workaround: `claudemesh launch`
|
||||||
|
again. Auto-refresh queued for 1.31.0+ alongside HKDF identity.
|
||||||
|
- **Per-session policy DSL** — the per-launch WS could carry
|
||||||
|
per-session capabilities later. Out of scope here.
|
||||||
|
- **Cross-machine session sync** — waits on 2.0.0 HKDF identity.
|
||||||
|
- **Launch-wizard refactor** — bumped to 1.31.0 to keep this release
|
||||||
|
scoped to presence.
|
||||||
|
|
||||||
## 1.29.0 (2026-05-04) — per-session IPC tokens + auto-scoping
|
## 1.29.0 (2026-05-04) — per-session IPC tokens + auto-scoping
|
||||||
|
|
||||||
Sprint A Phase 2. Every `claudemesh launch`-spawned session gets a
|
Sprint A Phase 2. Every `claudemesh launch`-spawned session gets a
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudemesh-cli",
|
"name": "claudemesh-cli",
|
||||||
"version": "1.29.0",
|
"version": "1.30.0",
|
||||||
"description": "Peer mesh for Claude Code sessions — CLI + MCP server.",
|
"description": "Peer mesh for Claude Code sessions — CLI + MCP server.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude-code",
|
"claude-code",
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ export interface RunDaemonOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1.30.0 feature flag. Default OFF for one release cycle so the broker
|
* 1.30.0 feature flag. Default ON — the daemon opens a long-lived WS per
|
||||||
* side has time to deploy + bake before the daemon starts opening
|
* registered session so siblings see each other in `peer list`. Set
|
||||||
* per-session WebSockets. Set CLAUDEMESH_SESSION_PRESENCE=0 to disable
|
* CLAUDEMESH_SESSION_PRESENCE=0 (or "false"/"off") to disable for
|
||||||
* once the flag flips default-on.
|
* rollback if the broker side is misbehaving on a given mesh.
|
||||||
*/
|
*/
|
||||||
function isSessionPresenceEnabled(): boolean {
|
function isSessionPresenceEnabled(): boolean {
|
||||||
const v = process.env.CLAUDEMESH_SESSION_PRESENCE;
|
const v = process.env.CLAUDEMESH_SESSION_PRESENCE;
|
||||||
if (v === undefined || v === "") return false;
|
if (v === undefined || v === "") return true;
|
||||||
return v !== "0" && v.toLowerCase() !== "false" && v.toLowerCase() !== "off";
|
return v !== "0" && v.toLowerCase() !== "false" && v.toLowerCase() !== "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ What this leaves on the v2.0.0 redesign is documented at
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v1.26.0 → v1.29.0 — *Sprint A toward v2* — *shipped*
|
## v1.26.0 → v1.30.0 — *Sprint A toward v2* — *shipped*
|
||||||
|
|
||||||
The Sprint A push completed everything spec'd for v2.0.0 *except* HKDF
|
The Sprint A push completed everything spec'd for v2.0.0 *except* HKDF
|
||||||
identity (deferred for security review).
|
identity (deferred for security review).
|
||||||
@@ -268,14 +268,26 @@ identity (deferred for security review).
|
|||||||
joined meshes (verified: `peer list` returns 1 workspace's peers
|
joined meshes (verified: `peer list` returns 1 workspace's peers
|
||||||
with token, all 3 without). Server-side `meshFromCtx()` plumbing
|
with token, all 3 without). Server-side `meshFromCtx()` plumbing
|
||||||
on every read route.
|
on every read route.
|
||||||
|
- **1.30.0** — per-session broker presence. Two `claudemesh launch`
|
||||||
|
sessions in the same cwd finally see each other in `peer list`. Each
|
||||||
|
launched session has a long-lived broker presence row owned by the
|
||||||
|
daemon, identified by a per-launch ephemeral keypair vouched by the
|
||||||
|
member's stable key (OAuth-refresh-vs-access shape). Broker gains a
|
||||||
|
`session_hello` handler with parent-attestation TTL ≤24h + session-
|
||||||
|
signature checks; daemon adds a slim `SessionBrokerClient` and
|
||||||
|
registry lifecycle hooks. Also fixes a latent 1.29.0 TDZ bug where
|
||||||
|
`claudemesh launch`'s IPC session-token registration was silently
|
||||||
|
failing every run. Flag-gated for one cycle, default ON in this
|
||||||
|
release; set `CLAUDEMESH_SESSION_PRESENCE=0` for rollback. Spec at
|
||||||
|
`.artifacts/specs/2026-05-04-per-session-presence.md`.
|
||||||
|
|
||||||
What's left for true v2.0.0 (next sessions):
|
What's left for true v2.0.0 (next sessions):
|
||||||
|
|
||||||
- **1.30.0** — launch wizard refactor (single render loop, daemon-as-
|
- **1.31.0** — launch wizard refactor (single render loop, daemon-as-
|
||||||
step probe panel, last-used persistence, drop `@ts-nocheck`).
|
step probe panel, last-used persistence, drop `@ts-nocheck`).
|
||||||
- **1.31.0** — setup wizard refactor (state-detection snapshot, four-
|
- **1.32.0** — setup wizard refactor (state-detection snapshot, four-
|
||||||
branch flow, daemon install offer, post-join panel).
|
branch flow, daemon install offer, post-join panel).
|
||||||
- **1.32.0** — full mesh→workspace public-surface rename in help/docs/
|
- **1.33.0** — full mesh→workspace public-surface rename in help/docs/
|
||||||
site; mesh aliases tagged deprecated; protocol/DB stay `mesh_*`.
|
site; mesh aliases tagged deprecated; protocol/DB stay `mesh_*`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user