zebra-spaces: log full pubkey on every role / spotlight / boot / join / leave event
Pulled from the matching server broadcast enrichment (commit 7d16a16, zebra-spaces-signal). Each role-change / hand-raised / hand-lowered / mic-invite / mic-invite-declined / peer-joined / peer-left / peer-booted / host-left / host-promoted / spotlight log line now carries the actor's authoritative pubkey from the server message instead of trusting the local uuid -> handle map. Full hex, never truncated. New helpers pubHexFromMsg() + idTag() keep call sites compact.
This commit is contained in:
parent
54f97866a2
commit
72d5208493
1 changed files with 61 additions and 22 deletions
|
|
@ -722,6 +722,31 @@ function logLine(kind, msg){
|
|||
logEl.appendChild(d); logEl.scrollTop=logEl.scrollHeight;
|
||||
}
|
||||
|
||||
/* pubHex straight from a server message — prefers the explicit hex field,
|
||||
* falls back to decoding the b64 form. Used so role/spot/boot/etc. log lines
|
||||
* carry the actor's authoritative key (the server already verified the
|
||||
* signature), independent of the local uuid→handle map. A spoof attempt
|
||||
* is then visible in the log retroactively: same handle, different pubkey. */
|
||||
function pubHexFromMsg(m, b64key, hexkey){
|
||||
if (m && hexkey && m[hexkey]) return m[hexkey];
|
||||
if (m && b64key && m[b64key]){
|
||||
try {
|
||||
const bin = atob(m[b64key].replace(/-/g,'+').replace(/_/g,'/'));
|
||||
let h=''; for (let i=0;i<bin.length;i++) h += bin.charCodeAt(i).toString(16).padStart(2,'0');
|
||||
return h;
|
||||
} catch(_){}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
/* "handle pubhex" or just "pubhex" if we don't have the member yet. Full
|
||||
* hex — never truncated. */
|
||||
function idTag(uuidOrHandle, pubHex){
|
||||
const mm = (uuidOrHandle && members.get) ? members.get(uuidOrHandle) : null;
|
||||
const handle = mm ? mm.handle : (uuidOrHandle || '');
|
||||
if (!pubHex) return handle;
|
||||
return handle ? (handle+' '+pubHex) : pubHex;
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* state-machine framework — pure transitions, testable in isolation.
|
||||
*
|
||||
|
|
@ -1639,11 +1664,15 @@ function broadcastSpotlight(){
|
|||
if (typeof inRoleTransition !== 'undefined' && inRoleTransition) return;
|
||||
try { send({ type: 'spotlight', key: spotlightKey() }); } catch(_){}
|
||||
}
|
||||
function logSpotlightChange(uuid, key){
|
||||
const handle = (members.get(uuid) || {}).handle || shortHex(uuid);
|
||||
if (!key){ logLine('', handle + ' looked away'); return; }
|
||||
function logSpotlightChange(uuid, key, viewerPubHex){
|
||||
/* viewerPubHex is the authoritative pubkey from the server's spotlight
|
||||
* broadcast; for self-spotlight calls we fall back to my own key. */
|
||||
const mm = members.get(uuid);
|
||||
const pub = viewerPubHex || (uuid === myUUID && myKeys ? myKeys.pubHex : (mm && mm.pubkey ? pubHexFromMsg({pubkey:mm.pubkey},'pubkey') : ''));
|
||||
const who = idTag(uuid, pub);
|
||||
if (!key){ logLine('', who + ' looked away'); return; }
|
||||
const [kind, pubHex] = key.split(':');
|
||||
logLine('', handle + ' now viewing ' + ownerLabel(kind, pubHex));
|
||||
logLine('', who + ' now viewing ' + ownerLabel(kind, pubHex) + (pubHex?' ['+pubHex+']':''));
|
||||
}
|
||||
|
||||
function updateContainerVisibility(){
|
||||
|
|
@ -2702,7 +2731,7 @@ async function handleSignal(raw){
|
|||
roomEpoch = m.epoch; applyState(m.state); flushSfuStreams(); renderRoom(); break;
|
||||
case 'peer-joined':
|
||||
members.set(m.uuid, { uuid:m.uuid, pubkey:m.pubkey, handle:m.handle, role:m.role, joined_at: Date.now()/1000 });
|
||||
logLine('', m.handle+' joined as '+m.role);
|
||||
{ const pub = pubHexFromMsg(m, 'pubkey'); logLine('', idTag(m.uuid, pub)+' joined as '+m.role); }
|
||||
/* if the original host rejoined during their grace window, the room
|
||||
* is rescued — drop the "space closing" status from our top bar */
|
||||
if (m.role === 'host'){
|
||||
|
|
@ -2721,7 +2750,8 @@ async function handleSignal(raw){
|
|||
case 'peer-left':
|
||||
{
|
||||
const left = members.get(m.uuid);
|
||||
if (left) logLine('', left.handle+' left');
|
||||
const pub = pubHexFromMsg(m, 'pubkey') || (left && left.pubkey ? pubHexFromMsg({pubkey:left.pubkey}, 'pubkey') : '');
|
||||
if (left || pub) logLine('', idTag(m.uuid, pub)+' left');
|
||||
/* if they were sharing screen or camera, drop the tiles */
|
||||
if (left && left.pubkey){
|
||||
try { const h = hex(unb64(left.pubkey)); removeScreenTile(h); removeCameraTile(h); } catch(_){}
|
||||
|
|
@ -2750,32 +2780,36 @@ async function handleSignal(raw){
|
|||
const next = m.key || '';
|
||||
if (prev === next) break;
|
||||
spotlights.set(m.uuid, next);
|
||||
if (m.uuid !== myUUID) logSpotlightChange(m.uuid, next);
|
||||
if (m.uuid !== myUUID) logSpotlightChange(m.uuid, next, pubHexFromMsg(m,'pubkey'));
|
||||
reorderTiles();
|
||||
}
|
||||
break;
|
||||
case 'hand-raised':
|
||||
handraise.add(m.uuid); renderRoom();
|
||||
{ const mm = members.get(m.uuid); if (mm) logLine('', mm.handle+' raised hand'); }
|
||||
{ const pub = pubHexFromMsg(m, 'pubkey'); logLine('', idTag(m.uuid, pub)+' raised hand'); }
|
||||
break;
|
||||
case 'hand-lowered':
|
||||
handraise.delete(m.uuid); renderRoom(); break;
|
||||
handraise.delete(m.uuid); renderRoom();
|
||||
{ const pub = pubHexFromMsg(m, 'pubkey'); logLine('', idTag(m.uuid, pub)+' lowered hand'); }
|
||||
break;
|
||||
case 'mic-invite':
|
||||
if (m.to === myUUID){
|
||||
outstandingInvite = { from:m.from, epoch:m.epoch };
|
||||
const from = members.get(m.from);
|
||||
$('invite-from').textContent = 'from '+(from?from.handle:shortHex(m.from));
|
||||
$('invite-from').textContent = 'from '+(from?from.handle:m.from);
|
||||
$('sec-invite').classList.remove('hidden');
|
||||
logLine('', 'you were invited to the mic by '+(from?from.handle:m.from));
|
||||
const fromPub = pubHexFromMsg(m, 'from_pubkey');
|
||||
logLine('', 'you were invited to the mic by '+idTag(m.from, fromPub));
|
||||
} else {
|
||||
const from = members.get(m.from), to = members.get(m.to);
|
||||
if (from && to) logLine('', from.handle+' invited '+to.handle+' to the mic');
|
||||
const fromPub = pubHexFromMsg(m, 'from_pubkey');
|
||||
const toPub = pubHexFromMsg(m, 'to_pubkey');
|
||||
logLine('', idTag(m.from, fromPub)+' invited '+idTag(m.to, toPub)+' to the mic');
|
||||
}
|
||||
break;
|
||||
case 'mic-invite-declined':
|
||||
if (m.from === myUUID){
|
||||
const to = members.get(m.to);
|
||||
logLine('', (to?to.handle:m.to)+' declined the mic');
|
||||
const toPub = pubHexFromMsg(m, 'to_pubkey');
|
||||
logLine('', idTag(m.to, toPub)+' declined the mic');
|
||||
}
|
||||
break;
|
||||
case 'role-change':
|
||||
|
|
@ -2786,10 +2820,12 @@ async function handleSignal(raw){
|
|||
mm.role = m.role;
|
||||
roomEpoch = m.epoch;
|
||||
if (m.role === 'host') hostUUID = m.uuid;
|
||||
const subjPub = pubHexFromMsg(m, 'pubkey', 'pubkey_hex') || myKeys && (m.uuid===myUUID ? myKeys.pubHex : '');
|
||||
const byPub = pubHexFromMsg(m, 'by_pubkey');
|
||||
if (m.uuid === myUUID){
|
||||
myRole = m.role;
|
||||
roomMachines.call.send('ROLE_CHANGE', { role: m.role });
|
||||
logLine('', 'you are now '+m.role);
|
||||
logLine('', 'you are now '+m.role+(subjPub?' ['+subjPub+']':'')+(m.by?' by '+idTag(m.by, byPub):''));
|
||||
setStatus('connected as '+myRole, 'ok');
|
||||
const byMod = members.get(m.by);
|
||||
const byTxt = byMod ? ' by '+byMod.handle : '';
|
||||
|
|
@ -2801,7 +2837,7 @@ async function handleSignal(raw){
|
|||
else if (m.role === 'host') showNotice('You are now the host.', 'info');
|
||||
onRoleChanged(prev, m.role);
|
||||
} else {
|
||||
logLine('', mm.handle+' is now '+m.role);
|
||||
logLine('', idTag(m.uuid, subjPub)+' is now '+m.role+(m.by?' by '+idTag(m.by, byPub):''));
|
||||
/* mesh adjustments — fox: 'never drop people out of the mesh
|
||||
* automatically'. Promote: connect if we can speak + they
|
||||
* can speak + we don't already have them. Demote: do NOT
|
||||
|
|
@ -2820,7 +2856,9 @@ async function handleSignal(raw){
|
|||
{
|
||||
const mm = members.get(m.uuid);
|
||||
const by = members.get(m.by);
|
||||
if (mm) logLine('', mm.handle+' was removed by '+(by?by.handle:'a mod'));
|
||||
const victPub = pubHexFromMsg(m, 'pubkey');
|
||||
const byPub = pubHexFromMsg(m, 'by_pubkey');
|
||||
logLine('', idTag(m.uuid, victPub)+' was removed by '+(by||byPub?idTag(m.by, byPub):'a mod'));
|
||||
if (m.uuid === myUUID){
|
||||
/* server will close our socket; surface a clear notice and prevent
|
||||
* the WS reconnect loop from auto-rejoining into a boot loop. Tear
|
||||
|
|
@ -2842,17 +2880,18 @@ async function handleSignal(raw){
|
|||
}
|
||||
break;
|
||||
case 'host-left':
|
||||
logLine('', 'host left the space');
|
||||
{ const pub = pubHexFromMsg(m, 'host_pubkey'); logLine('', 'host left the space — '+idTag(m.host_uuid, pub)); }
|
||||
hostUUID = '';
|
||||
break;
|
||||
case 'host-promoted':
|
||||
roomEpoch = m.epoch;
|
||||
hostUUID = m.new_host_uuid;
|
||||
{ const mm = members.get(m.new_host_uuid);
|
||||
const pub = pubHexFromMsg(m, 'new_host_pubkey');
|
||||
if (mm){ mm.role = 'host';
|
||||
if (m.new_host_uuid === myUUID){ myRole = 'host'; setStatus('connected as host','ok'); onRoleChanged('cohost','host'); }
|
||||
else { setStatus('connected as '+myRole, 'ok'); } /* clears the space-closing warning */
|
||||
logLine('', (mm.handle||m.new_host_uuid)+' is now host'); }
|
||||
logLine('', idTag(m.new_host_uuid, pub)+' is now host'); }
|
||||
flushSfuStreams();
|
||||
renderRoom();
|
||||
reorderTiles();
|
||||
|
|
@ -3547,8 +3586,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">f908c19e47433151020f76df10fe81d0</span><br>
|
||||
sha256 <span class="stamp-sha">f751785bdcb457d172dee013fbe0a445ee01b0aacdb20c2c1fe11352ef98111c</span><br>
|
||||
md5 <span class="stamp-md5">c9a9641f134b920871292cd17598a637</span><br>
|
||||
sha256 <span class="stamp-sha">fb1aa6ee1857cf33e50985473b98ad9648850abf82289b771c3ab2295db707b6</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue