Board: show only real VoxVera flyers, not foreign kind-30078 events

kind 30078 is a shared "application data" kind, so other apps (e.g.
artstr.studio) publish under it too — and relays may ignore our #t
filter, or an unrelated note may carry a "voxvera" hashtag. The board
only checked kind + t-tag, never the payload, so foreign events could
appear.

parseFlyers now also requires VoxVera's own markers: a `voxvera:` d-tag
and a `type: "voxvera_flyer"` content payload (both real flyers and
tombstones have these), filtering foreign events out client-side
regardless of relay behavior.

Test: a foreign kind-30078 event carrying a voxvera t-tag is excluded
while a real flyer shows. Full suite 384 passing.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-11 22:37:57 -04:00
1 parent 6fe9069f82
commit 0ae69a2774
2 files changed
+31 -1

No files matched your search

+21
View File
@@ -1089,6 +1089,27 @@ test.describe("VoxVera static client", () => {
await expect(page.locator(".content h1")).toHaveCount(0); await expect(page.locator(".content h1")).toHaveCount(0);
}); });
test("board shows only VoxVera flyers, not foreign kind-30078 app data", async ({ page }) => {
const OTHER = "b".repeat(64);
await stubNip07(page); // viewer = 1*64, no follows → show-all
await stubRelays(page, [
flyerEvent("real", OTHER, "Real VoxVera Flyer"),
// A foreign app's kind-30078 event that still carries a "voxvera" tag
// (hashtag collision, or a relay ignoring the #t filter). It has neither a
// voxvera: d-tag nor a voxvera_flyer payload, so it must be excluded.
{
id: "f".repeat(64), kind: 30078, pubkey: OTHER, created_at: 5000,
tags: [["d", "artstr:gallery"], ["t", "voxvera"], ["t", "flyer"]],
content: JSON.stringify({ type: "artstr_note", title: "Foreign App Note", url: "https://artstr.studio/x" })
}
]);
await page.goto("/board.html");
await page.locator("#board-connect").click();
await expect(page.locator("#board-content")).toBeVisible();
await expect(page.locator("#board-rows")).toContainText("Real VoxVera Flyer");
await expect(page.locator("#board-rows")).not.toContainText("Foreign App Note");
});
test("blocking an author hides their flyers, persists, and clears", async ({ page }) => { test("blocking an author hides their flyers, persists, and clears", async ({ page }) => {
const OTHER = "b".repeat(64); const OTHER = "b".repeat(64);
await stubNip07(page); // viewer = 1*64 await stubNip07(page); // viewer = 1*64
+10 -1
View File
@@ -190,7 +190,16 @@
if (!e || e.kind !== EVENT_KIND) return; if (!e || e.kind !== EVENT_KIND) return;
const tags = e.tags || []; const tags = e.tags || [];
if (!tags.some((x) => x[0] === "t" && x[1] === "voxvera")) return; if (!tags.some((x) => x[0] === "t" && x[1] === "voxvera")) return;
const key = e.pubkey + ":" + dtagOf(tags); // kind 30078 is a shared "application data" kind, and relays may ignore the
// #t filter (or another app may carry a "voxvera" hashtag), so foreign
// events can arrive. Require VoxVera's own markers: a voxvera: d-tag and a
// voxvera_flyer content payload (real flyers and tombstones both have it).
const d = dtagOf(tags);
if (!/^voxvera:/.test(d)) return;
let payload;
try { payload = JSON.parse(e.content); } catch (_) { return; }
if (!payload || payload.type !== "voxvera_flyer") return;
const key = e.pubkey + ":" + d;
const prev = latest.get(key); const prev = latest.get(key);
if (!prev || (e.created_at || 0) > (prev.created_at || 0)) latest.set(key, e); if (!prev || (e.created_at || 0) > (prev.created_at || 0)) latest.set(key, e);
}); });