Files
pig/apps/api/test/buzz.test.ts
T
2026-08-13 01:39:01 -07:00

174 lines
6.4 KiB
TypeScript

import { strict as assert } from 'node:assert';
import { createHash } from 'node:crypto';
import { describe, it } from 'node:test';
import { schnorr } from '@noble/curves/secp256k1.js';
import { getPublicKey, nip19, verifyEvent, type Event } from 'nostr-tools';
import { buzzWorkspaceId } from '../src/routes/buzz';
import {
BuzzNotifier,
decodeBuzzAuthorization,
normaliseBuzzRelayUrl,
parseAndVerifyBuzzAuthTag,
} from '../src/services/buzz';
import { NotificationDeliveryError } from '../src/services/notifier';
import { loadConfig } from '../src/lib/config';
const AGENT_KEY = `${'0'.repeat(63)}1`;
const CHANNEL = '10000000-0000-4000-8000-000000000001';
const envelope = {
idempotencyKey: 'buzz:link:stage-event',
destination: CHANNEL,
workspaceId: 'buzz.example.com',
notification: {
kind: 'stage_change' as const,
accountId: '20000000-0000-4000-8000-000000000002',
dealId: '30000000-0000-4000-8000-000000000003',
dealSide: 'demand' as const,
dealName: 'Reserved H100 cluster',
fromStage: 'proposal',
toStage: 'procurement',
changedAt: '2026-08-13T12:00:00.000Z',
},
};
describe('Buzz relay identity', () => {
it('normalises the WebSocket URL used by Buzz clients into its HTTP bridge community', () => {
assert.equal(normaliseBuzzRelayUrl('wss://buzz.example.com/'), 'https://buzz.example.com');
assert.equal(buzzWorkspaceId('wss://buzz.example.com/'), 'buzz.example.com');
});
it('accepts official hex and nsec representations as the same identity', () => {
const nsec = nip19.nsecEncode(Uint8Array.from(Buffer.from(AGENT_KEY, 'hex')));
const fromHex = new BuzzNotifier({ relayUrl: 'https://buzz.example.com', privateKey: AGENT_KEY });
const fromNsec = new BuzzNotifier({ relayUrl: 'https://buzz.example.com', privateKey: nsec });
assert.equal(fromHex.workspaceId, fromNsec.workspaceId);
});
});
describe('Buzz configuration', () => {
const base = { DATABASE_URL: 'postgres://pig:pig@localhost:5432/pig', NODE_ENV: 'test' };
it('requires relay and private identity together', () => {
assert.throws(
() => loadConfig({ ...base, BUZZ_RELAY_URL: 'https://buzz.example.com' }),
/BUZZ_PRIVATE_KEY/,
);
assert.throws(
() => loadConfig({ ...base, BUZZ_PRIVATE_KEY: AGENT_KEY }),
/BUZZ_RELAY_URL/,
);
});
it('keeps the Buzz private identity in server configuration only', () => {
const config = loadConfig({
...base,
BUZZ_RELAY_URL: 'https://buzz.example.com',
BUZZ_PRIVATE_KEY: AGENT_KEY,
});
assert.equal(config.BUZZ_RELAY_URL, 'https://buzz.example.com');
assert.equal(config.BUZZ_PRIVATE_KEY, AGENT_KEY);
});
});
describe('Buzz signed delivery', () => {
it('reuses the signed message event id while refreshing NIP-98 replay nonces', async () => {
const requests: { event: Event; auth: Event }[] = [];
let nonce = 0;
const notifier = new BuzzNotifier({
relayUrl: 'https://buzz.example.com',
privateKey: AGENT_KEY,
now: () => new Date('2026-08-13T12:00:01.000Z'),
nonce: () => `nonce-${++nonce}`,
fetchImpl: async (_input, init) => {
const event = JSON.parse(String(init?.body)) as Event;
const headers = new Headers(init?.headers);
const auth = decodeBuzzAuthorization(headers.get('authorization') ?? '');
assert.ok(auth);
requests.push({ event, auth });
return Response.json({ event_id: event.id, accepted: true, message: '' });
},
});
await notifier.send(envelope);
await notifier.send(envelope);
await notifier.send({ ...envelope, idempotencyKey: 'buzz:link:different-event' });
assert.equal(requests[0]?.event.id, requests[1]?.event.id);
assert.notEqual(requests[0]?.event.id, requests[2]?.event.id);
assert.notEqual(requests[0]?.auth.id, requests[1]?.auth.id);
assert.equal(requests[0]?.event.kind, 9);
assert.ok(verifyEvent(requests[0]!.event));
assert.deepEqual(requests[0]?.event.tags[0], ['h', CHANNEL]);
const payloadTag = requests[0]?.auth.tags.find((tag) => tag[0] === 'payload');
assert.equal(
payloadTag?.[1],
createHash('sha256').update(JSON.stringify(requests[0]!.event)).digest('hex'),
);
});
it('refuses to route a link from another relay community', async () => {
let called = false;
const notifier = new BuzzNotifier({
relayUrl: 'https://buzz.example.com',
privateKey: AGENT_KEY,
fetchImpl: async () => {
called = true;
return Response.json({});
},
});
await assert.rejects(
notifier.send({ ...envelope, workspaceId: 'other.example.com' }),
(error: unknown) =>
error instanceof NotificationDeliveryError &&
error.code === 'buzz_workspace_mismatch' &&
!error.retryable,
);
assert.equal(called, false);
});
it('marks relay throttling retryable without exposing response content', async () => {
const notifier = new BuzzNotifier({
relayUrl: 'https://buzz.example.com',
privateKey: AGENT_KEY,
fetchImpl: async () =>
new Response('{"error":"rate-limited: private detail"}', {
status: 429,
headers: { 'retry-after': '9' },
}),
});
await assert.rejects(
notifier.send(envelope),
(error: unknown) =>
error instanceof NotificationDeliveryError &&
error.code === 'buzz_rate_limited' &&
error.retryable &&
error.retryAfterMs === 9_000 &&
!error.message.includes('private detail'),
);
});
});
describe('Buzz owner attestation', () => {
it('verifies NIP-OA against the configured agent before sending it', () => {
const ownerKey = Uint8Array.from(Buffer.from(`${'0'.repeat(63)}2`, 'hex'));
const agentPublicKey = getPublicKey(Uint8Array.from(Buffer.from(AGENT_KEY, 'hex')));
const conditions = 'kind=9';
const digest = createHash('sha256')
.update(`nostr:agent-auth:${agentPublicKey}:${conditions}`)
.digest();
const tag = JSON.stringify([
'auth',
getPublicKey(ownerKey),
conditions,
Buffer.from(schnorr.sign(digest, ownerKey)).toString('hex'),
]);
assert.deepEqual(parseAndVerifyBuzzAuthTag(tag, agentPublicKey), JSON.parse(tag));
const tampered = JSON.parse(tag) as string[];
tampered[3] = `${tampered[3]![0] === '0' ? '1' : '0'}${tampered[3]!.slice(1)}`;
assert.throws(
() => parseAndVerifyBuzzAuthTag(JSON.stringify(tampered), agentPublicKey),
/BUZZ_AUTH_TAG/,
);
});
});