From 0378e8e0912a1a7e6cea4424e69d524e7afecbf8 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Wed, 30 Sep 2026 21:04:39 +0200 Subject: fix: an identity signs a named kind, and a device approval answers a request The desktop main process builds every transcript itself from fields (transcripts.js) and signs no raw bytes; the page's identity has the same contract (crypto.js transcriptFor). The keyring seals no bundle while browser access is off. On the node, device_add must redeem a pending request filed by the same keys, and device_revoke is signed under its own prefix (meshbay:device_revoke:v1), so a retirement signature admits nothing. Co-Authored-By: Claude Opus 5.5 --- .../src/meshbay_hub/static/transport-admin.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js b/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js index d5226fc..1a5a4c0 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport-admin.js @@ -32,9 +32,10 @@ extendTransport(class { const ts = Math.floor(Date.now() / 1000); // group_id is empty: operator authority is node-wide, not per group. - const transcript = C.joinTranscript( - this.nodePk, '', userId, pkEdB64, pkXB64, this._nonceNode, ts); - const sig = await this._identity.sign(transcript); + const sig = await this._identity.signAs('join', { + nodePk: this.nodePk, groupId: '', userId, + nonceNode: C.b64encode(this._nonceNode), ts, + }); const resp = await this._sendAndWait({ type: 'join_request', @@ -107,11 +108,12 @@ extendTransport(class { } if (!signFn) throw new Error('Admin challenge received but no signing key available'); - const transcript = window.MeshBayCrypto.adminTranscript( - challenge.op, challenge.node_pk, challenge.group_id, - challenge.subject, challenge.nonce, challenge.ts); - - const signature = await signFn(transcript); + // The fields, not the bytes: whatever holds the key builds the transcript + // from them (crypto.js, transcriptFor). + const signature = await signFn({ + op: challenge.op, nodePk: challenge.node_pk, groupId: challenge.group_id, + subject: challenge.subject, nonce: challenge.nonce, ts: challenge.ts, + }); console.log('[MeshBay] _authorizeAdminOp: signed', challenge.op, 'op_id=', challenge.op_id, '— sending admin_response'); const ack = await this._sendAndWait({ -- cgit v1.2.3