mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
[reactions] Fix "Missing signing primitives" — session actor must have a usable key
Root cause of "Failed to send reaction" (after the dispatch fix made the button
reach the publish path): reactions are published under the anonymous SESSION ACTOR,
signed with its private key. But ensureSessionActor adopted a stored session-actor
entry as that key — and a persisted nsec login is passphrase-encrypted with
privateKey "" ("never exposes plaintext"). So a leftover saved-nsec entry hijacked
the anonymous telemetry key, and signEventWithPrivateKey(event, "") threw
"Missing signing primitives". (Repro: logged in with NIP-07 while a saved nsec
account exists on the device.)
Every ensureSessionActor caller uses it to SIGN telemetry (reactions / view events /
watch-history), so the returned actor must carry a usable plaintext key. Now it only
reuses a current/stored actor that has one; otherwise it tracks the locked entry (for
other flows) and generates a fresh ephemeral signing key — instead of returning a
keyless actor that can sign nothing.
Existing session-actor suites green; lint + build green.
Follow-up: the fresh ephemeral key isn't persisted, so a like isn't remembered across
reloads (different anonymous key) — separate from this crash fix.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
6e2894e3d8
commit
6b251dbda5
1 file changed
+18
-4
@@ -138,16 +138,30 @@ export class SignerManager {
|
||||
}
|
||||
|
||||
async ensureSessionActor(force = false) {
|
||||
// The session actor is an ANONYMOUS telemetry key used to SIGN reactions /
|
||||
// view events / watch-history when they aren't tied to the login. It must
|
||||
// therefore carry a usable plaintext private key. A persisted nsec login is
|
||||
// passphrase-encrypted (privateKey ""), so adopting one as the session actor
|
||||
// made reactions fail with "Missing signing primitives" (a leftover saved-nsec
|
||||
// entry hijacked the anonymous key). Only reuse an actor that can actually
|
||||
// sign; otherwise fall through and generate a fresh ephemeral key.
|
||||
const hasUsableKey = (actor) =>
|
||||
Boolean(
|
||||
actor &&
|
||||
typeof actor.privateKey === "string" &&
|
||||
HEX64_REGEX.test(actor.privateKey),
|
||||
);
|
||||
|
||||
if (!force) {
|
||||
if (this.sessionActor) {
|
||||
if (hasUsableKey(this.sessionActor)) {
|
||||
return this.sessionActor.pubkey;
|
||||
}
|
||||
if (this.lockedSessionActor) {
|
||||
return this.lockedSessionActor.pubkey;
|
||||
}
|
||||
const storedEntry = readStoredSessionActorEntry();
|
||||
if (storedEntry) {
|
||||
// Track a locked/encrypted entry (other flows read it) but don't sign with it.
|
||||
this.lockedSessionActor = storedEntry;
|
||||
}
|
||||
if (hasUsableKey(storedEntry)) {
|
||||
this.sessionActor = storedEntry;
|
||||
return storedEntry.pubkey;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user