๐Ÿ”Œ chlorine.js - remote signing

chlorine.js is a miniature NIP-46 client: it talks to a signer that lives elsewhere, a bunker or a phone app, over relays. It connects with a bunker:// URI or by showing a nostrconnect:// URI for the signer to scan, and remembers the session so reloads reconnect without a new handshake. It composes by wearing the NIP-07 face: once connected, window.nostr is chlorine's signer, so xenon.js publishes through it, helium.js steps aside, and every other nobel library just works.

<script src="helium.js"></script>
<script src="fluorine.js"></script>
<script src="chlorine.js"></script>
<script src="xenon.js"></script>

<form ncl-connect><input name="bunker" placeholder="bunker://..."><button>Connect</button></form>
<form nhy-relay="wss://relay.example" nxe-publish><input name="content"><button>Post</button></form>

Pasting a bunker URI connects to the remote signer, and from then on the post form's events are signed by it.

Attributes

attribute description
ncl-connect A form that connects on submit, using its bunker field
ncl-nostrconnect On load (when not connected), gets a nostrconnect:// URI as its href (links) or text, and connects when a signer answers
ncl-relay Space-separated relays for ncl-nostrconnect, from the closest ancestor. Default nclCfg.relays
ncl-name, ncl-perms App name (default the page title) and requested permissions in the nostrconnect:// URI
ncl-disconnect Clicking it disconnects
ncl-connected Set on <html> to the user's pubkey while connected

window.nclCfg = {relays, timeout} sets default relays for listen and the request timeout (30000 ms).

Events

Dispatched on document (or the form or link involved), or on globalThis outside a browser.

event cancel to detail
ncl:listen set the href or text yourself (to draw a QR code) uri
ncl:connected pubkey (also fired when a stored session is restored)
ncl:auth skip window.open(url) url the signer asks the user to visit. The request keeps waiting
ncl:error error from a form or link connection
ncl:disconnected

API

function returns
chlorine.connect(bunkerUri) Promise of the user's pubkey. Sends connect with the URI's secret, then get_public_key
chlorine.listen({relays, name, perms, ...}) {uri, done}: a nostrconnect:// URI (extra keys become query parameters) and a Promise of the user's pubkey once a signer answers with the secret
chlorine.disconnect() forgets the session and restores the previous window.nostr
chlorine.rpc(method, params) Promise of the result of any NIP-46 method, such as ping
chlorine.signer the NIP-07 face {getPublicKey, signEvent, nip44, nip04} set as window.nostr
chlorine.previous the window.nostr that was there before
chlorine.session {sk, pk, remote, relays, secret, user} or null

Each signature, encryption and decryption is a relay round trip to the signer, so expect it to take a moment rather than be instant. The session, including the throwaway client key, is kept in localStorage under ncl:session. Requires helium.js and fluorine.js. Tested against nak bunker (both flows) on tinyrelay and the dev relay.

chlorine.js (4.6/1.8kb raw/brotli), prefix ncl-, needs helium, fluorine

← ๐Ÿงช fluorineโœ‰๏ธ iodine →