fix(api): /v1/me/peer-pubkey only updates web-managed members
Adds a 409 not_web_member guard to POST /v1/me/peer-pubkey: the endpoint will only rewrite peer_pubkey on members that have dashboard_user_id set. CLI members own their on-disk keypair — overwriting their stored peer_pubkey would break the next WS hello because the signature verification would fail against the new pubkey. In practice this restriction is invisible to the legitimate browser flow: the dashboard always mints its apikey against the web member (dashboard_user_id is non-null by construction in mutations.ts). Guard ensures a misuse (e.g. a CLI-minted apikey being used to call peer-pubkey) gets a clear 409 instead of silently breaking the CLI's auth. Discovered during phase 3.5 smoke when a CLI-minted apikey clobbered the only openclaw member (CLI-owned) and the user's CLI signature would have stopped verifying on the next launch.
This commit is contained in:
@@ -305,12 +305,33 @@ export const v1Router = new Hono<Env>()
|
||||
const body = c.req.valid("json");
|
||||
const newPubkey = body.pubkey.toLowerCase();
|
||||
const [existing] = await db
|
||||
.select({ peerPubkey: meshMember.peerPubkey })
|
||||
.select({
|
||||
peerPubkey: meshMember.peerPubkey,
|
||||
dashboardUserId: meshMember.dashboardUserId,
|
||||
})
|
||||
.from(meshMember)
|
||||
.where(eq(meshMember.id, key.issuedByMemberId));
|
||||
if (!existing) {
|
||||
return c.json({ error: "member_not_found" }, 404);
|
||||
}
|
||||
// Safety: only web-managed members (dashboardUserId set) can have
|
||||
// their peer_pubkey rewritten via this endpoint. CLI-created
|
||||
// members hold a real on-disk secret that matches their existing
|
||||
// peer_pubkey; overwriting it would break their next WS hello
|
||||
// (signature verification fails because the stored pubkey no
|
||||
// longer matches the secret they sign with). The browser flow
|
||||
// always mints its apikey against the dashboard member, so this
|
||||
// restriction is invisible to legitimate callers.
|
||||
if (!existing.dashboardUserId) {
|
||||
return c.json(
|
||||
{
|
||||
error: "not_web_member",
|
||||
detail:
|
||||
"this endpoint only updates web-managed members (mesh.member.dashboard_user_id IS NOT NULL); CLI members own their on-disk keypair and can't have peer_pubkey rewritten remotely",
|
||||
},
|
||||
409,
|
||||
);
|
||||
}
|
||||
const changed = existing.peerPubkey !== newPubkey;
|
||||
if (changed) {
|
||||
await db
|
||||
|
||||
Reference in New Issue
Block a user