mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-24 01:28:33 +02:00
Layers global chat on the existing ChatProvider seam without changing callers.
NostrChatProvider DECORATES the local SocketChatProvider: every message is still
delivered in-game + persisted exactly as before, and a global message is also
signed and fanned out to nostr relays (relay.smirk.cash). Messages from the relay
are delivered locally-only (never re-published) so there are no echo loops.
North-star fit: relay.smirk.cash's default inbox-outbox policy only lets
REGISTERED Smirk npubs publish — so putting a message in global chat is a concrete
reason to sign in with Smirk. Signer is injectable: a server bridge key today
(createBridgeSigner, must be a registered npub to publish; omit -> receive-only),
each player's own Smirk npub later (chat identity == wallet identity) by swapping
the signer, no provider change.
- src/utils/nostrLoader.js: shared nostr-tools loader (prod require / Jest bundle
shim), factored from the nip98 pattern.
- src/utils/escapeChat.js: entity-escape + length cap, applied to untrusted remote
messages (they have no trusted caller, unlike local ones).
- src/network/chat/nostr/{NostrTransport,bridgeSigner}.js: SimplePool transport
(degrades to noop if nostr-tools/ws unavailable — enabling can't crash the
server) + server key signer.
- src/network/chat/NostrChatProvider.js: decorator; echo dedupe (own pubkey +
seen-id FIFO), remote-delivery rate limit, XSS escape on remote content/username.
- src/network/chat/index.js buildChatProvider: opt-in via NOSTR_CHAT_ENABLED /
NOSTR_CHAT_SCOPE / NOSTR_RELAYS / NOSTR_CHAT_TAG / NOSTR_BRIDGE_SK. chatHandler
wraps its local provider through it and calls initialize().
Behavior-preserving: NOSTR_CHAT_ENABLED unset (today) => the plain local provider
is returned, chat is byte-for-byte unchanged. Server stays moderation authority
(bans/rate-limit/history in Postgres via the local provider). Tests: +10 (decorate/
fan-out/room-skip, remote deliver+escape+dedupe+rate-limit, factory, real schnorr
round-trip). Suite 54 suites / 382 tests green. Not yet live against relay.smirk.cash.
24 lines
1.0 KiB
JavaScript
24 lines
1.0 KiB
JavaScript
/**
|
|
* Load nostr-tools in a way that works in BOTH runtimes (see the same shim in utils/nip98.js):
|
|
* - Production (plain Node >=22): a normal require() succeeds — Node natively require()s
|
|
* nostr-tools' ESM-only transitive deps (@noble/*).
|
|
* - A CJS-only test runtime (Jest without --experimental-vm-modules) cannot parse those ESM
|
|
* deps, so we fall back to nostr-tools' pre-bundled self-contained IIFE build.
|
|
* Same public API either way. Shared so chat + auth load nostr identically.
|
|
*/
|
|
function loadNostrTools() {
|
|
try {
|
|
return require('nostr-tools');
|
|
} catch (_e) {
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const cjsIndex = require.resolve('nostr-tools');
|
|
const bundlePath = path.join(path.dirname(cjsIndex), '..', 'nostr.bundle.js');
|
|
const src = fs.readFileSync(bundlePath, 'utf8');
|
|
// eslint-disable-next-line no-new-func
|
|
return new Function(`${src}\nreturn NostrTools;`)();
|
|
}
|
|
}
|
|
|
|
module.exports = { loadNostrTools };
|