The relay-side read-row emitter relies on a load-bearing contract from
`Db::communities_of_channels`: a channel id with no row in the DB MUST
be absent from the returned map, never mapped to a default. The relay's
`MissingLookup → ImplBug{row_community_lookup_missing} → CoverageBreach`
fail-closed guard-rail goes blind if this helper ever started returning
a default/zero entry for unknown channels — and the relay-side
mutate-bite for that guard-rail wouldn't catch it (different layer).
This adds a PG-ignored test that pins both directions:
- (1) Existing channel → present with its true community.
- (2) Missing channel → ABSENT from the result map (load-bearing).
- (3) Map size equals the number of existing channels.
Mutate → red → restore verified against live Postgres:
Mutant: post-loop `for ch in channel_ids { entry().or_insert(nil) }`
Result: assertion (2) bites with explicit message
"missing channel must be absent from the result map,
got Some(CommunityId(00000000-…))"
Restored: green.
Closes the read-seam fail-closed chain end-to-end (DB layer through
checker), making it non-vacuous across both layers.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Land the read-seam emitter for the runtime conformance gate. Two
emit sites, one buzz-db helper, one negative fixture.
## buzz-db: `communities_of_channels` helper
`Buzz::communities_of_channels(&[Uuid]) -> HashMap<Uuid, CommunityId>`
— batched per-channel community lookup. Used by the relay emitters to
project each row's true community label independently of the fetch
query's WHERE clause. That independence is what makes the
`Inv_NonInterference` / `Inv_ReadConfinement` bite non-vacuous: a
mutation dropping `community_id = $X` from `query_events` would
still let this helper return the row's true label and the checker
would catch the mismatch.
Channels missing from the result map are intentionally NOT mapped to
a default — callers MUST treat "channel-id not in map" as a coverage
breach, never as "use the resolved community."
## buzz-relay: projection + record helpers
`crate::conformance` gains four new items:
- `project_row_community` — single-row helper encoding the (B)
strategy: channel-less → resolved (honest, not tautological);
channel-scoped → lookup or `None` (caller fails closed).
- `RowCommunityProjection` enum — Ok(Vec<CommunityLabel>) OR
MissingLookup discriminated outcome.
- `record_read_message_rows` — non-search lane: emits
`ReadMessageRows` on Ok projection, `ImplBug { kind:
"row_community_lookup_missing" }` on MissingLookup.
- `record_read_by_id_rows` — search lane companion, same shape but
emits `ReadByIdRows`. `filter_channel` is `None` for the search
lane (search at the abstract level isn't bound to a single channel;
per-row `channel_id` carries channel identity honestly).
## req.rs wire-up: two emit sites
- Site 1 (`req.rs` non-search loop, after `query_events`): collect
distinct channel ids from the result set → `communities_of_channels`
→ `record_read_message_rows`. Production cost: one extra DB query
per request (NoopTracer short-circuits in non-conformance builds).
- Site 2 (`req.rs` search loop, after `get_events_by_ids`): same
pattern → `record_read_by_id_rows`. `handle_search_req` gains a
threaded-through `trace_state: Option<&AbstractState>` parameter.
DB-helper errors on either site fall back to an empty lookup map.
This intentionally triggers `MissingLookup` → `ImplBug` for any
channel-scoped row in the result set, surfacing the helper failure
as a coverage breach (fail-closed) rather than a silent resolved-
label substitution.
## Negative fixture: foreign-row leak
New `bad_foreign_row_leak.jsonl` + matching test. The fixture is a
`ReadMessageRows` whose row_communities contains community B while
the state is bound to community A. This is the proof artifact Eva
requested for the (B)-projection guard-rail: if the row had been
mis-projected as channel-less (defaulting to resolved A), the subset
check would have passed vacuously. By recording the row's TRUE
community independently, `Inv_NonInterference` surfaces it
immediately as `NonInterference`.
## Unit tests (conformance::tests)
Five new tests pinning every behavior:
- `project_row_communities_channelless_uses_resolved` (positive)
- `project_row_communities_channel_scoped_uses_lookup_label` (the
non-tautological correctness — lookup label, NOT resolved)
- `project_row_communities_channel_scoped_missing_is_breach` (the
guard-rail bite)
- `record_read_message_rows_missing_lookup_emits_impl_bug`
- `record_read_by_id_rows_ok_emits_read_by_id_rows`
## Mutate → red → restore (three independent bites)
1. Make `project_row_community` fall back to resolved on missing-
lookup (the tempting wrong-fix): `project_row_communities_channel_
scoped_missing_is_breach` + `record_read_message_rows_missing_
lookup_emits_impl_bug` go red with explicit messages
("missing lookup must be a breach, got Ok([...])", "expected
ImplBug coverage breach, got ReadMessageRows {...}"). Restored.
2. Make every channel-scoped row project to resolved (the
tautological projection): 4 of 9 unit tests go red — the lookup-
label, missing-breach, and record-helper tests all bite. Restored.
3. Edit the negative fixture to use community_a instead of
community_b: `foreign_row_leak_is_non_interference` reds ("foreign
row community label must be rejected by Inv_NonInterference"). The
fixture is load-bearing, not decorative. Restored.
## Test surfaces
- `cargo test -p buzz-relay --lib` → **394/0** (was 387 baseline).
- `cargo test -p buzz-conformance --lib` → 9/0.
- `cargo test -p buzz-conformance --test replay_fixtures` → **6/0**
(was 5; +foreign_row_leak).
- `cargo test -p buzz-db --lib` → 75/0 (helper compiles; DB-driven
integration coverage lives in `--include-ignored` lane on PG).
- `cargo clippy -p buzz-relay -p buzz-db -p buzz-conformance
--all-targets -- -D warnings` clean.
- `cargo fmt --all -- --check` clean.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
CI Rust Lint + Windows Rust run `cargo clippy --workspace --all-targets -- -D warnings`; the community_id/tenant args pushed six fns to 8/7 and the new NIP-98 replay code tripped clamp/const-assert lints. Resolve at the bar, matching existing repo conventions:
- 6x #[allow(clippy::too_many_arguments)] on the fns that gained a tenant/community arg (same convention already used across buzz-db/relay).
- buzz-pubsub replay TTL: .max().min() -> .clamp() (floor 120 < ceiling 3600, cannot panic; behavior identical, incl. the u64::MAX clamp test).
- buzz-auth replay const-drift tripwires: scoped #[allow(clippy::assertions_on_constants)] — the assert-on-constant IS the design (fails if someone drifts the TTL constants).
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Add EventQuery::for_community so relay call sites can keep concise
struct updates without restoring a tenantless Default. The constructor
requires the server-resolved CommunityId and preserves the old optional
filter defaults everywhere else.
Return the owning community host from the ephemeral-channel reaper by
joining communities in the archive UPDATE. Reaper consumers can now build
TenantContext per archived row from DB-resolved community+host instead of
hoisting or forging a batch-level tenant.
Co-authored-by: Mari <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>