npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nostr-signer-connector

v0.3.0

Published

A library that allows Nostr clients to interact with various *Nostr event signers* in [a uniform manner](#the-nostrsigner-interface).

Downloads

14

Readme

nostr-signer-connector

A library that allows Nostr clients to interact with various Nostr event signers in a uniform manner.

Currently this library supports 3 types of Nostr event signers:

  • Signers based on a bare secret key (SecretKeySigner)
  • NIP-07 browser extensions (Nip07ExtensionSigner)
  • NIP-46 remote signer a.k.a. Nostr Connect / nsecBunker (Nip46RemoteSigner)
    • Both "Started by the signer (nsecBunker)" and "Started by the client" signer discovery flows are supported!

As a bonus, with this library, you can support "Login with nsecBunker" feature in your Nostr app without relying on NDK! If you curious about it, read Nip46RemoteSigner usage.

Installation

npm install nostr-signer-connector

Usage: How to Initialize Signer Instances

SecretKeySigner

Signers based on a bare secret key on memory.

You can create it from a secret (private) key in various format using the constructor:

  • Hex string
  • Bech32-encoded key (nsec1...)
  • 32 bytes of binary data (in a Uint8Array)
import { SecretKeySigner } from 'nostr-signer-connector';

// from hex string
const hexkey = "deadbeef...";
const signer1 = new SecretKeySigner(hexkey);

// from nsec
const nsec = "nsec1...";
const signer2 = new SecretKeySigner(nsec);

// from binary
const binkey: Uint8Array = ...;
const signer3 = new SecretKeySigner(binkey);

Nip07ExtensionSigner

Signers based on a NIP-07 browser extension.

You can create it by passing a window.nostr instance to the constructor.

import { Nip07ExtensionSigner, type Nip07Extension } from 'nostr-signer-connector';

const signer = new Nip07ExtensionSigner(window.nostr as Nip07Extension);

Nip46RemoteSigner

Signers based on a NIP-46 remote signer (a.k.a. Nostr Connect or nsecBunker).

You can't create Nip46RemoteSigner instances using the constructor. You should use static initialization methods on Nip46RemoteSigner class, appropriate for type of remote signer you want to support.

Connect to a nsecBunker ("Started by the signer" flow)

Use Nip46RemoteSigner.connectToRemote().

It tries to connect to a nsecBunker by passing the connection token (npub1...#<secret>?relay=<relay-url>) generated by a remote signer (nsecBunker), and establishes a session to the nsecBunker.

If it succeeded to connect, the returned promise resolves to an object that have session data (session) along with a handle to the nsecBunker (signer). You should store this session data in somewhere to resume the session later (e.g. after a browser reload).

import { Nip46RemoteSigner } from 'nostr-signer-connector';

const connToken = "npub1...#secret-token?relay=wss%3A%2F%2Frelay.nsecbunker.com"
const { signer, session } = await Nip46RemoteSigner.connectToRemote(connToken);

// store session data to LocalStorage
localStorage.setItem("nostr_connect_session", JSON.stringify(session));

// use the signer as you want...
const ev = await signer.signEvent({...});

Listen connection from a remote signer ("Started by the client" flow)

Use Nip46RemoteSigner.listenConnectionFromRemote().

It starts to listen a connection request from a remote signer, and establishes a session to the remote signer once a connection request is received.

First of all, it generates a connect URI (nostrconnect://...) which allows a remote signer to send connect request to your app. You should show the URI to users in some way, and instruct them to paste it on their remote signer.

You can wait until a session is established by await-ing on established property in the return value. This promise resolves to an object that have session data (session) along with a handle to the remote signer (signer). You should store this session data in somewhere to resume the session later (e.g. after a browser reload).

You can cancel listening a connection from remote signer by calling cancel function in the return value. When you cancel, established is rejected with an error.

import { Nip46RemoteSigner, type Nip46ClientMetadata } from 'nostr-signer-connector';

const relayUrls = ["wss://relay.damus.io"];
const client: Nip46ClientMetadata = {
    name: "sample client",
    url: "https://example.com",
    description: "just a sample"
};
const { connectUri, established, cancel } = 
    Nip46RemoteSigner.listenConnectionFromRemote(relayUrls, client);

// show the connect URI to user
console.log("paste this URI on Nostr Connect signer:", connectUri);

// wait until a session to a remote signer is established...
const { signer, session } = await established;

// store session data to LocalStorage
localStorage.setItem("nostr_connect_session", JSON.stringify(session));

// use the signer as you want...
const ev = await signer.signEvent({...});

Resume a session to a remote signer

Once a session to a remote signer have been established by initialization methods above, you can resume the session by passing a stored session data to Nip46RemoteSigner.resumeSession().

import { Nip46RemoteSigner, type Nip46SessionState } from 'nostr-signer-connector';

const rawSess = localStorage.getItem("nostr_connect_session")
if (rawSess === null) {
    // session not stored: start session by methods above
}

const sess = JSON.parse(rawSess) as Nip46SessionState;
const signer = await Nip46RemoteSigner.resumeSession(sess);

// use the signer as you want...
const ev = await signer.signEvent({...});

The NostrSigner Interface

All signer implementations share common interface as follows:

/**
 * The common interface of Nostr event signer.
 */
type NostrSigner = {
  /**
   * Returns the public key that corresponds to the underlying secret key, in hex string format.
   */
  getPublicKey(): Promise<string>;

  /**
   * Signs a given Nostr event with the underlying secret key.
   */
  signEvent(event: NostrEventTemplate): Promise<NostrEvent>;

  /**
   * Encrypts a given text to secretly communicate with others, by the encryption algorithm defined in NIP-04.
   */
  nip04Encrypt(recipientPubkey: string, plaintext: string): Promise<string>;

  /**
   * Decrypts a given ciphertext from others, by the decryption algorithm defined in NIP-04.
   */
  nip04Decrypt(senderPubkey: string, ciphertext: string): Promise<string>;
};