Skip to content

Commit adb9443

Browse files
vveerrggclaude
andcommitted
feat: add NIP-46 bunker server mode — extension acts as remote signer
NostrKey can now act as a NIP-46 bunker server, not just a client. The extension subscribes for kind 24133 requests on the relay, decrypts them, signs with the local key, and publishes responses. - New BunkerServer class (src/utilities/bunker-server.js) - window.nostr.nip46 API: startBunker, stopBunker, status - Background handlers: bunkerServer.start/stop/status - Test page updated with "Start NostrKey Bunker" + auto-connect - nostr-crypto-utils bumped to 0.6.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 85abc95 commit adb9443

7 files changed

Lines changed: 542 additions & 251 deletions

File tree

docs/test-bunker.html

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,16 @@ <h1>NostrKey</h1>
421421

422422
<!-- NIP-46 panel -->
423423
<div id="panel-nip46" class="panel" style="display:none">
424+
<!-- NostrKey as Bunker section -->
425+
<div id="nip46-nostrkey-bunker" style="margin-bottom:16px">
426+
<h4 style="font-size:14px;color:#2d2d2d;margin-bottom:8px">NostrKey as Bunker</h4>
427+
<button id="btn-start-bunker" class="btn btn-connect">Start NostrKey Bunker</button>
428+
<p id="bunker-server-uri" class="mono" style="font-size:12px;color:#75715e;word-break:break-all;margin-top:8px;display:none"></p>
429+
<p id="bunker-server-error" class="field-error"></p>
430+
</div>
431+
432+
<div style="text-align:center;color:#b0b0a8;font-size:13px;margin:12px 0">&mdash; or paste URI manually &mdash;</div>
433+
424434
<div id="nip46-loading" style="padding:10px 0">
425435
<p style="font-size:13px;color:#75715e">Loading NIP-46 libraries&hellip;</p>
426436
</div>
@@ -481,6 +491,7 @@ <h3>Relays</h3>
481491
var formDirty = false;
482492
var relayPool = null;
483493
var nip46Session = null;
494+
var bunkerServerActive = false;
484495
var activeTab = 'nip07';
485496

486497
// === Utilities ===
@@ -913,6 +924,100 @@ <h3>Relays</h3>
913924
}
914925
}
915926

927+
// === Start NostrKey as Bunker ===
928+
async function startNostrKeyBunker() {
929+
if (!window.nostr || !window.nostr.nip46) {
930+
document.getElementById('bunker-server-error').textContent = 'NostrKey extension not detected';
931+
document.getElementById('bunker-server-error').style.display = '';
932+
return;
933+
}
934+
935+
var btn = document.getElementById('btn-start-bunker');
936+
var uriEl = document.getElementById('bunker-server-uri');
937+
var errEl = document.getElementById('bunker-server-error');
938+
btn.disabled = true;
939+
btn.textContent = 'Starting\u2026';
940+
errEl.style.display = 'none';
941+
942+
try {
943+
log('info', 'Starting NostrKey bunker server\u2026');
944+
var result = await window.nostr.nip46.startBunker({
945+
relayUrls: ['wss://relay.nostrkey.com']
946+
});
947+
948+
if (!result.success) throw new Error(result.error || 'Failed to start bunker');
949+
950+
bunkerServerActive = true;
951+
var uri = result.uri;
952+
uriEl.textContent = uri;
953+
uriEl.style.display = '';
954+
btn.textContent = 'Bunker Running';
955+
log('success', 'Bunker server started');
956+
log('info', 'URI: ' + uri);
957+
958+
// Auto-connect: wait for NIP-46 client to be ready, then connect
959+
log('info', 'Auto-connecting via NIP-46\u2026');
960+
var nip46 = window.__nip46;
961+
if (!nip46 || !nip46.loaded) {
962+
// Wait a moment for the module to load
963+
await new Promise(function(resolve) {
964+
var handler = function() {
965+
window.removeEventListener('nip46-loaded', handler);
966+
resolve();
967+
};
968+
window.addEventListener('nip46-loaded', handler);
969+
setTimeout(resolve, 3000);
970+
});
971+
nip46 = window.__nip46;
972+
}
973+
974+
if (!nip46 || !nip46.loaded) throw new Error('NIP-46 client libraries not loaded');
975+
976+
var config = nip46.parseBunkerUrl(uri);
977+
var session = new nip46.Nip46Session(config);
978+
await session.connect();
979+
log('success', 'NIP-46 session established via NostrKey bunker');
980+
981+
var pubkey = await session.getPublicKey();
982+
connectedPubkey = pubkey;
983+
nip46Session = session;
984+
currentSigner = {
985+
getPublicKey: function() { return Promise.resolve(pubkey); },
986+
signEvent: function(e) { return session.signEvent(e); }
987+
};
988+
log('success', 'Remote signer: ' + hexToNpub(pubkey));
989+
990+
relayPool = new RelayPool(ALL_RELAYS);
991+
log('info', 'Connecting to relays\u2026');
992+
await relayPool.connect();
993+
updateRelayDots();
994+
995+
updateConnectionUI(true, pubkey);
996+
enableForm(true);
997+
998+
log('info', 'Fetching profile\u2026');
999+
var profile = await fetchProfile(pubkey);
1000+
if (profile) {
1001+
populateForm(profile);
1002+
log('success', 'Profile loaded');
1003+
} else {
1004+
originalProfile = {};
1005+
log('info', 'No existing profile \u2014 create a new one');
1006+
}
1007+
} catch (err) {
1008+
log('error', 'Bunker start failed: ' + err.message);
1009+
errEl.textContent = err.message;
1010+
errEl.style.display = '';
1011+
btn.disabled = false;
1012+
btn.textContent = 'Start NostrKey Bunker';
1013+
// Clean up server if it started but connect failed
1014+
if (bunkerServerActive) {
1015+
try { await window.nostr.nip46.stopBunker(); } catch (e) {}
1016+
bunkerServerActive = false;
1017+
}
1018+
}
1019+
}
1020+
9161021
// === Connect NIP-46 ===
9171022
async function connectNip46() {
9181023
var uri = document.getElementById('bunker-uri').value.trim();
@@ -973,6 +1078,10 @@ <h3>Relays</h3>
9731078
if (formDirty && !confirm('You have unsaved changes. Disconnect anyway?')) return;
9741079

9751080
if (nip46Session) { nip46Session.disconnect(); nip46Session = null; }
1081+
if (bunkerServerActive) {
1082+
try { window.nostr.nip46.stopBunker(); } catch (e) {}
1083+
bunkerServerActive = false;
1084+
}
9761085
if (relayPool) { relayPool.disconnect(); relayPool = null; }
9771086

9781087
currentSigner = null;
@@ -994,6 +1103,13 @@ <h3>Relays</h3>
9941103
document.getElementById('bunker-uri').disabled = false;
9951104
validateBunkerUri();
9961105

1106+
// Reset bunker server UI
1107+
var btnBunker = document.getElementById('btn-start-bunker');
1108+
btnBunker.textContent = 'Start NostrKey Bunker';
1109+
btnBunker.disabled = false;
1110+
document.getElementById('bunker-server-uri').style.display = 'none';
1111+
document.getElementById('bunker-server-error').style.display = 'none';
1112+
9971113
log('info', 'Disconnected');
9981114
}
9991115

@@ -1054,6 +1170,7 @@ <h3>Relays</h3>
10541170
t.addEventListener('click', function() { switchTab(t.getAttribute('data-tab')); });
10551171
});
10561172
document.getElementById('btn-nip07-connect').addEventListener('click', connectNip07);
1173+
document.getElementById('btn-start-bunker').addEventListener('click', startNostrKeyBunker);
10571174
document.getElementById('btn-nip46-connect').addEventListener('click', connectNip46);
10581175
document.getElementById('btn-disconnect').addEventListener('click', doDisconnect);
10591176
document.getElementById('btn-save').addEventListener('click', saveProfile);

0 commit comments

Comments
 (0)