Merge pull request 'fix(#9): close race between create_new_key and NIP-46 connect' (#10) from issue-9-fix-create-new-key-race into dev
Some checks failed
Docker image / build-and-push-image (push) Has been cancelled

Reviewed-on: #10
This commit is contained in:
padreug 2026-05-30 11:23:42 +00:00
commit 3ec413b70d
2 changed files with 40 additions and 3 deletions

View file

@ -22,6 +22,43 @@ export class Backend extends NDKNip46Backend {
// this.setStrategy('publish_event', new PublishEventHandlingStrategy());
}
/**
* Override NDKNip46Backend.start() to await the kind-24133
* subscription's EOSE before resolving. The base implementation
* calls `this.ndk.subscribe(...)` and returns immediately the
* NDKSubscription queues a REQ on the relay connection but the
* relay's acknowledgement (EOSE) hasn't arrived yet. Any caller
* that publishes a NIP-46 event in the immediate window after
* `start()` returns races against the relay registering this
* subscription.
*
* aiolabs/lnbits#33's eager-bind chain publishes a NIP-46
* `connect` event in the same HTTP round-trip as `create_new_key`,
* which loses this race deterministically the bunker never
* sees the connect event because its subscription wasn't yet
* registered with the relay when the event was broadcast.
*
* Awaiting EOSE closes the race: by the time `start()` resolves,
* the relay has confirmed it has the bunker's subscription on
* file and will route matching kind-24133 events to it.
*
* See aiolabs/nsecbunkerd#9 for the full diagnosis.
*/
async start(): Promise<void> {
this.localUser = await this.signer.user();
await new Promise<void>((resolve) => {
const sub = this.ndk.subscribe(
{
kinds: [24133],
"#p": [this.localUser!.pubkey],
},
{ closeOnEose: false }
);
sub.on("event", (e: any) => this.handleIncomingEvent(e));
sub.on("eose", () => resolve());
});
}
private async validateToken(token: string) {
if (!token) throw new Error("Invalid token");

View file

@ -257,14 +257,14 @@ class Daemon {
const nsec = decryptNsec(iv, data, passphrase);
this.activeKeys[keyName] = nsec;
this.startKey(keyName, nsec);
await this.startKey(keyName, nsec);
return true;
}
loadNsec(keyName: string, nsec: string) {
async loadNsec(keyName: string, nsec: string) {
this.activeKeys[keyName] = nsec;
this.startKey(keyName, nsec);
await this.startKey(keyName, nsec);
}
}