Skip to content

Desktop never verifies event id or signature on the message timeline — channel authenticity rests on TLS to the relay, not the signature #7564

Description

@Giszmo

Summary

Buzz Desktop renders relay-delivered events without ever checking their NIP-01 id hash or
Schnorr signature. Everything a human reads in a channel is authenticated by the TLS
connection to the relay and by the relay's own ingest check — not by the signature that
travels with the event.

This is now asymmetric with the agent runtime: since #7010 (merged 2026-09-04) buzz-acp
verifies every EVENT frame before routing, so an agent and the human sitting next to it no
longer agree on what is authentic.

Source state for every line reference below: origin/main @ 051c3a270.

Evidence

The renderer's relay session dispatches EVENT frames straight into the render buffer:

  • desktop/src/shared/api/relayClientSession.ts:780-782["EVENT", subId, event]handleEvent
  • desktop/src/shared/api/relayClientSession.ts:841-858 — dedupe, buffer, flush. No id check, no signature check on this path or anything downstream of it.

verifyEvent from nostr-tools/pure is imported in exactly one file in the renderer:

  • desktop/src/shared/lib/authors.ts:2,44-46, used at :92 — and only to decide whether a
    relay-signed event's actor/p attribution tag is allowed to override the signer as the
    displayed author.

So the capability is present and already wired for one narrow decision; it is simply not
applied to the messages themselves.

Verification that does exist elsewhere in Desktop

None of these cover the channel timeline:

Path Line Scope
Native relay client desktop/src-tauri/src/native_relay_client.rs:551-553 finite (catalog) REQ results only — live/archive subscription events skip it
Persona catalog desktop/src-tauri/src/persona_catalog.rs:93 catalog head selection
Team catalog desktop/src-tauri/src/team_catalog.rs:191 catalog head selection
NIP-OA owner auth tag desktop/src-tauri/src/nostr_convert.rs:79 kind:0 ownership provenance
Local archive ingest desktop/src-tauri/src/archive/pipeline.rs:122 verify_id() + verify_signature()

Impact

The relay is the single source of truth for reads (ARCHITECTURE.md:7), and it does verify
at ingest (crates/buzz-relay/src/handlers/ingest.rs:2219). But a client that never checks
signatures cannot distinguish a relay that is behaving from one that is not:

  1. A compromised, malicious, or simply buggy relay can fabricate a message attributed to any
    pubkey — including a community owner or an agent's owner — and Desktop renders it as
    authentic, with the right avatar and name.
  2. Self-hosted and third-party relays are a supported deployment. "Trust your relay operator
    completely, for authorship as well as availability" is a much stronger requirement than
    the signed-event model implies, and it is nowhere stated in the UI.
  3. The agent/human asymmetry cuts both ways. A forged event that buzz-acp drops is still
    shown to the human, who may then act on it or ask the agent why it ignored an instruction
    that — from the human's view — is right there in the channel.

Nothing here is exploitable by an ordinary member: ingest.rs:2253 requires the event pubkey
to equal the NIP-42 authenticated identity, so member-to-member impersonation is already
blocked at the relay. This issue is specifically about the client having no independent
check.

Suggested direction

Verify at the transport seam and fail closed, mirroring what buzz-acp does:

  • Check id and sig in relayClientSession.handleEvent before the event reaches the
    dedupe/render buffer, on a worker so the crypto does not block the UI thread; or move
    channel traffic onto native_relay_client, which already has the check for finite
    requests and could apply it to live subscriptions.
  • Drop failures rather than render them, and surface a counter/telemetry so a
    misconfigured relay is visible rather than silent.
  • Consider shipping behind a flag with an "unverified" affordance first, because of the
    caveat below.

One caveat before enabling this

#5616 reports that kind:9 events authored in Buzz Desktop fail third-party NIP-01
verification (id ≠ sha256 of the canonical serialization, and sig verifies against neither
the stated nor the recomputed id). If that reproduces, turning on client-side verification
would drop "legitimate" traffic.

Partial check on 2026-09-10 against a hosted relay: the ids of two kind:9 events recompute
exactly from the delivered fields, so that half of #5616 did not reproduce there. The
signature half could not be checked from the CLI because buzz messages get strips sig
(#6874). Worth settling #5616 first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions