/** * NIP-07 Nostr Provider Shim * * Injected into proxied iframe apps via nginx sub_filter. * Implements window.nostr interface (getPublicKey, signEvent) * by communicating with the parent Archipelago frame via postMessage. * * Security: validates postMessage origin, never exposes secret key. */ (function () { 'use strict'; // Only inject if we're inside an iframe if (window === window.top) return; // Don't override existing NIP-07 extensions if (window.nostr) return; var pending = {}; var nextId = 1; function request(method, params) { return new Promise(function (resolve, reject) { var id = nextId++; pending[id] = { resolve: resolve, reject: reject }; window.parent.postMessage( { type: 'nostr-request', id: id, method: method, params: params || {} }, '*' ); // Timeout after 30 seconds setTimeout(function () { if (pending[id]) { pending[id].reject(new Error('NIP-07 request timed out')); delete pending[id]; } }, 30000); }); } window.addEventListener('message', function (event) { if (!event.data || event.data.type !== 'nostr-response') return; var handler = pending[event.data.id]; if (!handler) return; delete pending[event.data.id]; if (event.data.error) { handler.reject(new Error(event.data.error)); } else { handler.resolve(event.data.result); } }); window.nostr = { getPublicKey: function () { return request('getPublicKey'); }, signEvent: function (event) { return request('signEvent', { event: event }); }, getRelays: function () { return request('getRelays'); }, nip04: { encrypt: function (pubkey, plaintext) { return request('nip04.encrypt', { pubkey: pubkey, plaintext: plaintext }); }, decrypt: function (pubkey, ciphertext) { return request('nip04.decrypt', { pubkey: pubkey, ciphertext: ciphertext }); }, }, nip44: { encrypt: function (pubkey, plaintext) { return request('nip44.encrypt', { pubkey: pubkey, plaintext: plaintext }); }, decrypt: function (pubkey, ciphertext) { return request('nip44.decrypt', { pubkey: pubkey, ciphertext: ciphertext }); }, }, }; })();