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.
| 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).
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 |
| 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