Skip to content

fix(meshcore): resolve author full public key for room server messages#3733

Merged
Yeraze merged 2 commits into
mainfrom
claude/great-dijkstra-ueptaa
Jun 25, 2026
Merged

fix(meshcore): resolve author full public key for room server messages#3733
Yeraze merged 2 commits into
mainfrom
claude/great-dijkstra-ueptaa

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #3732 — room server messages showed a truncated hex string instead of the sender's name.

Root cause: The room_message event handler in meshcoreManager.ts was storing author_pubkey_prefix (a short prefix) directly as fromPublicKey, while the room itself was correctly resolved to its full public key via resolveContactByPrefix(). Since nameForKey() in the frontend does an exact-match lookup, it never matched the prefix and fell back to displaying fromPublicKey.substring(0, 8)….

Fix: Mirror the existing roomFullKey resolution pattern for the author:

// Before
fromPublicKey: authorPrefixHex,

// After
const authorFullKey = authorContact?.publicKey ?? authorPrefixHex;
fromPublicKey: authorFullKey,

When the contact is in the contacts map at ingest time, the full public key is stored, allowing nameForKey() to resolve the sender name on the frontend. If the contact isn't known yet, falls back to the prefix (same behaviour as before).

Test plan

  • Send a message from a companion to a room server
  • Open the room from another companion — sender name should display correctly instead of a hex string
  • CI suite

🤖 Generated with Claude Code


Generated by Claude Code

Room post handler was storing the raw author_pubkey_prefix as
fromPublicKey instead of resolving it to the full public key via the
contacts map. This meant nameForKey() lookups in the frontend always
missed the contact (prefix ≠ full key), causing the sender to display as
a truncated hex string rather than their name.

The fix mirrors the existing roomFullKey resolution pattern on the line
above: authorContact?.publicKey ?? authorPrefixHex.

Fixes #3732.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018nJjqaZZjhXFtj4JcZSHCS
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


Claude's Review

  • Gather context
  • Read changed file
  • Analyze fix
  • Post review feedback

Summary

The fix is correct and mirrors the existing roomFullKey resolution pattern cleanly. One issue found: the existing test asserts the old (broken) behavior and will fail.


Bug: Test not updated to match fix

src/server/meshcoreManager.rooms.test.ts:92

// This still asserts the PRE-FIX (broken) behavior:
expect(msg.fromPublicKey).toBe(authorPubkey.substring(0, 8));

After this fix, when the author contact is found via resolveContactByPrefix(), msg.fromPublicKey will be authorFullKey (the full 64-char hex key), not the 8-char prefix. This assertion will fail. It should be:

expect(msg.fromPublicKey).toBe(authorPubkey);

A second test case covering the unknown author fallback path (contact not in map → falls back to prefix) would also be valuable to guard against regressions.


Minor Observations

contact_message handler (line 980) is inconsistent

The contact_message handler stores data.pubkey_prefix directly as fromPublicKey without resolving it to the full key:

fromPublicKey: data.pubkey_prefix,   // line 980 — not resolved

The same nameForKey() comment in MeshCoreMessageStream.tsx (lines 80-82) says it was written to handle prefix-based lookups precisely because of this pattern. The room message fix makes the two handlers inconsistent. This isn't a blocker — nameForKey() does prefix-startsWith matching so it works either way — but it's worth noting as a future cleanup.

resolveContactByPrefix called with a full key works correctly (line 3260 does an exact-match first), so no edge case there.


Verdict

The logic change is correct and safe. Please fix the stale test assertion on line 92 before merging.

…3732)

The room_message test asserted the old buggy behavior — fromPublicKey == the
8-char wire prefix — which this PR's fix intentionally changes: when the author
contact is known, fromPublicKey now resolves to the full public key so
nameForKey() matches it on the frontend (symmetric with toPublicKey, which the
same test already expects as the full room key). Update the assertion to match.
The unknown-contact fallback test is unaffected (still expects the prefix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
@Yeraze

Yeraze commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

CI was red: meshcoreManager.rooms.test.ts:92 still asserted the old behavior (fromPublicKey == 8-char wire prefix), which this PR intentionally changes to the full key. Updated that assertion to expect the full author key (symmetric with the toPublicKey assertion right above it, which already expects the full room key) in 077552d. The unknown-contact fallback test is unaffected. The lint annotations in the run are pre-existing on main (non-blocking).

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


Claude's Review

  • Gather context
  • Read changed files
  • Analyze fix and test updates
  • Post review feedback

Summary

The fix is clean, correct, and well-tested. The stale assertion from the previous review has been addressed. Here's a full pass:


meshcoreManager.ts — Fix (lines ~3270–3275)

const authorContact = this.resolveContactByPrefix(authorPrefixHex);
const authorFullKey = authorContact?.publicKey ?? authorPrefixHex;

This mirrors the existing roomFullKey pattern exactly and is idiomatic. The fallback to authorPrefixHex preserves prior behavior for unknown contacts. No issues.


meshcoreManager.rooms.test.ts — Test update (line 95)

// Before (broken assertion):
expect(msg.fromPublicKey).toBe(authorPubkey.substring(0, 8));

// After (correct):
expect(msg.fromPublicKey).toBe(authorPubkey);

Correctly updated. The comment added on lines 92–94 explains the intent clearly and references the issue number — good documentation.


Coverage check

The test suite covers:

  • Known author → full key stored (fromPublicKey === authorPubkey) ✅
  • Unknown room, unknown author → both fall back to prefix (toPublicKey === 'aabbccddee00') ✅ (line 135)
  • Unknown author's fromPublicKey → implicitly covered by the fallback test at line 130 (author_pubkey_prefix: '11223344', no contact in map → fromPublicKey stays as the prefix). ✅

The unknown-author fallback path the previous review asked for as a "second test case" is already covered by the existing 'falls back to prefix when room contact not found' test at line 122.


Minor observation (non-blocking, pre-existing)

contact_message handler still stores data.pubkey_prefix directly as fromPublicKey (~line 980), while the room_message handler now stores the full key. This is an inconsistency but nameForKey() on the frontend handles both via prefix-startsWith matching, so it works correctly either way. No action needed here.


Verdict

The logic change is correct, the test assertion is fixed, and coverage is adequate. Ready to merge.

@Yeraze Yeraze merged commit c8602b2 into main Jun 25, 2026
20 checks passed
@Yeraze Yeraze deleted the claude/great-dijkstra-ueptaa branch June 25, 2026 14:10
Yeraze added a commit that referenced this pull request Jun 25, 2026
…3747)

`gh run list --branch <branch>` returns runs for EVERY commit ever pushed to
the branch, so after a branch is fixed and re-pushed, watch-ci kept reporting
the OLD commit's failed run forever — even though the new tip's runs all
passed. This produced false "CI FAILED" verdicts (observed monitoring PR #3733,
where the fix commit was green but the watcher reported the pre-fix failure).

Resolve the target's current head SHA each poll cycle (PR → headRefOid,
fork-safe; plain branch → commits API) and filter the run list to that SHA in
jq. Re-resolving each cycle also means a push mid-watch is tracked
automatically, and "no runs for this SHA yet" cleanly covers the window between
a push and its runs being created. On a transient SHA-resolution failure, fall
back to the last known tip rather than crashing.

Also fix the failure hint, which suggested the invalid
`gh run view --log-failed --branch <branch>` (run view takes a run id, not
--branch), and surface the short SHA in the pass/fail summary lines.


Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] MeshCore: Message to room server with wrong sender ID

2 participants