Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,9 @@ per-platform steps and limits.

For manual testing of shipped behavior, use `BUZZ_DEV_VIEWER= just desktop` to
exercise the native path, even if `.env.local` contains a public viewer pin.
Broker-backed runs do not count as acceptance. New features do not get broker
support; see [host boundaries and testing guidance](docs/contributing.md#shared-logic-and-host-boundaries).
Broker-backed runs do not prove native acceptance. Browser capabilities remain
supported; consolidate shared policy rather than maintain parallel feature logic.
See [host boundaries and testing guidance](docs/contributing.md#shared-logic-and-host-boundaries).

Live **browser/broker development** requires an existing Buzz account in the OS credential
store: the `buzz-desktop` / `secrets` Keychain entry on macOS, or the same entry in
Expand Down
7 changes: 4 additions & 3 deletions dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ for the runtime/identity matrix, ownership rules and cross-host test expectation
Keep platform-neutral feature policy under its existing `src/features/*` owner;
retain host custody and boundary enforcement here. Existing tests are discovered
by Vitest. [Broker setup](../README.md#relay-channels) requires an explicit public
identity pin; never put a private key in environment configuration. The broker is
being shrunk and does not receive new feature support. Use the native path for
manual acceptance; see the [broker reduction sequence](../docs/contributing.md#broker-reduction-sequence).
identity pin; never put a private key in environment configuration. Preserve
supported browser capabilities while consolidating duplicate implementations.
Use the native path for native acceptance and browser workflows to validate
browser support; see the [consolidation guidance](../docs/contributing.md#broker-consolidation-and-completion).
44 changes: 31 additions & 13 deletions dev/archive.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -253,19 +253,37 @@ test("clear reclaims every free page from a multi-megabyte archive", () => {
const file = path(),
h = harness(file),
db = new DatabaseSync(file);
for (let i = 0; i < 200; i++) h.ingest(event(i));
// Inflate synthetic stored envelopes without weakening real ingest validation.
db.exec(
"UPDATE archive_events SET envelope=zeroblob(16000); PRAGMA wal_checkpoint(TRUNCATE)",
);
const before = statSync(file).size;
h.request({ action: "clear", kind: 24200 });
expect(statSync(file).size).toBeLessThan(before);
expect(statSync(`${file}-wal`).size).toBe(0);
expect(db.prepare("PRAGMA freelist_count").get().freelist_count).toBe(0);
expect(h.settings().bytes).toBe(0);
db.close();
h.store.close();
try {
// Reclamation exercises stored pages, not 200 fresh-ingest crypto checks and
// commits. Seed the same multi-megabyte footprint in one transaction; the
// ingest/decoding tests above retain real signed, encrypted envelopes.
const insert = db.prepare(
"INSERT INTO archive_events(viewer,community,subscription,id,agent,kind,created,received,bytes,envelope) VALUES (?,?,'observer',?,?,24200,?,?,16000,zeroblob(16000))",
);
const now = Math.floor(Date.now() / 1000);
db.exec("BEGIN");
for (let i = 0; i < 200; i++)
insert.run(
viewer,
community,
i.toString(16).padStart(64, "0"),
sender,
now,
now,
);
db.exec("COMMIT; PRAGMA wal_checkpoint(TRUNCATE)");
const before = statSync(file).size;
expect(before).toBeGreaterThanOrEqual(200 * 16000);
expect(h.settings().bytes).toBe(200 * 16000);
h.request({ action: "clear", kind: 24200 });
expect(statSync(file).size).toBeLessThan(before);
expect(statSync(`${file}-wal`).size).toBe(0);
expect(db.prepare("PRAGMA freelist_count").get().freelist_count).toBe(0);
expect(h.settings().bytes).toBe(0);
} finally {
db.close();
h.store.close();
}
});

test("v1 migration preserves encrypted rows, policy settings and CAS revisions", () => {
Expand Down
12 changes: 3 additions & 9 deletions dev/direct-messages.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { validDirectMessageParticipants } from "../src/features/relay/direct-message-policy.ts";
import { randomUUID } from "node:crypto";
import { finalizeEvent } from "nostr-tools";

Expand All @@ -6,15 +7,8 @@ export function directMessageEvent(input, viewer, key) {
if (
!input ||
!Array.isArray(input.pubkeys) ||
input.pubkeys.length < 1 ||
input.pubkeys.length > 8 ||
input.pubkeys.some(
(value) =>
typeof value !== "string" ||
!/^[0-9a-f]{64}$/.test(value) ||
value === viewer,
) ||
new Set(input.pubkeys).size !== input.pubkeys.length
input.pubkeys.some((value) => typeof value !== "string") ||
!validDirectMessageParticipants(input.pubkeys, viewer)
)
throw new Error("Choose between one and eight other people.");
return finalizeEvent(
Expand Down
28 changes: 28 additions & 0 deletions dev/relay-broker-api.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,10 @@ test("direct-message transport signs only bounded participants and binds the ret
[recipients[0], recipients[0]],
Array(9).fill(recipients[0]),
["invalid"],
["A".repeat(64)],
[123],
null,
"a".repeat(64),
]) {
expect((await h.post("direct-message", { pubkeys })).status).toBe(400);
}
Expand Down Expand Up @@ -1989,6 +1993,30 @@ test("status signing and publication preserve scoped replacements and explicit c
expect(
(await h.post("publish", finalizeEvent(future, secret))).status,
).toBe(400);
for (const input of [
{ content: "😀".repeat(51) },
{ content: "one\ntwo" },
{
content: "x",
tags: [
["d", "general"],
["emoji", " "],
],
},
{
content: "x",
tags: [
["d", "general"],
["emoji", "😀".repeat(51)],
],
},
]) {
const template = { ...future, created_at: 1700000000, ...input };
expect((await h.post("sign", template)).status).toBe(400);
expect(
(await h.post("publish", finalizeEvent(template, secret))).status,
).toBe(400);
}
expect(h.publications).toHaveLength(2);
} finally {
await h.close();
Expand Down
8 changes: 3 additions & 5 deletions dev/relay-broker.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import { isWorkflowDefinitionBatch } from "../src/features/workflows/queries.ts"
import { validStatusTemplate } from "./user-status.mjs";
import { memoryFilter, decodeAgentMemory } from "./agent-memory.mjs";
import { memoryResponseText } from "../src/features/agents/memory.ts";
import {
assertSidebarMuteIntent,
mutateSidebarMute,
} from "./sidebar-mutes.mjs";
import { prepareMedia } from "./media-preparation.mjs";
import { assertSidebarSortIntent, mutateSidebarSort } from "./sidebar-sort.mjs";
import { readProjectGit } from "./project-git.mjs";
Expand Down Expand Up @@ -47,7 +43,9 @@ import { SocketRequestError } from "../src/features/relay/socket-requests.ts";
import {
assertSidebarStarIntent,
mutateSidebarStar,
} from "./sidebar-stars.mjs";
assertSidebarMuteIntent,
mutateSidebarMute,
} from "./sidebar-toggle.mjs";
import {
validateWorkflowEvent,
WORKFLOW_KINDS,
Expand Down
2 changes: 1 addition & 1 deletion dev/sidebar-group-moves.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
decodeSidebarPreferences,
mutateSidebarAssignment,
} from "./sidebar-preferences.mjs";
import { mutateSidebarStar } from "./sidebar-stars.mjs";
import { mutateSidebarStar } from "./sidebar-toggle.mjs";

async function setup({ cachedAssignment = true } = {}) {
const secret = generateSecretKey();
Expand Down
45 changes: 45 additions & 0 deletions dev/sidebar-intents.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect, it } from "vitest";
import { assertSidebarAssignmentIntent } from "./sidebar-preferences.mjs";
import { assertSidebarStarIntent } from "./sidebar-toggle.mjs";
import { assertSidebarMuteIntent } from "./sidebar-toggle.mjs";
import { assertSidebarSortIntent } from "./sidebar-sort.mjs";

it("keeps broker-only envelope checks before shared sidebar policy", () => {
const cases = [
[assertSidebarAssignmentIntent, { channelId: "c" }],
[assertSidebarStarIntent, { channelId: "c", starred: true }],
[assertSidebarMuteIntent, { channelId: "c", muted: false }],
[
assertSidebarSortIntent,
{ group: "channels", mode: "recent", sectionIds: [] },
],
];
for (const [assertIntent, valid] of cases) {
expect(() => assertIntent(valid)).not.toThrow();
for (const invalid of [null, [], {}, { ...valid, extra: true }])
expect(() => assertIntent(invalid)).toThrow("Invalid sidebar");
}
for (const createSection of [
null,
false,
[],
{ id: "x", name: "Work" },
{ id: "11111111-1111-1111-1111-111111111111", name: "Work", extra: true },
])
expect(() =>
assertSidebarAssignmentIntent({ channelId: "c", createSection }),
).toThrow("Invalid sidebar assignment intent");
expect(() =>
assertSidebarSortIntent({
group: "channels",
mode: "recent",
sectionIds: [1],
}),
).toThrow("Invalid sidebar sort intent");
expect(() =>
assertSidebarStarIntent({ channelId: "c", starred: "true" }),
).toThrow("Invalid sidebar star intent");
expect(() => assertSidebarMuteIntent({ channelId: 1, muted: false })).toThrow(
"Invalid sidebar mute intent",
);
});
2 changes: 1 addition & 1 deletion dev/sidebar-mutes-broker.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { brokerSocket, openBrokerSocket } from "../tests/broker-socket.mjs";
import { generateSecretKey, getPublicKey, verifyEvent } from "nostr-tools";
import { SIDEBAR_HEAD_BYTES } from "./sidebar-preferences.mjs";
import { relayBrokerPlugin } from "./relay-broker.mjs";
import { prepareSidebarMute } from "./sidebar-mutes.mjs";
import { prepareSidebarMute } from "./sidebar-toggle.mjs";
import { connectBrokerTransport } from "../src/features/relay/transport.ts";
import { fixtureRelayUrl, fixtureAliases } from "../tests/relay-config.ts";

Expand Down
84 changes: 0 additions & 84 deletions dev/sidebar-mutes.mjs

This file was deleted.

Loading
Loading