Skip to content

feat: knowledge receipt event kinds — a machine-readable record that room events were derived into an external store #4529

Description

@Zigoljube

Motivation

Rooms produce durable knowledge, and that output increasingly lands in stores outside the relay — a knowledge graph, an archive, a wiki, a git repo. Today the only way a service can tell the room "an artifact derived from these messages now exists over there" is a kind:9 chat message with structured text inside. That works — we run it in production — but it is invisible to the protocol: clients can't render or filter it, nothing can subscribe to "knowledge receipts in this channel", search can't treat it as a distinct thing, and third-party NIP-29 clients see opaque bot text.

This is the situation Buzz's own architecture principle addresses — "Event kinds are the only switch. … Adding a new feature means defining a new kind." (CONTRIBUTING.md, Architecture Overview.) And because the relay deliberately rejects unregistered kinds, an integration cannot solve it from the outside: the registry is the only door.

Motivating consumer: a shipping integration that derives pinned Buzz threads into an external knowledge store and posts receipts back as kind:9 (https://github.com/OriginTrail/buzz-dkg-integration). The kinds proposed here are provider-neutral — store is a free string, and the same two kinds serve an IPFS pinner, a git archiver, or a wiki exporter identically.

Proposed solution

Two regular (immutable) channel-scoped kinds. Numbers are a concrete suggestion; final placement is maintainers' call (same posture as #2451).

Suggested numbers: the registry allocates thousand-blocks per domain (43xxx jobs, 45xxx forum, 46xxx workflow, 47xxx reserved user-groups, 48xxx system, 49xxx media — crates/buzz-core/src/kind.rs); the next free block is 50000–50999 (nothing ≥50000 in ALL_KINDS; fits the u16 the nostr crate requires). Regular semantics on purpose: receipts are append-only facts.

Kind 50001 — knowledge receipt — "an artifact derived from these room events now exists in an external store."

Tags: h = channel (required — receipts inherit channel membership visibility and the fan-out scoping invariant); e = source event id(s), ≥1.

Content (JSON):

field required meaning
artifact_uri yes resolvable identifier of the artifact in the external store
store yes free-form store discriminator (e.g. ipfs, git, wiki, a knowledge-graph name)
artifact_kind no what the artifact is (e.g. decision-summary)
content_hash no sha256:<hex> over the canonical source set, for independent re-derivation
title no one human-readable line for rendering/search

Kind 50002 — knowledge promotion receipt — "that artifact was finalized/anchored." Same tags, plus one e referencing the kind:50001 it promotes. Content adds anchor (free-form object, e.g. {"system":"…","tx":"…"}) and permanence (finalized | anchored).

Trust semantics (explicit): a receipt is a signed claim by the posting member, not a relay-verified fact. The relay validates shape and channel scope only; it never fetches artifact_uri and cannot verify that the external artifact exists or matches. Verification is the consumer's job, via artifact_uri and content_hash. That is the honest ceiling for external stores — and precisely the line that separates this from #2451, whose in-relay artifacts the relay can validate.

Privacy: payloads carry pointers and hashes only — never the artifact body or credentials. A receipt necessarily reveals to channel members that content was exported to an external store; posting one is the service's declaration of that fact, which is a feature (today the same export happens with no protocol-visible trace). Whether receipts should be text-searchable is an operator/maintainer policy call — see search note below.

Relay wiring (per CONTRIBUTING § How to Add a New Event Kind): scope Scope::MessagesWrite (same class as stream messages/pins); no side effects beyond storage and fan-out in v1; standard events table, no side table. Search: FTS inclusion is not a uniform one-line change across installations — fresh databases get the positive allowlist from migration 0008 (empty-DB conditional), populated databases retain their earlier expression until an operator runs scripts/maintenance/nip_rs_search_allowlist.sql, and later migrations wrap whichever expression is installed (see 0014). Simplest v1: leave receipts out of FTS entirely and let title rendering carry discoverability; adding them to search later is an ordinary follow-up in that same framework.

Alternatives considered

Additional context

Questions for maintainers

  1. Does an external-artifact receipt primitive belong in the event registry, alongside (not instead of) the in-relay direction RFC: evidence-backed knowledge crystallization as channel-scoped signed events #2451 sketches?
  2. One kind with a status field, or two kinds (receipt / promotion) as proposed?
  3. Should receipts be in FTS at all, given the mixed allowlist/exclusion install base?
  4. Issue-first is per CONTRIBUTING — would you rather see this as a docs/nips/ draft before any implementation?

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