When Activity Becomes Memory

Buzz's search and audit layers answer a practical question: how can a relay-centered workspace remain useful after the live conversation has moved on? This chapter examines how canonical events become searchable history, tamper-evident audit evidence, and human-readable activity without turning any projection into a second source of truth. The reader should leave able to judge what an agent's “receipts” establish—and what they still require a person to verify.

A workspace becomes memory not when it stores everything, but when it makes the right records retrievable without losing their context or access boundaries.

Search makes the event log usable without owning permission

Suppose an engineer asks, “Have we seen this error before?” In a small room, the answer may be visible by scrolling. In a large project, the useful history might include a message from an incident channel, a reply that names the root cause, a patch discussion, and a release note. A durable event log without retrieval would still be a poor memory. Buzz's search layer exists to make that history usable.

The implementation keeps the search index close to the canonical event store. The events table contains a generated full-text vector derived from event content, and a GIN index supports the text query. Because the vector is generated as part of the row, the client does not submit a second representation that can drift away from the signed content. The repository describes no separate search service, message queue, or reindex window that must be reconciled before a newly stored event becomes indexable. The database write is also the index update.

That is a consistency choice, not just a performance choice. A separate search system can be useful at scale, but it introduces another state boundary: the event may exist while the indexer is delayed, duplicated, or unavailable. Keeping the generated vector on the event row reduces that particular seam. It also makes the source of the search text explicit. Search is derived from the event content; it is not an independently authored memory of what the event supposedly said.

The query remains scoped. Every search request carries a community identity, and the SQL shape puts the community predicate before the full-text match. Channel scope can narrow results to selected channels, channel-less events, or a combination. Privacy-sensitive kinds are excluded at storage level by producing no searchable vector, so a normal full-text query cannot accidentally make those events discoverable.

Those protections answer an important question: which candidates may the database return for this community and requested scope? They do not answer the final question: may this authenticated reader see each candidate? The relay reauthorizes search hits before returning them. A community filter is necessary, but it is not the entire permission decision. The relay may need to check channel membership, visibility, author restrictions, or the rules associated with the event kind.

This two-stage shape is easy to overlook because search feels like a read-only operation. It is still a disclosure path. A query can expose information through its returned content, its identifiers, or the difference between a match and no match. By treating search as candidate retrieval followed by authorization, Buzz keeps the access decision at the relay boundary rather than delegating it to the database index.